1
0
Fork 0
mirror of https://github.com/tobast/libunwind-eh_elf.git synced 2024-11-14 04:18:11 +01:00

elf: Follow .gnu_debuglink when resolving function names

Centralize gnu_debuglink logic in elfxx.  Remove previous duplicated logic
from Gfind_proc_info and os-linux.

Logic is roughly the same as previous load_debug_frame, but uses VLAs
instead of malloc.
This commit is contained in:
Dave Watson 2017-02-15 16:31:04 -08:00
parent f3f456c0d1
commit 54a970079d
4 changed files with 119 additions and 139 deletions

View file

@ -86,151 +86,42 @@ linear_search (unw_addr_space_t as, unw_word_t ip,
/* Load .debug_frame section from FILE. Allocates and returns space
in *BUF, and sets *BUFSIZE to its size. IS_LOCAL is 1 if using the
local process, in which case we can search the system debug file
directory; 0 for other address spaces, in which case we do not; or
-1 for recursive calls following .gnu_debuglink. Returns 0 on
success, 1 on error. Succeeds even if the file contains no
.debug_frame. */
directory; 0 for other address spaces, in which case we do
not. Returns 0 on success, 1 on error. Succeeds even if the file
contains no .debug_frame. */
/* XXX: Could use mmap; but elf_map_image keeps tons mapped in. */
static int
load_debug_frame (const char *file, char **buf, size_t *bufsize, int is_local)
{
FILE *f;
Elf_W (Ehdr) ehdr;
Elf_W (Half) shstrndx;
Elf_W (Shdr) *sec_hdrs = NULL;
char *stringtab = NULL;
unsigned int i;
size_t linksize = 0;
char *linkbuf = NULL;
*buf = NULL;
*bufsize = 0;
f = fopen (file, "r");
if (!f)
return 1;
if (fread (&ehdr, sizeof (Elf_W (Ehdr)), 1, f) != 1)
goto file_error;
shstrndx = ehdr.e_shstrndx;
Debug (4, "opened file '%s'. Section header at offset %d\n",
file, (int) ehdr.e_shoff);
struct elf_image ei;
Elf_W (Shdr) *shdr;
int ret;
fseek (f, ehdr.e_shoff, SEEK_SET);
sec_hdrs = calloc (ehdr.e_shnum, sizeof (Elf_W (Shdr)));
if (fread (sec_hdrs, sizeof (Elf_W (Shdr)), ehdr.e_shnum, f) != ehdr.e_shnum)
goto file_error;
Debug (4, "loading string table of size %zd\n",
sec_hdrs[shstrndx].sh_size);
stringtab = malloc (sec_hdrs[shstrndx].sh_size);
fseek (f, sec_hdrs[shstrndx].sh_offset, SEEK_SET);
if (fread (stringtab, 1, sec_hdrs[shstrndx].sh_size, f) != sec_hdrs[shstrndx].sh_size)
goto file_error;
for (i = 1; i < ehdr.e_shnum && *buf == NULL; i++)
ei.image = NULL;
ret = elf_w (load_debuglink) (file, &ei, is_local);
if (ret != 0)
return ret;
shdr = elf_w (find_section) (&ei, ".debug_frame");
if (!shdr ||
(shdr->sh_offset + shdr->sh_size > ei.size))
{
char *secname = &stringtab[sec_hdrs[i].sh_name];
if (strcmp (secname, ".debug_frame") == 0)
{
*bufsize = sec_hdrs[i].sh_size;
*buf = malloc (*bufsize);
fseek (f, sec_hdrs[i].sh_offset, SEEK_SET);
if (fread (*buf, 1, *bufsize, f) != *bufsize)
goto file_error;
Debug (4, "read %zd bytes of .debug_frame from offset %zd\n",
*bufsize, sec_hdrs[i].sh_offset);
}
else if (strcmp (secname, ".gnu_debuglink") == 0)
{
linksize = sec_hdrs[i].sh_size;
linkbuf = malloc (linksize);
fseek (f, sec_hdrs[i].sh_offset, SEEK_SET);
if (fread (linkbuf, 1, linksize, f) != linksize)
goto file_error;
Debug (4, "read %zd bytes of .gnu_debuglink from offset %zd\n",
linksize, sec_hdrs[i].sh_offset);
}
}
free (stringtab);
free (sec_hdrs);
fclose (f);
/* Ignore separate debug files which contain a .gnu_debuglink section. */
if (linkbuf && is_local == -1)
{
free (linkbuf);
munmap(ei.image, ei.size);
return 1;
}
if (*buf == NULL && linkbuf != NULL && memchr (linkbuf, 0, linksize) != NULL)
{
char *newname, *basedir, *p;
static const char *debugdir = "/usr/lib/debug";
int ret;
*bufsize = shdr->sh_size;
*buf = malloc (*bufsize);
/* XXX: Don't bother with the checksum; just search for the file. */
basedir = malloc (strlen (file) + 1);
newname = malloc (strlen (linkbuf) + strlen (debugdir)
+ strlen (file) + 9);
memcpy(*buf, shdr->sh_offset + ei.image, *bufsize);
p = strrchr (file, '/');
if (p != NULL)
{
memcpy (basedir, file, p - file);
basedir[p - file] = '\0';
}
else
basedir[0] = 0;
strcpy (newname, basedir);
strcat (newname, "/");
strcat (newname, linkbuf);
ret = load_debug_frame (newname, buf, bufsize, -1);
if (ret == 1)
{
strcpy (newname, basedir);
strcat (newname, "/.debug/");
strcat (newname, linkbuf);
ret = load_debug_frame (newname, buf, bufsize, -1);
}
if (ret == 1 && is_local == 1)
{
strcpy (newname, debugdir);
strcat (newname, basedir);
strcat (newname, "/");
strcat (newname, linkbuf);
ret = load_debug_frame (newname, buf, bufsize, -1);
}
free (basedir);
free (newname);
}
free (linkbuf);
Debug (4, "read %zd bytes of .debug_frame from offset %zd\n",
*bufsize, shdr->sh_offset);
munmap(ei.image, ei.size);
return 0;
/* An error reading image file. Release resources and return error code */
file_error:
free(stringtab);
free(sec_hdrs);
free(linkbuf);
fclose(f);
return 1;
}
/* Locate the binary which originated the contents of address ADDR. Return

View file

@ -312,8 +312,13 @@ elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
unsigned long segbase, mapoff;
struct elf_image ei;
int ret;
char file[PATH_MAX];
ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, NULL, 0);
ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, file, PATH_MAX);
if (ret < 0)
return ret;
ret = elf_w (load_debuglink) (file, &ei, 1);
if (ret < 0)
return ret;
@ -368,3 +373,92 @@ elf_w (find_section) (struct elf_image *ei, const char* secname)
/* section not found */
return 0;
}
/* Load a debug section, following .gnu_debuglink if appropriate
* Loads ei from file if not already mapped.
* If is_local, will also search sys directories /usr/local/dbg
*
* Returns 0 on success, failure otherwise.
* ei will be mapped to file or the located .gnu_debuglink from file
*/
HIDDEN int
elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local)
{
int ret;
Elf_W (Shdr) *shdr;
if (!ei->image)
{
ret = elf_map_image(ei, file);
if (ret)
return ret;
}
/* Ignore separate debug files which contain a .gnu_debuglink section. */
if (is_local == -1) {
return 0;
}
shdr = elf_w (find_section) (ei, ".gnu_debuglink");
if (shdr) {
if (shdr->sh_size >= PATH_MAX ||
(shdr->sh_offset + shdr->sh_size > ei->size))
{
return 0;
}
{
char linkbuf[shdr->sh_size];
char *link = ((char *) ei->image) + shdr->sh_offset;
char *p;
static const char *debugdir = "/usr/lib/debug";
char basedir[strlen(file) + 1];
char newname[shdr->sh_size + strlen (debugdir) + strlen (file) + 9];
memcpy(linkbuf, link, shdr->sh_size);
if (memchr (linkbuf, 0, shdr->sh_size) == NULL)
return 0;
munmap (ei->image, ei->size);
ei->image = NULL;
Debug(1, "Found debuglink section, following %s\n", linkbuf);
p = strrchr (file, '/');
if (p != NULL)
{
memcpy (basedir, file, p - file);
basedir[p - file] = '\0';
}
else
basedir[0] = 0;
strcpy (newname, basedir);
strcat (newname, "/");
strcat (newname, linkbuf);
ret = elf_w (load_debuglink) (newname, ei, -1);
if (ret == -1)
{
strcpy (newname, basedir);
strcat (newname, "/.debug/");
strcat (newname, linkbuf);
ret = elf_w (load_debuglink) (newname, ei, -1);
}
if (ret == -1 && is_local == 1)
{
strcpy (newname, debugdir);
strcat (newname, basedir);
strcat (newname, "/");
strcat (newname, linkbuf);
ret = elf_w (load_debuglink) (newname, ei, -1);
}
return ret;
}
}
return 0;
}

View file

@ -55,6 +55,7 @@ extern int elf_w (get_proc_name_in_image) (unw_addr_space_t as,
char *buf, size_t buf_len, unw_word_t *offp);
extern Elf_W (Shdr)* elf_w (find_section) (struct elf_image *ei, const char* secname);
extern int elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local);
static inline int
elf_w (valid_object) (struct elf_image *ei)

View file

@ -37,7 +37,6 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip,
struct map_iterator mi;
int found = 0, rc;
unsigned long hi;
char debug_path[PATH_MAX];
if (maps_init (&mi, pid) < 0)
return -1;
@ -58,12 +57,7 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip,
{
strncpy(path, mi.path, pathlen);
}
snprintf (debug_path, sizeof (debug_path), "/usr/lib/debug%s", mi.path);
rc = elf_map_image (ei, debug_path);
if (rc != 0)
{
rc = elf_map_image (ei, mi.path);
}
rc = elf_map_image (ei, mi.path);
maps_close (&mi);
return rc;
}