summaryrefslogtreecommitdiffstats
path: root/trunk/src/ld-libs.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/ld-libs.c')
-rw-r--r--trunk/src/ld-libs.c1512
1 files changed, 1512 insertions, 0 deletions
diff --git a/trunk/src/ld-libs.c b/trunk/src/ld-libs.c
new file mode 100644
index 0000000..fe758b8
--- /dev/null
+++ b/trunk/src/ld-libs.c
@@ -0,0 +1,1512 @@
+/* Copyright (C) 2003 MontaVista Software, Inc.
+ Written by Daniel Jacobowitz <drow@mvista.com>, 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <error.h>
+#include <argp.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "prelinktab.h"
+#include "reloc.h"
+#include "reloc-info.h"
+
+#include "ld-libs.h"
+
+struct search_path
+{
+ int maxlen, count, allocated;
+ char **dirs;
+};
+
+struct search_path ld_dirs, ld_library_search_path;
+int host_paths;
+
+void string_to_path (struct search_path *path, const char *string);
+
+const char *argp_program_version = PRELINK_RTLD_PROG PKGVERSION " 1.0";
+
+const char *argp_program_bug_address = REPORT_BUGS_TO;
+
+static char argp_doc[] = PRELINK_RTLD_PROG " -- program to simulate the runtime linker";
+
+#define OPT_SYSROOT 0x8c
+#define OPT_LIBRARY_PATH 0x8e
+#define OPT_TARGET_PATHS 0x8f
+
+static struct argp_option options[] = {
+ {"library-path", OPT_LIBRARY_PATH, "LIBRARY_PATH", 0, "Set library search path to LIBRARY_PATH" },
+ {"root", OPT_SYSROOT, "ROOT_PATH", 0, "Prefix all paths with ROOT_PATH" },
+ {"target-paths", OPT_TARGET_PATHS, 0, 0, "Specified paths are based on ROOT_PATH" },
+ { 0 }
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case OPT_SYSROOT:
+ sysroot = arg;
+ break;
+ case OPT_LIBRARY_PATH:
+ string_to_path(&ld_library_search_path, arg);
+ break;
+ case OPT_TARGET_PATHS:
+ host_paths = 0;
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+#ifndef PT_TLS
+#define PT_TLS 7 /* Thread-local storage segment */
+#endif
+
+#ifndef R_ARM_TLS_DTPMOD32
+#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */
+#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */
+#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */
+#endif
+
+/* This function returns the same constants expected by glibc's
+ symbol lookup routines. This is slightly different from the
+ equivalent routines in prelink. It should return PLT for any
+ relocation where an undefined symbol in the application should
+ be ignored: typically, this means any jump slot or TLS relocations,
+ but not copy relocations. Don't return the prelinker's
+ RTYPE_CLASS_TLS. */
+int
+reloc_type_class (int type, int machine)
+{
+ switch (machine)
+ {
+ case EM_386:
+ switch (type)
+ {
+ case R_386_COPY: return ELF_RTYPE_CLASS_COPY;
+ case R_386_JMP_SLOT:
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_TPOFF:
+ return ELF_RTYPE_CLASS_PLT;
+ default: return 0;
+ }
+
+ case EM_X86_64:
+ switch (type)
+ {
+ case R_X86_64_COPY: return ELF_RTYPE_CLASS_COPY;
+ case R_X86_64_JUMP_SLOT:
+ case R_X86_64_DTPMOD64:
+ case R_X86_64_DTPOFF64:
+ case R_X86_64_TPOFF64:
+ case R_X86_64_DTPOFF32:
+ case R_X86_64_TPOFF32:
+ return ELF_RTYPE_CLASS_PLT;
+ default: return 0;
+ }
+
+ case EM_ARM:
+ switch (type)
+ {
+ case R_ARM_COPY: return ELF_RTYPE_CLASS_COPY;
+ case R_ARM_JUMP_SLOT:
+ case R_ARM_TLS_DTPMOD32:
+ case R_ARM_TLS_DTPOFF32:
+ case R_ARM_TLS_TPOFF32:
+ return ELF_RTYPE_CLASS_PLT;
+ default: return 0;
+ }
+
+ case EM_SH:
+ switch (type)
+ {
+ case R_SH_COPY: return ELF_RTYPE_CLASS_COPY;
+ case R_SH_JMP_SLOT: return ELF_RTYPE_CLASS_PLT;
+ default: return 0;
+ }
+
+ case EM_PPC:
+ switch (type)
+ {
+ case R_PPC_COPY: return ELF_RTYPE_CLASS_COPY;
+ case R_PPC_JMP_SLOT: return ELF_RTYPE_CLASS_PLT;
+ default:
+ if (type >= R_PPC_DTPMOD32 && type <= R_PPC_DTPREL32)
+ return ELF_RTYPE_CLASS_PLT;
+ return 0;
+ }
+
+ case EM_PPC64:
+ switch (type)
+ {
+ case R_PPC64_COPY: return ELF_RTYPE_CLASS_COPY;
+ case R_PPC64_ADDR24: return ELF_RTYPE_CLASS_PLT;
+ default:
+ if (type >= R_PPC64_DTPMOD64 && type <= R_PPC64_TPREL16_HIGHESTA)
+ return ELF_RTYPE_CLASS_PLT;
+ return 0;
+ }
+
+ case EM_MIPS:
+ switch (type)
+ {
+ case R_MIPS_COPY:
+ return ELF_RTYPE_CLASS_COPY;
+ case R_MIPS_JUMP_SLOT:
+ case R_MIPS_TLS_DTPMOD32:
+ case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_TPREL32:
+ return ELF_RTYPE_CLASS_PLT;
+ default:
+ return 0;
+ }
+
+ case EM_SPARC:
+ case EM_SPARC32PLUS:
+ switch (type)
+ {
+ case R_SPARC_COPY:
+ return ELF_RTYPE_CLASS_COPY;
+ case R_SPARC_JMP_SLOT:
+ case R_SPARC_TLS_DTPMOD32:
+ case R_SPARC_TLS_DTPOFF32:
+ case R_SPARC_TLS_TPOFF32:
+ case R_SPARC_TLS_LE_HIX22:
+ case R_SPARC_TLS_LE_LOX10:
+ return ELF_RTYPE_CLASS_PLT;
+ default:
+ return 0;
+ }
+
+ case EM_SPARCV9:
+ switch (type)
+ {
+ case R_SPARC_COPY:
+ return ELF_RTYPE_CLASS_COPY;
+ case R_SPARC_JMP_SLOT:
+ case R_SPARC_TLS_DTPMOD64:
+ case R_SPARC_TLS_DTPOFF64:
+ case R_SPARC_TLS_TPOFF64:
+ case R_SPARC_TLS_LE_HIX22:
+ case R_SPARC_TLS_LE_LOX10:
+ return ELF_RTYPE_CLASS_PLT;
+ default:
+ return 0;
+ }
+
+ default:
+ printf ("Unknown architecture!\n");
+ exit (1);
+ return 0;
+ }
+}
+
+int
+is_ldso_soname (const char *soname)
+{
+ if (! strcmp (soname, "ld-linux.so.2")
+ || ! strcmp (soname, "ld-linux.so.3")
+ || ! strcmp (soname, "ld.so.1")
+ || ! strcmp (soname, "ld-linux-ia64.so.2")
+ || ! strcmp (soname, "ld-linux-x86-64.so.2")
+ || ! strcmp (soname, "ld64.so.1"))
+ return 1;
+ return 0;
+}
+
+
+struct needed_list
+{
+ struct dso_list *ent;
+ struct needed_list *next;
+};
+
+struct dso_list
+{
+ DSO *dso;
+ struct ldlibs_link_map *map;
+ struct dso_list *next, *prev;
+ struct needed_list *needed, *needed_tail;
+ const char *name;
+ struct dso_list *loader;
+ const char *canon_filename;
+};
+
+static int dso_open_error = 0;
+
+static void
+free_needed (struct needed_list *p)
+{
+ struct needed_list *old_p = p;
+ while (old_p)
+ {
+ old_p = p->next;
+ free (p);
+ p = old_p;
+ }
+}
+
+static struct dso_list *
+in_dso_list (struct dso_list *dso_list, const char *soname, const char *filename)
+{
+ while (dso_list != NULL)
+ {
+ if (dso_list->dso != NULL)
+ {
+ if (strcmp (dso_list->dso->soname, soname) == 0)
+ return dso_list;
+ }
+
+ if (strcmp (dso_list->name, soname) == 0)
+ return dso_list;
+
+ if (filename && dso_list->canon_filename
+ && strcmp (dso_list->canon_filename, filename) == 0)
+ return dso_list;
+
+ dso_list = dso_list->next;
+ }
+ return NULL;
+}
+
+static int
+in_needed_list (struct needed_list *needed_list, const char *soname)
+{
+ while (needed_list != NULL)
+ {
+ if (needed_list->ent->dso != NULL
+ && strcmp (needed_list->ent->dso->soname, soname) == 0)
+ return 1;
+ needed_list = needed_list->next;
+ }
+ return 0;
+}
+
+
+/****/
+
+void
+add_dir (struct search_path *path, const char *dir, int dirlen)
+{
+ if (path->allocated == 0)
+ {
+ path->allocated = 5;
+ path->dirs = malloc (sizeof (char *) * 5);
+ }
+ else if (path->count == path->allocated)
+ {
+ path->allocated *= 2;
+ path->dirs = realloc (path->dirs, sizeof (char *) * path->allocated);
+ }
+ path->dirs[path->count] = malloc (dirlen + 1);
+ memcpy (path->dirs[path->count], dir, dirlen);
+ path->dirs[path->count++][dirlen] = 0;
+
+ if (path->maxlen < dirlen)
+ path->maxlen = dirlen;
+}
+
+void
+free_path (struct search_path *path)
+{
+ if (path->allocated)
+ {
+ int i;
+ for (i = 0; i < path->count; i++)
+ free (path->dirs[i]);
+ free (path->dirs);
+ }
+}
+
+void
+load_ld_so_conf (int use_64bit, int use_mipsn32)
+{
+ int fd;
+ FILE *conf;
+ char buf[1024];
+
+ memset (&ld_dirs, 0, sizeof (ld_dirs));
+
+ /* Only use the correct machine, to prevent mismatches if we
+ have both /lib/ld.so and /lib64/ld.so on x86-64. */
+ if (use_64bit)
+ {
+ add_dir (&ld_dirs, "/lib64/tls", strlen ("/lib64/tls"));
+ add_dir (&ld_dirs, "/lib64", strlen ("/lib64"));
+ add_dir (&ld_dirs, "/usr/lib64/tls", strlen ("/usr/lib64/tls"));
+ add_dir (&ld_dirs, "/usr/lib64", strlen ("/usr/lib64"));
+ }
+ else if (use_mipsn32)
+ {
+ add_dir (&ld_dirs, "/lib32/tls", strlen ("/lib32/tls"));
+ add_dir (&ld_dirs, "/lib32", strlen ("/lib32"));
+ add_dir (&ld_dirs, "/usr/lib32/tls", strlen ("/usr/lib32/tls"));
+ add_dir (&ld_dirs, "/usr/lib32", strlen ("/usr/lib32"));
+ }
+ else
+ {
+ add_dir (&ld_dirs, "/lib/tls", strlen ("/lib/tls"));
+ add_dir (&ld_dirs, "/lib", strlen ("/lib"));
+ add_dir (&ld_dirs, "/usr/lib/tls", strlen ("/usr/lib/tls"));
+ add_dir (&ld_dirs, "/usr/lib", strlen ("/usr/lib"));
+ }
+
+ fd = wrap_open ("/etc/ld.so.conf", O_RDONLY);
+ if (fd == -1)
+ return;
+ conf = fdopen (fd, "r");
+ while (fgets (buf, 1024, conf) != NULL)
+ {
+ int len;
+ char *p;
+
+ p = strchr (buf, '#');
+ if (p)
+ *p = 0;
+ len = strlen (buf);
+ while (len > 0 && isspace (buf[len - 1]))
+ buf[--len] = 0;
+
+ if (len > 0)
+ add_dir (&ld_dirs, buf, len);
+ }
+ fclose (conf);
+}
+
+void
+string_to_path (struct search_path *path, const char *string)
+{
+ const char *start, *end, *end_tmp;
+
+ start = string;
+ while (1) {
+ end = start;
+ while (*end && *end != ':' && *end != ';')
+ end ++;
+
+ /* Eliminate any trailing '/' characters, but be sure to leave a
+ leading slash if someeone wants / in their RPATH. */
+ end_tmp = end;
+ while (end_tmp > start + 1 && end_tmp[-1] == '/')
+ end_tmp --;
+
+ add_dir (path, start, end_tmp - start);
+
+ if (*end == 0)
+ break;
+
+ /* Skip the separator. */
+ start = end + 1;
+ }
+}
+
+char *
+find_lib_in_path (struct search_path *path, const char *soname,
+ int elfclass, int machine)
+{
+ char *ret;
+ int i;
+ int alt_machine;
+
+ switch (machine)
+ {
+ case EM_SPARC:
+ alt_machine = EM_SPARC32PLUS;
+ break;
+ case EM_SPARC32PLUS:
+ alt_machine = EM_SPARC;
+ break;
+ default:
+ alt_machine = machine;
+ break;
+ }
+
+ ret = malloc (strlen (soname) + 2 + path->maxlen);
+
+ for (i = 0; i < path->count; i++)
+ {
+ sprintf (ret, "%s/%s", path->dirs[i], soname);
+ if (wrap_access (ret, F_OK) == 0)
+ {
+ DSO *dso = open_dso (ret);
+ int dso_class = gelf_getclass (dso->elf);
+ int dso_machine = (dso_class == ELFCLASS32) ?
+ elf32_getehdr (dso->elf)->e_machine :
+ elf64_getehdr (dso->elf)->e_machine;
+
+ if (dso == NULL)
+ continue;
+
+ /* Skip 32-bit libraries when looking for 64-bit. Also
+ skip libraries for alternative machines. */
+ if (gelf_getclass (dso->elf) != elfclass
+ || (dso_machine != machine && dso_machine != alt_machine))
+ {
+ close_dso (dso);
+ continue;
+ }
+
+ close_dso (dso);
+ return ret;
+ }
+ }
+
+ free (ret);
+ return NULL;
+}
+
+char *
+find_lib_by_soname (const char *soname, struct dso_list *loader,
+ int elfclass, int machine)
+{
+ char *ret;
+
+ if (strchr (soname, '/'))
+ return strdup (soname);
+
+ if (loader->dso->info[DT_RUNPATH] == 0)
+ {
+ /* Search DT_RPATH all the way up. */
+ struct dso_list *loader_p = loader;
+ while (loader_p)
+ {
+ if (loader_p->dso->info[DT_RPATH])
+ {
+ struct search_path r_path;
+ const char *rpath = get_data (loader_p->dso,
+ loader_p->dso->info[DT_STRTAB]
+ + loader_p->dso->info[DT_RPATH],
+ NULL, NULL);
+ memset (&r_path, 0, sizeof (r_path));
+ string_to_path (&r_path, rpath);
+ ret = find_lib_in_path (&r_path, soname, elfclass, machine);
+ free_path (&r_path);
+ if (ret)
+ return ret;
+ }
+ loader_p = loader_p->loader;
+ }
+ }
+
+ ret = find_lib_in_path (&ld_library_search_path, soname, elfclass, machine);
+ if (ret)
+ return ret;
+
+ if (loader->dso->info[DT_RUNPATH])
+ {
+ struct search_path r_path;
+ const char *rpath = get_data (loader->dso,
+ loader->dso->info[DT_STRTAB]
+ + loader->dso->info[DT_RUNPATH],
+ NULL, NULL);
+ memset (&r_path, 0, sizeof (r_path));
+ string_to_path (&r_path, rpath);
+ ret = find_lib_in_path (&r_path, soname, elfclass, machine);
+ free_path (&r_path);
+ if (ret)
+ return ret;
+ }
+
+ ret = find_lib_in_path (&ld_dirs, soname, elfclass, machine);
+ if (ret)
+ return ret;
+
+ return NULL;
+}
+
+static struct dso_list *
+load_dsos (DSO *dso, int host_paths)
+{
+ struct dso_list *dso_list, *dso_list_tail, *cur_dso_ent, *new_dso_ent;
+ struct stat64 st;
+
+ dso_list = malloc (sizeof (struct dso_list));
+ dso_list->dso = dso;
+ dso_list->next = NULL;
+ dso_list->prev = NULL;
+ dso_list->needed = NULL;
+ dso_list->name = dso->filename;
+ dso_list->loader = NULL;
+
+ if (host_paths)
+ dso_list->canon_filename = canonicalize_file_name (dso->filename);
+ else
+ dso_list->canon_filename = prelink_canonicalize (dso->filename, &st);
+
+ if (dso_list->canon_filename == NULL)
+ dso_list->canon_filename = strdup (dso->filename);
+
+ cur_dso_ent = dso_list_tail = dso_list;
+
+ while (cur_dso_ent != NULL)
+ {
+ DSO *cur_dso, *new_dso;
+ Elf_Scn *scn;
+ Elf_Data *data;
+ GElf_Dyn dyn;
+
+ cur_dso = cur_dso_ent->dso;
+ if (cur_dso == NULL)
+ {
+ cur_dso_ent = cur_dso_ent->next;
+ continue;
+ }
+
+ scn = cur_dso->scn[cur_dso->dynamic];
+ data = NULL;
+ while ((data = elf_getdata (scn, data)) != NULL)
+ {
+ int ndx, maxndx;
+ maxndx = data->d_size / cur_dso->shdr[cur_dso->dynamic].sh_entsize;
+ for (ndx = 0; ndx < maxndx; ++ndx)
+ {
+ gelfx_getdyn (cur_dso->elf, data, ndx, &dyn);
+ if (dyn.d_tag == DT_NULL)
+ break;
+ if (dyn.d_tag == DT_NEEDED)
+ {
+ char *new_name=NULL, *new_canon_name=NULL;
+ const char *soname = get_data (cur_dso,
+ cur_dso->info[DT_STRTAB]
+ + dyn.d_un.d_val,
+ NULL, NULL);
+ new_dso_ent = in_dso_list (dso_list, soname, NULL);
+ if (new_dso_ent == NULL)
+ {
+ int machine;
+ int class = gelf_getclass (dso->elf);
+ machine = (class == ELFCLASS32) ?
+ elf32_getehdr (dso->elf)->e_machine :
+ elf64_getehdr (dso->elf)->e_machine;
+ new_name = find_lib_by_soname (soname, cur_dso_ent,
+ class, machine);
+ if (new_name == 0 || wrap_access (new_name, R_OK) < 0)
+ {
+ dso_open_error ++;
+
+ new_dso_ent = malloc (sizeof (struct dso_list));
+ dso_list_tail->next = new_dso_ent;
+ dso_list_tail->next->prev = dso_list_tail;
+ dso_list_tail = dso_list_tail->next;
+ dso_list_tail->next = NULL;
+ dso_list_tail->dso = NULL;
+ dso_list_tail->needed = NULL;
+ dso_list_tail->name = soname;
+ dso_list_tail->loader = NULL;
+ dso_list_tail->canon_filename = soname;
+
+ continue;
+ }
+
+ /* See if the filename we found has already been
+ opened (possibly under a different SONAME via
+ some symlink). */
+ new_canon_name = prelink_canonicalize (new_name, NULL);
+ if (new_canon_name == NULL)
+ new_canon_name = strdup (new_name);
+ new_dso_ent = in_dso_list (dso_list, soname, new_canon_name);
+ }
+ else if (new_dso_ent->dso == NULL)
+ continue;
+
+ if (new_dso_ent == NULL)
+ {
+ new_dso = open_dso (new_name);
+ free (new_name);
+ new_dso_ent = malloc (sizeof (struct dso_list));
+ dso_list_tail->next = new_dso_ent;
+ dso_list_tail->next->prev = dso_list_tail;
+ dso_list_tail = dso_list_tail->next;
+ dso_list_tail->next = NULL;
+ dso_list_tail->dso = new_dso;
+ dso_list_tail->needed = NULL;
+ dso_list_tail->loader = cur_dso_ent;
+ dso_list_tail->canon_filename = new_canon_name;
+
+ if (is_ldso_soname (new_dso->soname))
+ dso_list_tail->name = new_dso->filename;
+ else if (strcmp (new_dso->soname, new_dso->filename) == 0)
+ /* new_dso->soname might be a full path if the library
+ had no SONAME. Use the original SONAME instead. */
+ dso_list_tail->name = soname;
+ else
+ /* Use the new SONAME if possible, in case some library
+ links to this one using an incorrect SONAME. */
+ dso_list_tail->name = new_dso->soname;
+ }
+
+ if (!cur_dso_ent->needed)
+ {
+ cur_dso_ent->needed = malloc (sizeof (struct needed_list));
+ cur_dso_ent->needed_tail = cur_dso_ent->needed;
+ cur_dso_ent->needed_tail->ent = new_dso_ent;
+ cur_dso_ent->needed_tail->next = NULL;
+ }
+ else if (!in_needed_list (cur_dso_ent->needed, soname))
+ {
+ cur_dso_ent->needed_tail->next = malloc (sizeof (struct needed_list));
+ cur_dso_ent->needed_tail = cur_dso_ent->needed_tail->next;
+ cur_dso_ent->needed_tail->ent = new_dso_ent;
+ cur_dso_ent->needed_tail->next = NULL;
+ }
+
+ continue;
+ }
+ if (dyn.d_tag == DT_FILTER || dyn.d_tag == DT_AUXILIARY)
+ {
+ // big fat warning;
+ }
+ }
+ }
+ cur_dso_ent = cur_dso_ent->next;
+ }
+ return dso_list;
+}
+
+static void
+get_version_info (DSO *dso, struct ldlibs_link_map *map)
+{
+ int i;
+ Elf_Data *data;
+ int ndx_high;
+ const char *strtab = map->l_info[DT_STRTAB];
+
+ /* Fortunately, 32-bit and 64-bit ELF use the same Verneed and Verdef
+ structures, so this function will work for either. */
+
+ Elf64_Verneed *verneed;
+ Elf64_Verdef *verdef;
+
+ map->l_versyms = NULL;
+
+ if (dso->info_set_mask & (1ULL << DT_VERNEED_BIT))
+ {
+ i = addr_to_sec (dso, dso->info_DT_VERNEED);
+ data = elf_getdata (dso->scn[i], NULL);
+ verneed = data->d_buf;
+ }
+ else
+ verneed = NULL;
+
+ if (dso->info_set_mask & (1ULL << DT_VERDEF_BIT))
+ {
+ i = addr_to_sec (dso, dso->info_DT_VERDEF);
+ data = elf_getdata (dso->scn[i], NULL);
+ verdef = data->d_buf;
+ }
+ else
+ verdef = NULL;
+
+ ndx_high = 0;
+ if (verneed)
+ {
+ Elf64_Verneed *ent = verneed;
+ Elf64_Vernaux *aux;
+ while (1)
+ {
+ aux = (Elf64_Vernaux *) ((char *) ent + ent->vn_aux);
+ while (1)
+ {
+ if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high)
+ ndx_high = aux->vna_other & 0x7fff;
+
+ if (aux->vna_next == 0)
+ break;
+ aux = (Elf64_Vernaux *) ((char *) aux + aux->vna_next);
+ }
+
+ if (ent->vn_next == 0)
+ break;
+ ent = (Elf64_Verneed *) ((char *) ent + ent->vn_next);
+ }
+ }
+
+ if (verdef)
+ {
+ Elf64_Verdef *ent = verdef;
+ while (1)
+ {
+ if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high)
+ ndx_high = ent->vd_ndx & 0x7fff;
+
+ if (ent->vd_next == 0)
+ break;
+ ent = (Elf64_Verdef *) ((char *) ent + ent->vd_next);
+ }
+ }
+
+ if (ndx_high)
+ {
+ map->l_versions = (struct r_found_version *)
+ calloc (ndx_high + 1, sizeof (struct r_found_version));
+ map->l_nversions = ndx_high + 1;
+
+ i = addr_to_sec (dso, dso->info_DT_VERSYM);
+ data = elf_getdata (dso->scn[i], NULL);
+ map->l_versyms = data->d_buf;
+
+ if (verneed)
+ {
+ Elf64_Verneed *ent = verneed;
+
+ while (1)
+ {
+ Elf64_Vernaux *aux;
+ aux = (Elf64_Vernaux *) ((char *) ent + ent->vn_aux);
+ while (1)
+ {
+ Elf64_Half ndx = aux->vna_other & 0x7fff;
+ map->l_versions[ndx].hash = aux->vna_hash;
+ map->l_versions[ndx].hidden = aux->vna_other & 0x8000;
+ map->l_versions[ndx].name = &strtab[aux->vna_name];
+ map->l_versions[ndx].filename = &strtab[ent->vn_file];
+
+ if (aux->vna_next == 0)
+ break;
+ aux = (Elf64_Vernaux *) ((char *) aux + aux->vna_next);
+ }
+
+ if (ent->vn_next == 0)
+ break;
+ ent = (Elf64_Verneed *) ((char *) ent + ent->vn_next);
+ }
+ }
+
+ if (verdef)
+ {
+ Elf64_Verdef *ent = verdef;
+ Elf64_Verdaux *aux;
+ while (1)
+ {
+ aux = (Elf64_Verdaux *) ((char *) ent + ent->vd_aux);
+
+ if ((ent->vd_flags & VER_FLG_BASE) == 0)
+ {
+ /* The name of the base version should not be
+ available for matching a versioned symbol. */
+ Elf64_Half ndx = ent->vd_ndx & 0x7fff;
+ map->l_versions[ndx].hash = ent->vd_hash;
+ map->l_versions[ndx].name = &strtab[aux->vda_name];
+ map->l_versions[ndx].filename = NULL;
+ }
+
+ if (ent->vd_next == 0)
+ break;
+ ent = (Elf64_Verdef *) ((char *) ent + ent->vd_next);
+ }
+ }
+ }
+}
+
+const char *rtld_progname;
+
+static Elf64_Addr load_addr = 0xdead0000;
+
+static void
+create_ldlibs_link_map (struct dso_list *cur_dso_ent)
+{
+ struct ldlibs_link_map *map = malloc (sizeof (struct ldlibs_link_map));
+ DSO *dso = cur_dso_ent->dso;
+ int i;
+ Elf_Data *data;
+ Elf_Symndx *hash;
+
+ memset (map, 0, sizeof (*map));
+ cur_dso_ent->map = map;
+
+ if (is_ldso_soname (cur_dso_ent->dso->soname))
+ {
+ map->l_name = dso->filename;
+ rtld_progname = dso->filename;
+ }
+ else
+ map->l_name = dso->soname;
+ map->l_soname = dso->soname;
+ map->filename = dso->filename;
+
+ if (dso->ehdr.e_type == ET_EXEC)
+ map->l_type = lt_executable;
+ else
+ map->l_type = lt_library;
+
+ /* FIXME: gelfify, endianness issues */
+ /* and leaks? */
+ i = addr_to_sec (dso, dso->info[DT_SYMTAB]);
+ data = elf_getdata (dso->scn[i], NULL);
+ map->l_info[DT_SYMTAB] = data->d_buf;
+
+ i = addr_to_sec (dso, dso->info[DT_STRTAB]);
+ data = elf_getdata (dso->scn[i], NULL);
+ map->l_info[DT_STRTAB] = data->d_buf;
+
+ map->l_gnu_hash = dynamic_info_is_set (dso, DT_GNU_HASH_BIT);
+ if (map->l_gnu_hash)
+ i = addr_to_sec (dso, dso->info_DT_GNU_HASH);
+ else
+ i = addr_to_sec (dso, dso->info[DT_HASH]);
+ data = elf_getdata (dso->scn[i], NULL);
+ hash = data->d_buf;
+ map->l_nbuckets = hash[0];
+ if (map->l_gnu_hash)
+ {
+ map->l_nmaskwords = hash[2];
+ map->l_maskword64 = gelf_getclass (dso->elf) == ELFCLASS64;
+ map->l_shift = hash[3];
+ map->l_maskwords = hash + 4;
+ map->l_buckets = hash + 4 + (map->l_nmaskwords << map->l_maskword64);
+ map->l_chain = map->l_buckets + map->l_nbuckets - hash[1];
+ }
+ else
+ {
+ map->l_buckets = hash + 2;
+ map->l_chain = map->l_buckets + map->l_nbuckets;
+ }
+
+ get_version_info (dso, map);
+
+ map->l_map_start = load_addr;
+ load_addr += 0x1000;
+
+ map->sym_base = dso->info[DT_SYMTAB] - dso->base;
+
+ for (i = 0; i < dso->ehdr.e_phnum; ++i)
+ if (dso->phdr[i].p_type == PT_TLS)
+ {
+ map->l_tls_blocksize = dso->phdr[i].p_memsz;
+ map->l_tls_align = dso->phdr[i].p_align;
+ if (map->l_tls_align == 0)
+ map->l_tls_firstbyte_offset = 0;
+ else
+ map->l_tls_firstbyte_offset = dso->phdr[i].p_vaddr & (map->l_tls_align - 1);
+ break;
+ }
+}
+
+struct
+{
+ void *symptr;
+ int rtypeclass;
+} cache;
+
+void
+do_reloc (DSO *dso, struct ldlibs_link_map *map, struct r_scope_elem *scope,
+ GElf_Word sym, GElf_Word type)
+{
+ struct r_found_version *ver;
+ int rtypeclass;
+ void *symptr;
+ const char *name;
+ Elf64_Word st_name;
+
+ if (map->l_versyms)
+ {
+ int vernum = map->l_versyms[sym] & 0x7fff;
+ ver = &map->l_versions[vernum];
+ }
+ else
+ ver = NULL;
+
+ rtypeclass = reloc_type_class (type, dso->ehdr.e_machine);
+
+ if (gelf_getclass (dso->elf) == ELFCLASS32)
+ {
+ Elf32_Sym *sym32 = &((Elf32_Sym *)map->l_info[DT_SYMTAB])[sym];
+
+ if (ELF32_ST_BIND (sym32->st_info) == STB_LOCAL)
+ return;
+ symptr = sym32;
+ st_name = sym32->st_name;
+ }
+ else
+ {
+ Elf64_Sym *sym64 = &((Elf64_Sym *)map->l_info[DT_SYMTAB])[sym];
+
+ if (ELF64_ST_BIND (sym64->st_info) == STB_LOCAL)
+ return;
+ symptr = sym64;
+ st_name = sym64->st_name;
+ }
+
+ if (cache.symptr == symptr && cache.rtypeclass == rtypeclass)
+ return;
+ cache.symptr = symptr;
+ cache.rtypeclass = rtypeclass;
+
+ name = ((const char *)map->l_info[DT_STRTAB]) + st_name;
+
+ if (gelf_getclass (dso->elf) == ELFCLASS32)
+ {
+ if (ver && ver->hash)
+ rtld_lookup_symbol_versioned (name, symptr, scope, ver, rtypeclass,
+ map, dso->ehdr.e_machine);
+ else
+ rtld_lookup_symbol (name, symptr, scope, rtypeclass, map,
+ dso->ehdr.e_machine);
+ }
+ else
+ {
+ if (ver && ver->hash)
+ rtld_lookup_symbol_versioned64 (name, symptr, scope, ver, rtypeclass,
+ map, dso->ehdr.e_machine);
+ else
+ rtld_lookup_symbol64 (name, symptr, scope, rtypeclass, map,
+ dso->ehdr.e_machine);
+ }
+}
+
+void
+do_rel_section (DSO *dso, struct ldlibs_link_map *map,
+ struct r_scope_elem *scope,
+ int tag, int section)
+{
+ Elf_Data *data;
+ int ndx, maxndx, sym, type;
+
+ data = elf_getdata (dso->scn[section], NULL);
+ maxndx = data->d_size / dso->shdr[section].sh_entsize;
+ for (ndx = 0; ndx < maxndx; ndx++)
+ {
+ if (tag == DT_REL)
+ {
+ GElf_Rel rel;
+ gelfx_getrel (dso->elf, data, ndx, &rel);
+ sym = reloc_r_sym (dso, rel.r_info);
+ type = reloc_r_type (dso, rel.r_info);
+ }
+ else
+ {
+ GElf_Rela rela;
+ gelfx_getrela (dso->elf, data, ndx, &rela);
+ sym = reloc_r_sym (dso, rela.r_info);
+ type = reloc_r_type (dso, rela.r_info);
+ }
+ if (sym != 0)
+ do_reloc (dso, map, scope, sym, type);
+ }
+}
+
+void
+do_relocs (DSO *dso, struct ldlibs_link_map *map, struct r_scope_elem *scope, int tag)
+{
+ GElf_Addr rel_start, rel_end;
+ GElf_Addr pltrel_start, pltrel_end;
+ int first, last;
+
+ /* Load the DT_REL or DT_RELA section. */
+ if (dso->info[tag] != 0)
+ {
+ rel_start = dso->info[tag];
+ rel_end = rel_start + dso->info[tag == DT_REL ? DT_RELSZ : DT_RELASZ];
+ first = addr_to_sec (dso, rel_start);
+ last = addr_to_sec (dso, rel_end - 1);
+ while (first <= last)
+ do_rel_section (dso, map, scope, tag, first++);
+
+ /* If the DT_JMPREL relocs are of the same type and not included,
+ load them too. Assume they overlap completely or not at all,
+ and are in at most a single section. They also need to be adjacent. */
+ if (dso->info[DT_PLTREL] == tag)
+ {
+ pltrel_start = dso->info[DT_JMPREL];
+ pltrel_end = pltrel_start + dso->info[DT_PLTRELSZ];
+ if (pltrel_start < rel_start || pltrel_start >= rel_end)
+ do_rel_section (dso, map, scope, tag, addr_to_sec (dso, pltrel_start));
+ }
+ }
+ else if (dso->info[DT_PLTREL] == tag)
+ do_rel_section (dso, map, scope, tag, addr_to_sec (dso, dso->info[DT_JMPREL]));
+}
+
+/* MIPS GOTs are not handled by normal relocations. Instead, entry X
+ in the global GOT is associated with symbol DT_MIPS_GOTSYM + X.
+ For the purposes of symbol lookup and conflict resolution, we need
+ to act as though entry X had a dynamic relocation against symbol
+ DT_MIPS_GOTSYM + X. */
+
+void
+do_mips_global_got_relocs (DSO *dso, struct ldlibs_link_map *map,
+ struct r_scope_elem *scope)
+{
+ GElf_Word i;
+
+ for (i = dso->info_DT_MIPS_GOTSYM; i < dso->info_DT_MIPS_SYMTABNO; i++)
+ do_reloc (dso, map, scope, i, R_MIPS_REL32);
+}
+
+void
+handle_relocs_in_entry (struct dso_list *entry, struct dso_list *dso_list)
+{
+ GElf_Word i;
+
+ do_relocs (entry->dso, entry->map, dso_list->map->l_local_scope, DT_REL);
+ do_relocs (entry->dso, entry->map, dso_list->map->l_local_scope, DT_RELA);
+ if (entry->dso->ehdr.e_machine == EM_MIPS)
+ do_mips_global_got_relocs (entry->dso, entry->map,
+ dso_list->map->l_local_scope);
+}
+
+void
+handle_relocs (DSO *dso, struct dso_list *dso_list)
+{
+ struct dso_list *ldso, *tail;
+
+ /* do them all last to first.
+ skip the dynamic linker; then do it last
+ in glibc this is conditional on the opencount; but every binary
+ should be linked to libc and thereby have an opencount for ld.so...
+ besides, that's the only way it would get on our dso list. */
+
+ tail = dso_list;
+ while (tail->next)
+ tail = tail->next;
+
+ ldso = NULL;
+ while (tail)
+ {
+ if (is_ldso_soname (tail->dso->soname))
+ ldso = tail;
+ else
+ handle_relocs_in_entry (tail, dso_list);
+ tail = tail->prev;
+ }
+
+ if (ldso)
+ handle_relocs_in_entry (ldso, dso_list);
+}
+
+void
+add_to_scope (struct r_scope_elem *scope, struct dso_list *ent)
+{
+ struct needed_list *n;
+ int i;
+
+ for (i = 0; i < scope->r_nlist; i++)
+ if (scope->r_list[i] == ent->map)
+ return;
+
+ scope->r_list[scope->r_nlist++] = ent->map;
+ n = ent->needed;
+ while (n)
+ {
+ add_to_scope (scope, n->ent);
+ n = n->next;
+ }
+}
+
+void
+build_local_scope (struct dso_list *ent, int max)
+{
+ ent->map->l_local_scope = malloc (sizeof (struct r_scope_elem));
+ ent->map->l_local_scope->r_list = malloc (sizeof (struct ldlibs_link_map *) * max);
+ ent->map->l_local_scope->r_nlist = 0;
+ add_to_scope (ent->map->l_local_scope, ent);
+}
+
+/* Assign TLS offsets for every loaded library. This code is taken
+ almost directly from glibc! */
+
+#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
+
+static void
+determine_tlsoffsets (int e_machine, struct r_scope_elem *search_list)
+{
+ uint64_t freetop = 0;
+ uint64_t freebottom = 0;
+ uint64_t offset;
+ uint64_t modid = 1;
+ int i;
+
+ /* This comes from each architecture's ABI. If TLS_TCB_AT_TP, then
+ set offset to -1; if TLS_DTV_AT_TP, then set offset to
+ TLS_TCB_SIZE. */
+ switch (e_machine)
+ {
+ case EM_X86_64:
+ offset = -1;
+ break;
+
+ case EM_386:
+ offset = -1;
+ break;
+
+ case EM_SH:
+ offset = 8;
+ break;
+
+ case EM_PPC:
+ offset = 0;
+ break;
+
+ case EM_PPC64:
+ offset = 0;
+ break;
+
+ case EM_ARM:
+ offset = 8;
+ break;
+
+ case EM_MIPS:
+ offset = 0;
+ break;
+
+ case EM_SPARC:
+ case EM_SPARC32PLUS:
+ offset = -1;
+ break;
+
+ case EM_SPARCV9:
+ offset = -1;
+ break;
+
+ default:
+ /* Hope there's no TLS! */
+ for (i = 0; i < search_list->r_nlist; i++)
+ {
+ struct ldlibs_link_map *map = search_list->r_list[i];
+
+ if (map->l_tls_blocksize > 0)
+ error (1, 0, "TLS encountered on an unsupported architecture");
+ }
+
+ return;
+ }
+
+ /* Loop over the loaded DSOs. We use the symbol search order; this
+ should be the same as glibc's ordering, which traverses l_next.
+ It's somewhat important that we use both the same ordering to
+ assign module IDs and the same algorithm to assign offsets,
+ because the prelinker will resolve all relocations using these
+ offsets... and then glibc will recalculate them. Future dynamic
+ relocations in any loaded modules will use glibc's values. Also
+ if we take too much space here, glibc won't allocate enough
+ static TLS area to hold it. */
+
+ if (offset == (uint64_t) -1)
+ {
+ /* We simply start with zero. */
+ offset = 0;
+
+ for (i = 0; i < search_list->r_nlist; i++)
+ {
+ struct ldlibs_link_map *map = search_list->r_list[i];
+ uint64_t firstbyte = (-map->l_tls_firstbyte_offset
+ & (map->l_tls_align - 1));
+ uint64_t off;
+
+ if (map->l_tls_blocksize == 0)
+ continue;
+ map->l_tls_modid = modid++;
+
+ if (freebottom - freetop >= map->l_tls_blocksize)
+ {
+ off = roundup (freetop + map->l_tls_blocksize
+ - firstbyte, map->l_tls_align)
+ + firstbyte;
+ if (off <= freebottom)
+ {
+ freetop = off;
+
+ map->l_tls_offset = off;
+ continue;
+ }
+ }
+
+ off = roundup (offset + map->l_tls_blocksize - firstbyte,
+ map->l_tls_align) + firstbyte;
+ if (off > offset + map->l_tls_blocksize
+ + (freebottom - freetop))
+ {
+ freetop = offset;
+ freebottom = off - map->l_tls_blocksize;
+ }
+ offset = off;
+
+ map->l_tls_offset = off;
+ }
+ }
+ else
+ {
+ for (i = 0; i < search_list->r_nlist; i++)
+ {
+ struct ldlibs_link_map *map = search_list->r_list[i];
+ uint64_t firstbyte = (-map->l_tls_firstbyte_offset
+ & (map->l_tls_align - 1));
+ uint64_t off;
+
+ if (map->l_tls_blocksize == 0)
+ continue;
+ map->l_tls_modid = modid++;
+
+ if (map->l_tls_blocksize <= freetop - freebottom)
+ {
+ off = roundup (freebottom, map->l_tls_align);
+ if (off - freebottom < firstbyte)
+ off += map->l_tls_align;
+ if (off + map->l_tls_blocksize - firstbyte <= freetop)
+ {
+ map->l_tls_offset = off - firstbyte;
+ freebottom = (off + map->l_tls_blocksize
+ - firstbyte);
+ continue;
+ }
+ }
+
+ off = roundup (offset, map->l_tls_align);
+ if (off - offset < firstbyte)
+ off += map->l_tls_align;
+
+ map->l_tls_offset = off - firstbyte;
+ if (off - firstbyte - offset > freetop - freebottom)
+ {
+ freebottom = offset;
+ freetop = off - firstbyte;
+ }
+
+ offset = off + map->l_tls_blocksize - firstbyte;
+ }
+ }
+}
+
+static struct argp argp = { options, parse_opt, "[FILES]", argp_doc };
+
+struct ldlibs_link_map *requested_map;
+
+static void process_one_dso (DSO *dso, int host_paths);
+
+int
+main(int argc, char **argv)
+{
+ int remaining;
+ int multiple = 0;
+ host_paths = 1;
+
+ sysroot = getenv ("PRELINK_SYSROOT");
+#ifdef DEFAULT_SYSROOT
+ if (sysroot == NULL)
+ {
+ extern char *make_relative_prefix (const char *, const char *, const char *);
+ sysroot = make_relative_prefix (argv[0], BINDIR, DEFAULT_SYSROOT);
+ }
+#endif
+
+ elf_version (EV_CURRENT);
+
+ argp_parse (&argp, argc, argv, 0, &remaining, 0);
+
+ if (sysroot)
+ sysroot = canonicalize_file_name (sysroot);
+
+ if (remaining == argc)
+ error (1, 0, "missing file arguments\nTry `%s: --help' for more information.", argv[0]);
+
+ if ((argc-remaining) >= 2)
+ multiple = 1;
+
+ while (remaining < argc)
+ {
+ DSO *dso = NULL;
+ int i, fd;
+
+ if (host_paths)
+ fd = open (argv[remaining], O_RDONLY);
+ else
+ fd = wrap_open (argv[remaining], O_RDONLY);
+
+ if (fd >= 0)
+ dso = fdopen_dso (fd, argv[remaining]);
+
+ if (dso == NULL)
+ error (1, errno, "Could not open %s", argv[remaining]);
+
+ load_ld_so_conf (gelf_getclass (dso->elf) == ELFCLASS64,
+ ( dso->ehdr.e_machine == EM_MIPS) && ( dso->ehdr.e_flags & EF_MIPS_ABI2 ) );
+
+ if (multiple)
+ printf ("%s:\n", argv[remaining]);
+
+ for (i = 0; i < dso->ehdr.e_phnum; ++i)
+ if (dso->phdr[i].p_type == PT_INTERP)
+ break;
+
+ /* If there are no PT_INTERP segments, it is statically linked. */
+ if (dso->ehdr.e_type == ET_EXEC && i == dso->ehdr.e_phnum)
+ printf ("\tnot a dynamic executable\n");
+ else
+ process_one_dso (dso, host_paths);
+
+ remaining++;
+ }
+
+ return 0;
+}
+
+static void
+process_one_dso (DSO *dso, int host_paths)
+{
+ struct dso_list *dso_list, *cur_dso_ent, *old_dso_ent;
+ const char *req = getenv ("RTLD_TRACE_PRELINKING");
+ int i, flag;
+ int process_relocs = 0;
+
+ /* Close enough. Really it's if LD_WARN is "" and RTLD_TRACE_PRELINKING. */
+ if (getenv ("LD_WARN") == 0 && req != NULL)
+ process_relocs = 1;
+
+ dso_list = load_dsos (dso, host_paths);
+
+ cur_dso_ent = dso_list;
+ i = 0;
+ while (cur_dso_ent)
+ {
+ if (cur_dso_ent->dso)
+ {
+ create_ldlibs_link_map (cur_dso_ent);
+ if (req && strcmp (req, cur_dso_ent->dso->filename) == 0)
+ requested_map = cur_dso_ent->map;
+ i++;
+ }
+ cur_dso_ent = cur_dso_ent->next;
+ }
+ dso_list->map->l_local_scope = malloc (sizeof (struct r_scope_elem));
+ dso_list->map->l_local_scope->r_list = malloc (sizeof (struct ldlibs_link_map *) * i);
+ dso_list->map->l_local_scope->r_nlist = i;
+ cur_dso_ent = dso_list;
+ i = 0;
+ while (cur_dso_ent)
+ {
+ if (cur_dso_ent->dso)
+ {
+ dso_list->map->l_local_scope->r_list[i] = cur_dso_ent->map;
+ if (cur_dso_ent != dso_list)
+ build_local_scope (cur_dso_ent, dso_list->map->l_local_scope->r_nlist);
+
+ i++;
+ }
+ cur_dso_ent = cur_dso_ent->next;
+ }
+
+ determine_tlsoffsets (dso->ehdr.e_machine, dso_list->map->l_local_scope);
+
+ cur_dso_ent = dso_list;
+ flag = 0;
+ /* In ldd mode, do not show the application. Note that we do show it
+ in list-loaded-objects RTLD_TRACE_PRELINK mode. */
+ if (req == NULL && cur_dso_ent)
+ cur_dso_ent = cur_dso_ent->next;
+ while (cur_dso_ent)
+ {
+ char *filename;
+
+ if (host_paths && sysroot && cur_dso_ent->dso)
+ {
+ const char *rooted_filename;
+
+ if (cur_dso_ent->dso->filename[0] == '/')
+ rooted_filename = cur_dso_ent->dso->filename;
+ else
+ rooted_filename = cur_dso_ent->canon_filename;
+
+ filename = malloc (strlen (rooted_filename) + strlen (sysroot) + 1);
+ strcpy (filename, sysroot);
+ strcat (filename, rooted_filename);
+ }
+ else if (cur_dso_ent->dso)
+ filename = strdup (cur_dso_ent->dso->filename);
+ else
+ filename = NULL;
+
+ /* The difference between the two numbers must be dso->base,
+ and the first number must be unique. */
+ if (cur_dso_ent->dso == NULL)
+ printf ("\t%s => not found\n", cur_dso_ent->name);
+ else if (gelf_getclass (cur_dso_ent->dso->elf) == ELFCLASS32)
+ {
+ if (process_relocs)
+ {
+ printf ("\t%s => %s (0x%08x, 0x%08x)",
+ cur_dso_ent->name, filename,
+ (uint32_t) cur_dso_ent->map->l_map_start,
+ (uint32_t) (cur_dso_ent->map->l_map_start - cur_dso_ent->dso->base));
+ if (cur_dso_ent->map->l_tls_modid)
+ printf (" TLS(0x%x, 0x%08x)",
+ (uint32_t) cur_dso_ent->map->l_tls_modid,
+ (uint32_t) cur_dso_ent->map->l_tls_offset);
+ printf ("\n");
+ }
+ else
+ printf ("\t%s => %s (0x%08x)\n",
+ cur_dso_ent->name, filename,
+ (uint32_t) cur_dso_ent->map->l_map_start);
+ }
+ else
+ {
+ if (process_relocs)
+ {
+ printf ("\t%s => %s (0x%016" HOST_LONG_LONG_FORMAT
+ "x, 0x%016" HOST_LONG_LONG_FORMAT "x)",
+ cur_dso_ent->name, filename,
+ (unsigned long long) cur_dso_ent->map->l_map_start,
+ (unsigned long long) (cur_dso_ent->map->l_map_start - cur_dso_ent->dso->base));
+ if (cur_dso_ent->map->l_tls_modid)
+ printf (" TLS(0x%x, 0x%016" HOST_LONG_LONG_FORMAT "x)",
+ (uint32_t) cur_dso_ent->map->l_tls_modid,
+ (unsigned long long) cur_dso_ent->map->l_tls_offset);
+ printf ("\n");
+ }
+ else
+ printf ("\t%s => %s (0x%08x)\n",
+ cur_dso_ent->name, filename,
+ (uint32_t) cur_dso_ent->map->l_map_start);
+ }
+
+ if (filename)
+ free (filename);
+
+ cur_dso_ent = cur_dso_ent->next;
+ flag = 1;
+ }
+
+ if (dso_open_error)
+ exit (1);
+
+ if (process_relocs)
+ handle_relocs (dso_list->dso, dso_list);
+
+ cur_dso_ent = dso_list;
+ while (cur_dso_ent)
+ {
+ if (cur_dso_ent->dso)
+ close_dso (cur_dso_ent->dso);
+ old_dso_ent = cur_dso_ent;
+ cur_dso_ent = cur_dso_ent->next;
+ if (old_dso_ent->needed)
+ free_needed (old_dso_ent->needed);
+ free (old_dso_ent);
+ }
+}