mirror of
git://sourceware.org/git/glibc.git
synced 2024-11-27 03:41:23 +08:00
(load_shobj): Add support for reading symbol table from debuginfo file.
This commit is contained in:
parent
31ff71d83f
commit
fcf5e9986d
176
elf/sprof.c
176
elf/sprof.c
@ -48,14 +48,18 @@
|
|||||||
|
|
||||||
#include <endian.h>
|
#include <endian.h>
|
||||||
#if BYTE_ORDER == BIG_ENDIAN
|
#if BYTE_ORDER == BIG_ENDIAN
|
||||||
#define byteorder ELFDATA2MSB
|
# define byteorder ELFDATA2MSB
|
||||||
#define byteorder_name "big-endian"
|
# define byteorder_name "big-endian"
|
||||||
#elif BYTE_ORDER == LITTLE_ENDIAN
|
#elif BYTE_ORDER == LITTLE_ENDIAN
|
||||||
#define byteorder ELFDATA2LSB
|
# define byteorder ELFDATA2LSB
|
||||||
#define byteorder_name "little-endian"
|
# define byteorder_name "little-endian"
|
||||||
#else
|
#else
|
||||||
#error "Unknown BYTE_ORDER " BYTE_ORDER
|
# error "Unknown BYTE_ORDER " BYTE_ORDER
|
||||||
#define byteorder ELFDATANONE
|
# define byteorder ELFDATANONE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
# define PATH_MAX 1024
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -82,7 +86,9 @@ static const struct argp_option options[] =
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Short description of program. */
|
/* 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. */
|
/* Strings for arguments in help texts. */
|
||||||
static const char args_doc[] = N_("SHOBJ [PROFDATA]");
|
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. */
|
/* Data structure to communicate with argp functions. */
|
||||||
static struct argp argp =
|
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
|
DEFAULT_MODE = FLAT_MODE | CALL_GRAPH_MODE
|
||||||
} mode;
|
} mode;
|
||||||
|
|
||||||
/* If nonzero the total number of invocations of a function is emitted. */
|
|
||||||
int count_total;
|
|
||||||
|
|
||||||
/* Nozero for testing. */
|
/* Nozero for testing. */
|
||||||
int do_test;
|
static int do_test;
|
||||||
|
|
||||||
/* Strcuture describing calls. */
|
/* Strcuture describing calls. */
|
||||||
struct here_fromstruct
|
struct here_fromstruct
|
||||||
@ -204,7 +207,7 @@ struct profdata
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Search tree for symbols. */
|
/* Search tree for symbols. */
|
||||||
void *symroot;
|
static void *symroot;
|
||||||
static struct known_symbol **sortsym;
|
static struct known_symbol **sortsym;
|
||||||
static size_t symidx;
|
static size_t symidx;
|
||||||
static uintmax_t total_ticks;
|
static uintmax_t total_ticks;
|
||||||
@ -374,11 +377,7 @@ load_shobj (const char *name)
|
|||||||
ElfW(Ehdr) *ehdr;
|
ElfW(Ehdr) *ehdr;
|
||||||
int fd;
|
int fd;
|
||||||
ElfW(Shdr) *shdr;
|
ElfW(Shdr) *shdr;
|
||||||
void *ptr;
|
|
||||||
size_t pagesize = getpagesize ();
|
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
|
/* 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
|
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"),
|
error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"),
|
||||||
map->l_name);
|
map->l_name);
|
||||||
|
|
||||||
/* Now map the section header. */
|
/* Map the section header. */
|
||||||
ptr = mmap (NULL, (ehdr->e_shnum * sizeof (ElfW(Shdr))
|
size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr));
|
||||||
+ (ehdr->e_shoff & (pagesize - 1))), PROT_READ,
|
shdr = (ElfW(Shdr) *) alloca (size);
|
||||||
MAP_SHARED|MAP_FILE, fd, ehdr->e_shoff & ~(pagesize - 1));
|
if (pread (fd, shdr, size, ehdr->e_shoff) != size)
|
||||||
if (ptr == MAP_FAILED)
|
error (EXIT_FAILURE, errno, _("reading of section headers failed"));
|
||||||
error (EXIT_FAILURE, errno, _("mapping of section headers failed"));
|
|
||||||
shdr = (ElfW(Shdr) *) ((char *) ptr + (ehdr->e_shoff & (pagesize - 1)));
|
|
||||||
|
|
||||||
/* Get the section header string table. */
|
/* Get the section header string table. */
|
||||||
ptr = mmap (NULL, (shdr[ehdr->e_shstrndx].sh_size
|
char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size);
|
||||||
+ (shdr[ehdr->e_shstrndx].sh_offset & (pagesize - 1))),
|
if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size,
|
||||||
PROT_READ, MAP_SHARED|MAP_FILE, fd,
|
shdr[ehdr->e_shstrndx].sh_offset)
|
||||||
shdr[ehdr->e_shstrndx].sh_offset & ~(pagesize - 1));
|
!= shdr[ehdr->e_shstrndx].sh_size)
|
||||||
if (ptr == MAP_FAILED)
|
|
||||||
error (EXIT_FAILURE, errno,
|
error (EXIT_FAILURE, errno,
|
||||||
_("mapping of section header string table failed"));
|
_("reading of section header string table failed"));
|
||||||
shstrtab = ((const char *) ptr
|
|
||||||
+ (shdr[ehdr->e_shstrndx].sh_offset & (pagesize - 1)));
|
|
||||||
|
|
||||||
/* Search for the ".symtab" section. */
|
/* Search for the ".symtab" section. */
|
||||||
symtab_entry = NULL;
|
ElfW(Shdr) *symtab_entry = NULL;
|
||||||
for (idx = 0; idx < ehdr->e_shnum; ++idx)
|
ElfW(Shdr) *debuglink_entry = NULL;
|
||||||
|
for (int idx = 0; idx < ehdr->e_shnum; ++idx)
|
||||||
if (shdr[idx].sh_type == SHT_SYMTAB
|
if (shdr[idx].sh_type == SHT_SYMTAB
|
||||||
&& strcmp (shstrtab + shdr[idx].sh_name, ".symtab") == 0)
|
&& strcmp (shstrtab + shdr[idx].sh_name, ".symtab") == 0)
|
||||||
{
|
{
|
||||||
symtab_entry = &shdr[idx];
|
symtab_entry = &shdr[idx];
|
||||||
break;
|
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. */
|
/* Get the file name of the debuginfo file if necessary. */
|
||||||
munmap (ptr, (shdr[ehdr->e_shstrndx].sh_size
|
int symfd = fd;
|
||||||
+ (shdr[ehdr->e_shstrndx].sh_offset & (pagesize - 1))));
|
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)
|
if (symtab_entry == NULL)
|
||||||
{
|
{
|
||||||
fprintf (stderr, _("\
|
fprintf (stderr, _("\
|
||||||
@ -591,9 +682,9 @@ load_shobj (const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
result->symbol_map = mmap (NULL, max_offset - min_offset,
|
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);
|
min_offset);
|
||||||
if (result->symbol_map == NULL)
|
if (result->symbol_map == MAP_FAILED)
|
||||||
error (EXIT_FAILURE, errno, _("failed to load symbol data"));
|
error (EXIT_FAILURE, errno, _("failed to load symbol data"));
|
||||||
|
|
||||||
result->symtab
|
result->symtab
|
||||||
@ -605,13 +696,10 @@ load_shobj (const char *name)
|
|||||||
result->symbol_mapsize = max_offset - min_offset;
|
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. */
|
/* Free the descriptor for the shared object. */
|
||||||
close (fd);
|
close (fd);
|
||||||
|
if (symfd != fd)
|
||||||
|
close (symfd);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user