diff options
-rw-r--r-- | elf/sprof.c | 176 |
1 files changed, 132 insertions, 44 deletions
diff --git a/elf/sprof.c b/elf/sprof.c index 1a4b0681a7..afe3955ad8 100644 --- a/elf/sprof.c +++ b/elf/sprof.c @@ -48,14 +48,18 @@ #include <endian.h> #if BYTE_ORDER == BIG_ENDIAN -#define byteorder ELFDATA2MSB -#define byteorder_name "big-endian" +# define byteorder ELFDATA2MSB +# define byteorder_name "big-endian" #elif BYTE_ORDER == LITTLE_ENDIAN -#define byteorder ELFDATA2LSB -#define byteorder_name "little-endian" +# define byteorder ELFDATA2LSB +# define byteorder_name "little-endian" #else -#error "Unknown BYTE_ORDER " BYTE_ORDER -#define byteorder ELFDATANONE +# error "Unknown BYTE_ORDER " BYTE_ORDER +# define byteorder ELFDATANONE +#endif + +#ifndef PATH_MAX +# define PATH_MAX 1024 #endif @@ -82,7 +86,9 @@ static const struct argp_option options[] = }; /* Short description of program. */ -static const char doc[] = N_("Read and display shared object profiling data"); +static const char doc[] = N_("Read and display shared object profiling data.\v\ +For bug reporting instructions, please see:\n\ +<http://www.gnu.org/software/libc/bugs.html>.\n"); /* Strings for arguments in help texts. */ static const char args_doc[] = N_("SHOBJ [PROFDATA]"); @@ -93,7 +99,7 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state); /* Data structure to communicate with argp functions. */ static struct argp argp = { - options, parse_opt, args_doc, doc, NULL, NULL + options, parse_opt, args_doc, doc }; @@ -108,11 +114,8 @@ static enum DEFAULT_MODE = FLAT_MODE | CALL_GRAPH_MODE } mode; -/* If nonzero the total number of invocations of a function is emitted. */ -int count_total; - /* Nozero for testing. */ -int do_test; +static int do_test; /* Strcuture describing calls. */ struct here_fromstruct @@ -204,7 +207,7 @@ struct profdata }; /* Search tree for symbols. */ -void *symroot; +static void *symroot; static struct known_symbol **sortsym; static size_t symidx; static uintmax_t total_ticks; @@ -374,11 +377,7 @@ load_shobj (const char *name) ElfW(Ehdr) *ehdr; int fd; ElfW(Shdr) *shdr; - void *ptr; size_t pagesize = getpagesize (); - const char *shstrtab; - int idx; - ElfW(Shdr) *symtab_entry; /* Since we use dlopen() we must be prepared to work around the sometimes strange lookup rules for the shared objects. If we have a file foo.so @@ -529,39 +528,131 @@ load_shobj (const char *name) error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"), map->l_name); - /* Now map the section header. */ - ptr = mmap (NULL, (ehdr->e_shnum * sizeof (ElfW(Shdr)) - + (ehdr->e_shoff & (pagesize - 1))), PROT_READ, - MAP_SHARED|MAP_FILE, fd, ehdr->e_shoff & ~(pagesize - 1)); - if (ptr == MAP_FAILED) - error (EXIT_FAILURE, errno, _("mapping of section headers failed")); - shdr = (ElfW(Shdr) *) ((char *) ptr + (ehdr->e_shoff & (pagesize - 1))); + /* Map the section header. */ + size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr)); + shdr = (ElfW(Shdr) *) alloca (size); + if (pread (fd, shdr, size, ehdr->e_shoff) != size) + error (EXIT_FAILURE, errno, _("reading of section headers failed")); /* Get the section header string table. */ - ptr = mmap (NULL, (shdr[ehdr->e_shstrndx].sh_size - + (shdr[ehdr->e_shstrndx].sh_offset & (pagesize - 1))), - PROT_READ, MAP_SHARED|MAP_FILE, fd, - shdr[ehdr->e_shstrndx].sh_offset & ~(pagesize - 1)); - if (ptr == MAP_FAILED) + char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size); + if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size, + shdr[ehdr->e_shstrndx].sh_offset) + != shdr[ehdr->e_shstrndx].sh_size) error (EXIT_FAILURE, errno, - _("mapping of section header string table failed")); - shstrtab = ((const char *) ptr - + (shdr[ehdr->e_shstrndx].sh_offset & (pagesize - 1))); + _("reading of section header string table failed")); /* Search for the ".symtab" section. */ - symtab_entry = NULL; - for (idx = 0; idx < ehdr->e_shnum; ++idx) + ElfW(Shdr) *symtab_entry = NULL; + ElfW(Shdr) *debuglink_entry = NULL; + for (int idx = 0; idx < ehdr->e_shnum; ++idx) if (shdr[idx].sh_type == SHT_SYMTAB && strcmp (shstrtab + shdr[idx].sh_name, ".symtab") == 0) { symtab_entry = &shdr[idx]; break; } + else if (shdr[idx].sh_type == SHT_PROGBITS + && strcmp (shstrtab + shdr[idx].sh_name, ".gnu_debuglink") == 0) + debuglink_entry = &shdr[idx]; - /* We don't need the section header string table anymore. */ - munmap (ptr, (shdr[ehdr->e_shstrndx].sh_size - + (shdr[ehdr->e_shstrndx].sh_offset & (pagesize - 1)))); + /* Get the file name of the debuginfo file if necessary. */ + int symfd = fd; + if (symtab_entry == NULL && debuglink_entry != NULL) + { + size_t size = debuglink_entry->sh_size; + char *debuginfo_fname = (char *) alloca (size + 1); + debuginfo_fname[size] = '\0'; + if (pread (fd, debuginfo_fname, size, debuglink_entry->sh_offset) + != size) + { + fprintf (stderr, _("*** Cannot read debuginfo file name: %m\n")); + goto no_debuginfo; + } + static const char procpath[] = "/proc/self/fd/%d"; + char origprocname[sizeof (procpath) + sizeof (int) * 3]; + snprintf (origprocname, sizeof (origprocname), procpath, fd); + char *origlink = (char *) alloca (PATH_MAX + 1); + origlink[PATH_MAX] = '\0'; + if (readlink (origprocname, origlink, PATH_MAX) == -1) + goto no_debuginfo; + + /* Try to find the actual file. There are three places: + 1. the same directory the DSO is in + 2. in a subdir named .debug of the directory the DSO is in + 3. in /usr/lib/debug/PATH-OF-DSO + */ + char *realname = canonicalize_file_name (origlink); + char *cp = NULL; + if (realname == NULL || (cp = strrchr (realname, '/')) == NULL) + error (EXIT_FAILURE, errno, _("cannot determine file name")); + + /* Leave the last slash in place. */ + *++cp = '\0'; + + /* First add the debuginfo file name only. */ + static const char usrlibdebug[]= "/usr/lib/debug/"; + char *workbuf = (char *) alloca (sizeof (usrlibdebug) + + (cp - realname) + + strlen (debuginfo_fname)); + strcpy (stpcpy (workbuf, realname), debuginfo_fname); + + int fd2 = open (workbuf, O_RDONLY); + if (fd2 == -1) + { + strcpy (stpcpy (stpcpy (workbuf, realname), ".debug/"), + debuginfo_fname); + fd2 = open (workbuf, O_RDONLY); + if (fd2 == -1) + { + strcpy (stpcpy (stpcpy (workbuf, usrlibdebug), realname), + debuginfo_fname); + fd2 = open (workbuf, O_RDONLY); + } + } + + if (fd2 != -1) + { + ElfW(Ehdr) ehdr2; + + /* Read the ELF header. */ + if (pread (fd2, &ehdr2, sizeof (ehdr2), 0) != sizeof (ehdr2)) + error (EXIT_FAILURE, errno, + _("reading of ELF header failed")); + + /* Map the section header. */ + size_t size = ehdr2.e_shnum * sizeof (ElfW(Shdr)); + ElfW(Shdr) *shdr2 = (ElfW(Shdr) *) alloca (size); + if (pread (fd2, shdr2, size, ehdr2.e_shoff) != size) + error (EXIT_FAILURE, errno, + _("reading of section headers failed")); + + /* Get the section header string table. */ + shstrtab = (char *) alloca (shdr2[ehdr2.e_shstrndx].sh_size); + if (pread (fd2, shstrtab, shdr2[ehdr2.e_shstrndx].sh_size, + shdr2[ehdr2.e_shstrndx].sh_offset) + != shdr2[ehdr2.e_shstrndx].sh_size) + error (EXIT_FAILURE, errno, + _("reading of section header string table failed")); + + /* Search for the ".symtab" section. */ + for (int idx = 0; idx < ehdr2.e_shnum; ++idx) + if (shdr2[idx].sh_type == SHT_SYMTAB + && strcmp (shstrtab + shdr2[idx].sh_name, ".symtab") == 0) + { + symtab_entry = &shdr2[idx]; + shdr = shdr2; + symfd = fd2; + break; + } + + if (fd2 != symfd) + close (fd2); + } + } + + no_debuginfo: if (symtab_entry == NULL) { fprintf (stderr, _("\ @@ -591,9 +682,9 @@ load_shobj (const char *name) } result->symbol_map = mmap (NULL, max_offset - min_offset, - PROT_READ, MAP_SHARED|MAP_FILE, fd, + PROT_READ, MAP_SHARED|MAP_FILE, symfd, min_offset); - if (result->symbol_map == NULL) + if (result->symbol_map == MAP_FAILED) error (EXIT_FAILURE, errno, _("failed to load symbol data")); result->symtab @@ -605,13 +696,10 @@ load_shobj (const char *name) result->symbol_mapsize = max_offset - min_offset; } - /* Now we also don't need the section header table anymore. */ - munmap ((char *) shdr - (ehdr->e_shoff & (pagesize - 1)), - (ehdr->e_phnum * sizeof (ElfW(Shdr)) - + (ehdr->e_shoff & (pagesize - 1)))); - /* Free the descriptor for the shared object. */ close (fd); + if (symfd != fd) + close (symfd); return result; } |