From: Roland McGrath Date: Sun, 20 Jun 2010 23:36:00 +0000 (-0700) Subject: Direct relocation handling in libdw reader. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0a9677ef82b285995eb9dd97fd06c7450a7403d0;p=thirdparty%2Felfutils.git Direct relocation handling in libdw reader. --- diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 021996d94..b6fe88502 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,5 +1,37 @@ +2010-06-21 Roland McGrath + + * libdwP.h (struct Dwarf_Line_s): Replace files with cu. + (struct Dwarf_Lines_s): New member reloc. + * dwarf_linesrc.c: Update usage. + * dwarf_getsrc_file.c: Likewise. + * dwarf_getsrclines.c: Record relocatable address locations in + parallel array, with Dwarf_Line.addr becoming a relative offset. + * dwarf_lineaddr.c: Use __libdw_read_address on demand for a + relocatable line address. + 2010-06-20 Roland McGrath + * relocate.c: New file. + * Makefile.am (libdw_a_SOURCES): Add it. + * libdwP.h (struct dwarf_file_reloc): New type. + (struct Dwarf): New member relocate. + (__libdw_relocate_address, __libdw_relocate_offset): Replace inlines + with extern decls. + (READ_AND_RELOCATE): Call RELOC_HOOK if DBG->relocate, + else read directly. + (__libdw_read_address, __libdw_read_offset): Just call *_inc variant. + (__libdw_relocate_begin, __libdw_relocate_end): Declare them. + * dwarf_begin_elf.c (check_section): Take new args to track reloc + sections. + (global_read, scngrp_read): Pass new args through to check_section. + (dwarf_begin_elf): Track reloc sections in an ET_REL file, + call __libdw_relocate_begin. + + * libdwP.h (DWARF_E_RELOC, DWARF_E_RELBADTYPE): New enum constants. + (DWARF_E_RELBADADDEND, DWARF_E_RELBADOFF): Likewise. + (DWARF_E_RELBADSYM, DWARF_E_RELUNDEF, DWARF_E_RELWRONGSEC): Likewise. + * dwarf_error.c (errmsgs): Add them. + * cfi.h (BYTE_ORDER_DUMMY): Macro moved ... * memory-access.h: ... here. @@ -7,6 +39,7 @@ to search TUs instead of CUs. * libdwP.h: Update decl. (struct Dwarf): New member tu_tree. + * dwarf_end.c (dwarf_end): Clean up tu_tree. * dwarf_offdie.c (do_offdie): New function, broken out of ... (dwarf_offdie): ... here. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index 530cbf4b2..6b0c0734b 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -84,7 +84,8 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_frame_info.c dwarf_frame_cfa.c dwarf_frame_register.c \ dwarf_cfi_addrframe.c \ dwarf_getcfi.c dwarf_getcfi_elf.c dwarf_cfi_end.c \ - dwarf_aggregate_size.c + dwarf_aggregate_size.c \ + relocate.c if MAINTAINER_MODE BUILT_SOURCES = $(srcdir)/known-dwarf.h diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index b5fb7c91f..c8efc1714 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -52,6 +52,7 @@ # include #endif +#include #include #include #include @@ -81,7 +82,9 @@ static const char dwarf_scnnames[IDX_last][17] = static Dwarf * -check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) +check_section (Dwarf *result, GElf_Ehdr *ehdr, + uint8_t scn_debug[], size_t shnum, Elf_Scn *relscn[IDX_last], + Elf_Scn *scn, bool inscngrp) { GElf_Shdr shdr_mem; GElf_Shdr *shdr; @@ -123,9 +126,40 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) } + /* This might be a relocation section. */ + if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + { + if (scn_debug != NULL + && likely (shdr->sh_info != 0) + && likely (shdr->sh_info < shnum)) + { + if (scn_debug[shdr->sh_info] < IDX_last) + { + /* This section has relocations for a debug section + we have already noticed. */ + relscn[scn_debug[shdr->sh_info]] = scn; + if (scn_debug[0] != IDX_debug_info) + scn_debug[0] = IDX_debug_abbrev; + } + else if (unlikely (shdr->sh_info > elf_ndxscn (scn))) + { + /* A relocation section usually follows its target section. + But there is no guarantee. */ + GElf_Shdr tshdr_mem; + GElf_Shdr *tshdr = gelf_getshdr (elf_getscn (result->elf, + shdr->sh_info), + &tshdr_mem); + if (likely (tshdr != NULL)) + /* Mark that we need a second pass. */ + scn_debug[0] = IDX_debug_info; + } + } + return result; + } + + /* Recognize the various sections. Most names start with .debug_. */ - size_t cnt; - for (cnt = 0; cnt < ndwarf_scnnames; ++cnt) + for (uint_fast8_t cnt = 0; cnt < ndwarf_scnnames; ++cnt) if (strcmp (scnname, dwarf_scnnames[cnt]) == 0) { /* Found it. Remember where the data is. */ @@ -136,8 +170,12 @@ check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp) /* Get the section data. */ Elf_Data *data = elf_getdata (scn, NULL); if (data != NULL && data->d_size != 0) - /* Yep, there is actually data available. */ - result->sectiondata[cnt] = data; + { + /* Yep, there is actually data available. */ + result->sectiondata[cnt] = data; + if (scn_debug != NULL) + scn_debug[elf_ndxscn (scn)] = cnt; + } break; } @@ -169,19 +207,22 @@ valid_p (Dwarf *result) static Dwarf * -global_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr) +global_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr, + uint8_t scn_debug[], size_t shnum, Elf_Scn *relscn[IDX_last]) { Elf_Scn *scn = NULL; while (result != NULL && (scn = elf_nextscn (elf, scn)) != NULL) - result = check_section (result, ehdr, scn, false); + result = check_section (result, ehdr, scn_debug, shnum, relscn, scn, false); return valid_p (result); } static Dwarf * -scngrp_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr, Elf_Scn *scngrp) +scngrp_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr, + uint8_t scn_debug[], size_t shnum, Elf_Scn *relscn[IDX_last], + Elf_Scn *scngrp) { /* SCNGRP is the section descriptor for a section group which might contain debug sections. */ @@ -209,7 +250,8 @@ scngrp_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr, Elf_Scn *scngrp) return NULL; } - result = check_section (result, ehdr, scn, true); + result = check_section (result, ehdr, scn_debug, shnum, relscn, + scn, true); if (result == NULL) break; } @@ -272,15 +314,38 @@ dwarf_begin_elf (elf, cmd, scngrp) if (cmd == DWARF_C_READ || cmd == DWARF_C_RDWR) { - /* If the caller provides a section group we get the DWARF - sections only from this setion group. Otherwise we search - for the first section with the required name. Further - sections with the name are ignored. The DWARF specification - does not really say this is allowed. */ - if (scngrp == NULL) - return global_read (result, elf, ehdr); + inline void read_it (uint8_t scn_debug[], size_t shnum, + Elf_Scn *relscn[IDX_last]) + { + /* If the caller provides a section group we get the DWARF + sections only from this setion group. Otherwise we search + for the first section with the required name. Further + sections with the name are ignored. The DWARF specification + does not really say this is allowed. */ + if (scngrp == NULL) + result = global_read (result, elf, ehdr, scn_debug, shnum, relscn); + else + result = scngrp_read (result, elf, ehdr, scn_debug, shnum, relscn, + scngrp); + } + + if (ehdr->e_type == ET_REL) + { + Elf_Scn *relscn[IDX_last] = {}; + size_t shnum; + int ret = elf_getshdrnum (elf, &shnum); + assert (ret == 0); + uint8_t scn_debug[shnum]; + memset (scn_debug, IDX_last, shnum); + read_it (scn_debug, shnum, relscn); + if (result != NULL && scn_debug[0] != IDX_last) + /* We saw some relocation sections. */ + __libdw_relocate_begin (result, relscn, + scn_debug[0] == IDX_debug_info); + } else - return scngrp_read (result, elf, ehdr, scngrp); + read_it (NULL, 0, NULL); + return result; } else if (cmd == DWARF_C_WRITE) { diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index ec10542e5..94b9b8760 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -94,6 +94,10 @@ dwarf_end (dwarf) tdestroy (dwarf->cu_tree, cu_free); tdestroy (dwarf->tu_tree, cu_free); + if (dwarf->relocate != NULL) + /* Clean up relocation tracking. */ + __libdw_relocate_end (dwarf); + struct libdw_memblock *memp = dwarf->mem_tail; /* The first block is allocated together with the Dwarf object. */ while (memp->prev != NULL) diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c index 0d95b8d20..503c9c4c1 100644 --- a/libdw/dwarf_error.c +++ b/libdw/dwarf_error.c @@ -1,5 +1,5 @@ /* Retrieve ELF descriptor used for DWARF access. - Copyright (C) 2002, 2003, 2004, 2005, 2009 Red Hat, Inc. + Copyright (C) 2002-2010 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper , 2002. @@ -112,6 +112,13 @@ static const char *errmsgs[] = [DWARF_E_INVALID_OFFSET] = N_("invalid offset"), [DWARF_E_NO_DEBUG_RANGES] = N_(".debug_ranges section missing"), [DWARF_E_INVALID_CFI] = N_("invalid CFI section"), + [DWARF_E_RELOC] = N_("value requires relocation"), + [DWARF_E_RELBADTYPE] = N_("unsupported relocation type"), + [DWARF_E_RELBADADDEND] = N_("relocation addend overflow"), + [DWARF_E_RELBADOFF] = N_("relocation at invalid offset"), + [DWARF_E_RELBADSYM] = N_("relocation refers to invalid symbol"), + [DWARF_E_RELUNDEF] = N_("relocation refers to undefined symbol"), + [DWARF_E_RELWRONGSEC] = N_("relocation refers to wrong DWARF section"), }; #define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0])) diff --git a/libdw/dwarf_getsrc_file.c b/libdw/dwarf_getsrc_file.c index bc612f6c6..57a5382ab 100644 --- a/libdw/dwarf_getsrc_file.c +++ b/libdw/dwarf_getsrc_file.c @@ -1,5 +1,5 @@ /* Find line information for given file/line/column triple. - Copyright (C) 2005-2009 Red Hat, Inc. + Copyright (C) 2005-2010 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper , 2005. @@ -110,14 +110,14 @@ dwarf_getsrc_file (Dwarf *dbg, const char *fname, int lineno, int column, if (lastfile != line->file) { lastfile = line->file; - if (lastfile >= line->files->nfiles) + if (lastfile >= line->cu->files->nfiles) { __libdw_seterrno (DWARF_E_INVALID_DWARF); return -1; } /* Match the name with the name the user provided. */ - const char *fname2 = line->files->info[lastfile].name; + const char *fname2 = line->cu->files->info[lastfile].name; if (is_basename) lastmatch = strcmp (basename (fname2), fname) == 0; else @@ -136,7 +136,7 @@ dwarf_getsrc_file (Dwarf *dbg, const char *fname, int lineno, int column, /* Determine whether this is the best match so far. */ size_t inner; for (inner = 0; inner < cur_match; ++inner) - if (match[inner]->files == line->files + if (match[inner]->cu->files == line->cu->files && match[inner]->file == line->file) break; if (inner < cur_match diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c index afdf9dba7..bdff80de5 100644 --- a/libdw/dwarf_getsrclines.c +++ b/libdw/dwarf_getsrclines.c @@ -68,6 +68,7 @@ struct filelist struct linelist { Dwarf_Line line; + const unsigned char *reloc; struct linelist *next; }; @@ -323,6 +324,7 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines) /* We are about to process the statement program. Initialize the state machine registers (see 6.2.2 in the v2.1 specification). */ + const unsigned char *addr_reloc = NULL; Dwarf_Word addr = 0; unsigned int op_index = 0; unsigned int file = 1; @@ -385,6 +387,7 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines) #undef SET + new_line->reloc = addr_reloc; new_line->next = linelist; linelist = new_line; ++nlinelist; @@ -450,6 +453,7 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines) /* Reset the registers. */ addr = 0; + addr_reloc = NULL; op_index = 0; file = 1; line = 1; @@ -469,8 +473,19 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines) op_index = 0; if (unlikely (lineendp - linep < cu->address_size)) goto invalid_data; - if (__libdw_read_address_inc (dbg, IDX_debug_line, &linep, - cu->address_size, &addr)) + if (dbg->relocate != NULL + && dbg->relocate->sectionrel[IDX_debug_line] != NULL) + { + /* We're just recording a relocatable address + and offsets from it. We'll do the relocation + only lazily in dwarf_lineaddr. */ + addr = 0; + addr_reloc = linep; + linep += cu->address_size; + } + else if (__libdw_read_address_inc (dbg, IDX_debug_line, + &linep, cu->address_size, + &addr)) goto out; break; @@ -703,9 +718,9 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines) We'll write the pointers in the end of the buffer, and then copy into the buffer from the beginning so the overlap works. */ assert (sizeof (Dwarf_Line) >= sizeof (Dwarf_Line *)); - Dwarf_Line **sortlines = (buf + sizeof (Dwarf_Lines) - + ((sizeof (Dwarf_Line) - - sizeof (Dwarf_Line *)) * nlinelist)); + struct linelist **sortlines = (buf + sizeof (Dwarf_Lines) + + ((sizeof (Dwarf_Line) + - sizeof (Dwarf_Line *)) * nlinelist)); /* The list is in LIFO order and usually they come in clumps with ascending addresses. So fill from the back to probably start with @@ -713,7 +728,7 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines) unsigned int i = nlinelist; while (i-- > 0) { - sortlines[i] = &linelist->line; + sortlines[i] = linelist; linelist = linelist->next; } assert (linelist == NULL); @@ -726,10 +741,24 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines) of SORTLINES by the time we're reading the later ones. */ cu->lines = buf; cu->lines->nlines = nlinelist; + + if (cu->dbg->relocate != NULL + && cu->dbg->relocate->sectionrel[IDX_debug_line] != NULL) + { + /* Add a parallel table of relocation pointers. */ + cu->lines->reloc = libdw_alloc (cu->dbg, const unsigned char *, + sizeof (const unsigned char *), + nlinelist); + for (i = 0; i < nlinelist; ++i) + cu->lines->reloc[i] = sortlines[i]->reloc; + } + else + cu->lines->reloc = NULL; + for (i = 0; i < nlinelist; ++i) { - cu->lines->info[i] = *sortlines[i]; - cu->lines->info[i].files = files; + cu->lines->info[i] = sortlines[i]->line; + cu->lines->info[i].cu = cu; } /* Success. */ diff --git a/libdw/dwarf_lineaddr.c b/libdw/dwarf_lineaddr.c index c59dd5d59..7bfefd6cc 100644 --- a/libdw/dwarf_lineaddr.c +++ b/libdw/dwarf_lineaddr.c @@ -1,5 +1,5 @@ /* Return line address. - Copyright (C) 2004 Red Hat, Inc. + Copyright (C) 2004-2010 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper , 2004. @@ -61,7 +61,22 @@ dwarf_lineaddr (Dwarf_Line *line, Dwarf_Addr *addrp) if (line == NULL) return -1; - *addrp = line->addr; + if (line->cu->lines->reloc == NULL + || line->cu->lines->reloc[line - line->cu->lines->info] == NULL) + { + *addrp = line->addr; + return 0; + } - return 0; + /* We have to relocate an address and then adjust it for this line record. */ + + int result = __libdw_read_address + (line->cu->dbg, IDX_debug_line, + line->cu->lines->reloc[line - line->cu->lines->info], + line->cu->address_size, addrp); + + if (result >= 0) + *addrp += line->addr; + + return result; } diff --git a/libdw/dwarf_linesrc.c b/libdw/dwarf_linesrc.c index 6ecce93fe..ad3a83bf0 100644 --- a/libdw/dwarf_linesrc.c +++ b/libdw/dwarf_linesrc.c @@ -1,5 +1,5 @@ /* Find line information for address. - Copyright (C) 2004 Red Hat, Inc. + Copyright (C) 2004-2010 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper , 2004. @@ -61,17 +61,17 @@ dwarf_linesrc (Dwarf_Line *line, Dwarf_Word *mtime, Dwarf_Word *length) if (line == NULL) return NULL; - if (line->file >= line->files->nfiles) + if (line->file >= line->cu->files->nfiles) { __libdw_seterrno (DWARF_E_INVALID_DWARF); return NULL; } if (mtime != NULL) - *mtime = line->files->info[line->file].mtime; + *mtime = line->cu->files->info[line->file].mtime; if (length != NULL) - *length = line->files->info[line->file].length; + *length = line->cu->files->info[line->file].length; - return line->files->info[line->file].name; + return line->cu->files->info[line->file].name; } diff --git a/libdw/libdwP.h b/libdw/libdwP.h index bb0ae1d11..6b22d6ee7 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -136,6 +136,13 @@ enum DWARF_E_INVALID_OFFSET, DWARF_E_NO_DEBUG_RANGES, DWARF_E_INVALID_CFI, + DWARF_E_RELOC, + DWARF_E_RELBADTYPE, + DWARF_E_RELBADADDEND, + DWARF_E_RELBADOFF, + DWARF_E_RELBADSYM, + DWARF_E_RELUNDEF, + DWARF_E_RELWRONGSEC, }; @@ -150,6 +157,9 @@ struct Dwarf /* The section data. */ Elf_Data *sectiondata[IDX_last]; + /* Information for relocating an ET_REL file, or null. */ + struct dwarf_file_reloc *relocate; + /* True if the file has a byte order different from the host. */ bool other_byte_order; @@ -201,6 +211,19 @@ struct Dwarf }; +/* Hook for relocation information (handled in relocate.c). */ +struct dwarf_file_reloc +{ + struct dwarf_section_reloc *sectionrel[IDX_last]; + + struct ebl *ebl; + + int (*resolve_symbol) (bool undef, Dwarf *dbg, + GElf_Sym *sym, GElf_Word shndx) + internal_function; +}; + + /* Abbreviation representation. */ struct Dwarf_Abbrev { @@ -236,7 +259,7 @@ typedef struct Dwarf_Fileinfo_s Dwarf_Fileinfo; struct Dwarf_Line_s { - Dwarf_Files *files; + struct Dwarf_CU *cu; Dwarf_Addr addr; unsigned int file; @@ -257,6 +280,7 @@ struct Dwarf_Line_s struct Dwarf_Lines_s { + const unsigned char **reloc; size_t nlines; struct Dwarf_Line_s info[0]; }; @@ -464,29 +488,27 @@ extern int __dwarf_errno_internal (void); /* Reader hooks. */ +extern void __libdw_relocate_begin (Dwarf *dbg, Elf_Scn *relscn[IDX_last], + bool incomplete) + __nonnull_attribute__ (1, 2) internal_function; + +extern void __libdw_relocate_end (Dwarf *dbg) + __nonnull_attribute__ (1) internal_function; + + /* Relocation hooks return -1 on error (in that case the error code must already have been set), 0 if there is no relocation and 1 if a - relocation was present.*/ + relocation was present. */ -static inline int -__libdw_relocate_address (Dwarf *dbg __attribute__ ((unused)), - int sec_index __attribute__ ((unused)), - const void *addr __attribute__ ((unused)), - int width __attribute__ ((unused)), - Dwarf_Addr *val __attribute__ ((unused))) -{ - return 0; -} +extern int __libdw_relocate_address (Dwarf *dbg, int sec_index, + const void *addr, int width, + Dwarf_Addr *val) + __nonnull_attribute__ (1, 3, 5) internal_function; -static inline int -__libdw_relocate_offset (Dwarf *dbg __attribute__ ((unused)), - int sec_index __attribute__ ((unused)), - const void *addr __attribute__ ((unused)), - int width __attribute__ ((unused)), - Dwarf_Off *val __attribute__ ((unused))) -{ - return 0; -} +extern int __libdw_relocate_offset (Dwarf *dbg, int sec_index, + const void *addr, int width, + Dwarf_Off *val) + __nonnull_attribute__ (1, 3, 5) internal_function; static inline Elf_Data * __libdw_checked_get_data (Dwarf *dbg, int sec_index) @@ -540,15 +562,19 @@ __libdw_in_section (Dwarf *dbg, int sec_index, if (!__libdw_in_section (dbg, sec_index, addr, width)) \ return -1; \ \ - const unsigned char *orig_addr = addr; \ - if (width == 4) \ + int status = 0; \ + if (dbg->relocate != NULL) \ + { \ + status = RELOC_HOOK (dbg, sec_index, addr, width, &VAL); \ + if (status < 0) \ + return status; \ + addr += width; \ + } \ + else if (width == 4) \ VAL = read_4ubyte_unaligned_inc (dbg, addr); \ else \ VAL = read_8ubyte_unaligned_inc (dbg, addr); \ \ - int status = RELOC_HOOK (dbg, sec_index, orig_addr, width, &VAL); \ - if (status < 0) \ - return status; \ status > 0; \ }) @@ -568,8 +594,7 @@ __libdw_read_address (Dwarf *dbg, int sec_index, const unsigned char *addr, int width, Dwarf_Addr *ret) { - READ_AND_RELOCATE (__libdw_relocate_address, (*ret)); - return 0; + return __libdw_read_address_inc (dbg, sec_index, &addr, width, ret); } static inline int @@ -590,8 +615,8 @@ __libdw_read_offset (Dwarf *dbg, int width, Dwarf_Off *ret, int sec_ret, size_t size) { - READ_AND_RELOCATE (__libdw_relocate_offset, (*ret)); - return __libdw_offset_in_section (dbg, sec_ret, *ret, size); + return __libdw_read_offset_inc (dbg, sec_index, &addr, width, + ret, sec_ret, size); } static inline size_t diff --git a/libdw/relocate.c b/libdw/relocate.c new file mode 100644 index 000000000..a7d57a483 --- /dev/null +++ b/libdw/relocate.c @@ -0,0 +1,546 @@ +/* Relocation handling for DWARF reader. + Copyright (C) 2010 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils 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; version 2 of the License. + + Red Hat elfutils 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 Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "libdwP.h" +#include "../libelf/libelfP.h" +#include "../libebl/libebl.h" +#include +#include +#include + + +struct dwarf_reloc_table +{ + size_t n; /* Number of elements in this table. */ + const unsigned char **datum; /* Sorted pointers into section data. */ + int *symndx; /* Corresponding symtab indices. */ + size_t hint; /* Index of last search. */ +}; + +struct dwarf_section_reloc +{ + /* This is set only in the laziest state from startup: the reloc + section needs to be examined, and the rest is uninitialized. + When this is cleared, the rest is initialized and safe to use. */ + Elf_Scn *scn; + + Elf_Data *symdata; + Elf_Data *symstrdata; + Elf_Data *symxndxdata; + + /* Two tables of predigested relocations, segregated by size. */ + struct dwarf_reloc_table rel4; + struct dwarf_reloc_table rel8; + + /* For SHT_RELA, a parallel table of addends. + For SHT_REL, these are null. */ + Elf32_Sword *rela4; + Elf64_Sxword *rela8; +}; + +static int +internal_function +noresolve_symbol (bool undef, + Dwarf *dbg __attribute__ ((unused)), + GElf_Sym *sym __attribute__ ((unused)), + GElf_Word shndx __attribute__ ((unused))) +{ + __libdw_seterrno (undef ? DWARF_E_RELUNDEF : DWARF_E_RELOC); + return -1; +} + +void +internal_function +__libdw_relocate_begin (Dwarf *dbg, Elf_Scn *relscn[IDX_last], bool incomplete) +{ + if (incomplete) + { + /* We may need a second pass to identify some relocation sections. */ + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (dbg->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr; + shdr = gelf_getshdr (scn, &shdr_mem); + assert (shdr == &shdr_mem); + if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) + for (size_t i = 0; i < IDX_last; ++i) + if (relscn[i] == NULL && dbg->sectiondata[i] != NULL + && (((Elf_Data_Scn * ) dbg->sectiondata[i])->s->index + == shdr->sh_link)) + relscn[i] = scn; + } + } + + dbg->relocate = libdw_typed_alloc (dbg, struct dwarf_file_reloc); + dbg->relocate->ebl = NULL; + dbg->relocate->resolve_symbol = &noresolve_symbol; + + /* All we do to start with is cache the section pointers. + We'll do the rest on demand in digest_relocs, below. */ + for (size_t i = 0; i < IDX_last; ++i) + if (relscn[i] == NULL) + dbg->relocate->sectionrel[i] = NULL; + else + (dbg->relocate->sectionrel[i] + = libdw_typed_alloc (dbg, struct dwarf_section_reloc))->scn = relscn[i]; +} + +void +internal_function +__libdw_relocate_end (Dwarf *dbg) +{ + // XXX let dwfl preinstall, don't close here + ebl_closebackend (dbg->relocate->ebl); +} + +struct digested_reloc +{ + GElf_Sxword addend; + const unsigned char *datum; + int symndx; + bool rel8; +}; + +static bool +match_r_type (const int *types, int r_type) +{ + for (const int *t = types; *t != 0; ++t) + if (r_type == *t) + return true; + return false; +} + +static inline int +digest_one_reloc (const int *rel8_types, const int *rel4_types, + struct dwarf_section_reloc *r, Elf_Data *data, + struct digested_reloc **digest, + GElf_Addr r_offset, GElf_Xword r_info, GElf_Sxword r_addend) +{ + const int r_type = GELF_R_TYPE (r_info); + int symndx = GELF_R_SYM (r_info); + + size_t *nrel = &r->rel8.n; + if (match_r_type (rel8_types, r_type)) + (*digest)->rel8 = true; + else if (unlikely (!match_r_type (rel4_types, r_type))) + return DWARF_E_RELBADTYPE; + else if (unlikely ((GElf_Sxword) (GElf_Sword) r_addend != r_addend)) + return DWARF_E_RELBADADDEND; + else + { + (*digest)->rel8 = false; + nrel = &r->rel4.n; + } + + if (unlikely (data->d_size - ((*digest)->rel8 ? 8 : 4) < r_offset)) + return DWARF_E_RELBADOFF; + + /* Fetch the symbol used in the reloc. STN_UNDEF means no symbol, + just an addend in some unallocated target DWARF section. If the + symbol is defined non-weak in another unallocated section, we can + adjust the addend now and not bother to record any symbol. */ + + inline bool allocated_scn (GElf_Word shndx) + { + Elf_Scn *const symscn = ((Elf_Data_Scn *) r->symdata)->s; + GElf_Shdr shdr; + return (gelf_getshdr (elf_getscn (symscn->elf, shndx), &shdr) == NULL + || (shdr.sh_flags & SHF_ALLOC)); + } + + GElf_Sym sym; + Elf32_Word shndx; + if (symndx != STN_UNDEF + && gelf_getsymshndx (r->symdata, r->symxndxdata, symndx, &sym, &shndx) + && GELF_ST_BIND (sym.st_info) < STB_WEAK + && (sym.st_shndx == SHN_XINDEX ? !allocated_scn (shndx) + : (sym.st_shndx != SHN_UNDEF + && sym.st_shndx < SHN_LORESERVE + && !allocated_scn (sym.st_shndx)))) + { + r_addend += sym.st_value; + symndx = STN_UNDEF; + } + +#if 0 + // XXX do in separate pass only for unstrip -R + if (modify && symndx == STN_UNDEF) + { + /* This is fully resolved statically. + Modify it in place and record nothing. */ + + unsigned char *datum = (unsigned char *) (*digest)->datum; + if ((*digest)->rel8) + { + Elf64_Xword val = read_8ubyte_unaligned (dbg, datum) + r_addend; + if (dbg->other_byte_order) + val = bswap_64 (val); + memcpy (datum, &val, sizeof val); + } + else + { + Elf32_Word val = read_4ubyte_unaligned (dbg, datum) + r_addend; + if (dbg->other_byte_order) + val = bswap_32 (val); + memcpy (datum, &val, sizeof val); + } + return 0; + } +#endif + + if (r_addend != 0 || symndx != STN_UNDEF) + { + /* This will require relocating the data dynamically. Record it. */ + + (*digest)->datum = data->d_buf + r_offset; + (*digest)->symndx = symndx; + (*digest)->addend = r_addend; + ++*digest; + ++*nrel; + } + + return 0; +} + +static int +compare_digested_reloc (const void *a, const void *b) +{ + const struct digested_reloc *r1 = a; + const struct digested_reloc *r2 = b; + return r1->datum > r2->datum ? 1 : r1->datum < r2->datum ? -1 : 0; +} + +static int +digest_relocs (Dwarf *dbg, Elf_Data *data, struct dwarf_section_reloc *r) +{ + GElf_Shdr shdr; + if (unlikely (gelf_getshdr (r->scn, &shdr) == NULL)) + assert (!"impossible gelf_getshdr failure"); + + /* XXX let dwfl supply defaults from main file for separate debug + with relocs pointing to SHT_NOBITS symtab + r->symdata = dbg->relocate->symdata; + r->symstrdata = dbg->relocate->symstrdata; + */ + { + GElf_Shdr symshdr; + Elf_Scn *const symscn = elf_getscn (r->scn->elf, shdr.sh_link); + if (unlikely (gelf_getshdr (symscn, &symshdr) == NULL)) + return DWARF_E_RELBADSYM; + if (symshdr.sh_type != SHT_NOBITS) + { + r->symdata = elf_getdata (symscn, NULL); + if (unlikely (r->symdata == NULL)) + return DWARF_E_RELBADSYM; + r->symstrdata = elf_getdata (elf_getscn (r->scn->elf, symshdr.sh_link), + NULL); + if (unlikely (r->symstrdata == NULL)) + return DWARF_E_RELBADSYM; + } + } + + if (dbg->relocate->ebl == NULL) + { + dbg->relocate->ebl = ebl_openbackend (dbg->elf); + if (unlikely (dbg->relocate->ebl == NULL)) + return DWARF_E_NOMEM; + } + + const int *rel8_types; + const int *rel4_types; + ebl_reloc_simple_types (dbg->relocate->ebl, &rel8_types, &rel4_types); + + Elf_Data *const reldata = elf_getdata (r->scn, NULL); + + const size_t nrel = shdr.sh_size / shdr.sh_entsize; + struct digested_reloc digest[nrel]; + struct digested_reloc *d = digest; + + r->rel4.n = r->rel8.n = 0; + + int ret = 0; + if (shdr.sh_type == SHT_RELA) + for (size_t i = 0; i < nrel && !ret; ++i) + { + GElf_Rela rela; + if (unlikely (gelf_getrela (reldata, i, &rela) == NULL)) + assert (!"impossible gelf_getrela failure"); + ret = digest_one_reloc (rel8_types, rel4_types, r, data, &d, + rela.r_offset, rela.r_info, rela.r_addend); + } + else + for (size_t i = 0; i < nrel && !ret; ++i) + { + GElf_Rel rel; + if (unlikely (gelf_getrel (reldata, i, &rel) == NULL)) + assert (!"impossible gelf_getrel failure"); + ret = digest_one_reloc (rel8_types, rel4_types, r, data, &d, + rel.r_offset, rel.r_info, 0); + } + + assert (r->rel4.n + r->rel8.n == (size_t) (d - digest)); + + if (ret) + return ret; + + /* Sort by datum address. */ + qsort (digest, d - digest, sizeof digest[0], &compare_digested_reloc); + + if (r->rel8.n > 0) + { + r->rel8.datum = libdw_alloc (dbg, const unsigned char *, + sizeof (const unsigned char *), + r->rel8.n); + r->rel8.symndx = libdw_alloc (dbg, int, sizeof (int), r->rel8.n); + if (shdr.sh_type == SHT_RELA) + r->rela8 = libdw_alloc (dbg, Elf64_Sxword, sizeof (Elf64_Sxword), + r->rel8.n); + else + r->rela8 = NULL; + } + + if (r->rel4.n > 0) + { + r->rel4.datum = libdw_alloc (dbg, const unsigned char *, + sizeof (const unsigned char *), + r->rel4.n); + r->rel4.symndx = libdw_alloc (dbg, int, sizeof (int), r->rel4.n); + if (shdr.sh_type == SHT_RELA) + r->rela4 = libdw_alloc (dbg, Elf32_Sword, sizeof (Elf32_Sword), + r->rel4.n); + else + r->rela4 = NULL; + } + + size_t n8 = 0; + size_t n4 = 0; + for (struct digested_reloc *dr = digest; dr < d; ++dr) + if (dr->rel8) + { + r->rel8.datum[n8] = dr->datum; + r->rel8.symndx[n8] = dr->symndx; + if (shdr.sh_type == SHT_RELA) + r->rela8[n8] = dr->addend; + ++n8; + } + else + { + r->rel4.datum[n4] = dr->datum; + r->rel4.symndx[n4] = dr->symndx; + if (shdr.sh_type == SHT_RELA) + r->rela4[n4] = dr->addend; + ++n4; + } + assert (n8 == r->rel8.n); + assert (n4 == r->rel4.n); + + /* Clearing this marks that the digested form is set up now. */ + r->scn = NULL; + return 0; +} + +/* Binary search for an exact match on the DATUM address. */ +static ssize_t +find_reloc (struct dwarf_reloc_table *table, const unsigned char *datum) +{ + size_t l = 0, u = table->n; + + if (u == 0) + return -1; + + if (table->hint < u && table->datum[table->hint] <= datum) + { + l = table->hint; + if (table->datum[l] == datum) + return l; + if (table->datum[l] < datum) + ++l; + } + + while (l < u) + { + size_t i = (l + u) / 2; + if (table->datum[i] < datum) + l = i + 1; + else if (table->datum[i] > datum) + u = i; + else + return table->hint = i; + } + + table->hint = l; + return -1; +} + +static int +relocatable_datum (Dwarf *dbg, int sec_index, struct dwarf_section_reloc *r, + const unsigned char *datum, int width, + int *symndx, GElf_Sxword *addend) +{ + if (r == NULL) + return 0; + + if (r->scn != NULL) + { + int result = digest_relocs (dbg, dbg->sectiondata[sec_index], r); + if (unlikely (result != 0)) + { + __libdw_seterrno (result); + return -1; + } + } + + ssize_t i; + if (width == 4) + { + i = find_reloc (&r->rel4, datum); + *symndx = i < 0 ? STN_UNDEF : r->rel4.symndx[i]; + if (addend != NULL) + *addend = ((i < 0 || r->rela4 == NULL) + ? read_4sbyte_unaligned (dbg, datum) : r->rela4[i]); + } + else + { + assert (width == 8); + + i = find_reloc (&r->rel8, datum); + *symndx = i < 0 ? STN_UNDEF : r->rel8.symndx[i]; + if (addend != NULL) + *addend = ((i < 0 || r->rela8 == NULL) + ? read_8sbyte_unaligned (dbg, datum) : r->rela8[i]); + } + + return i >= 0; +} + +static int +reloc_getsym (struct dwarf_section_reloc *r, int symndx, + GElf_Sym *sym, GElf_Word *shndx) +{ + if (unlikely (gelf_getsymshndx (r->symdata, r->symxndxdata, + symndx, sym, shndx) == NULL)) + { + __libdw_seterrno (DWARF_E_RELBADSYM); + return -1; + } + return 1; +} + +int +internal_function +__libdw_relocate_address (Dwarf *dbg, int sec_index, + const void *datum, int width, Dwarf_Addr *val) +{ + struct dwarf_section_reloc *const r = dbg->relocate->sectionrel[sec_index]; + int symndx; + GElf_Sxword addend; + int result = relocatable_datum (dbg, sec_index, r, datum, width, + &symndx, &addend); + if (result > 0 && symndx != STN_UNDEF) + { + GElf_Sym sym; + GElf_Word shndx; + result = reloc_getsym (r, symndx, &sym, &shndx); + if (result > 0) + { + result = (*dbg->relocate->resolve_symbol) + (sym.st_shndx == SHN_UNDEF || sym.st_shndx == SHN_COMMON + || GELF_ST_BIND (sym.st_info) > STB_WEAK, + dbg, &sym, sym.st_shndx == SHN_XINDEX ? shndx : sym.st_shndx); + addend += sym.st_value; + } + } + if (result >= 0) + *val = addend; + return result; +} + +int +internal_function +__libdw_relocate_offset (Dwarf *dbg, int sec_index, + const void *datum, int width, Dwarf_Off *val) +{ + struct dwarf_section_reloc *const r = dbg->relocate->sectionrel[sec_index]; + int symndx; + GElf_Sxword addend; + int result = relocatable_datum (dbg, sec_index, r, datum, width, + &symndx, &addend); + if (result > 0 && symndx != STN_UNDEF) + { + GElf_Sym sym; + GElf_Word shndx; + result = reloc_getsym (r, symndx, &sym, &shndx); + if (result > 0) + { + if (unlikely (sym.st_shndx == SHN_UNDEF) + || (sym.st_shndx > SHN_LORESERVE + && unlikely (sym.st_shndx != SHN_XINDEX))) + { + __libdw_seterrno (DWARF_E_RELUNDEF); + return -1; + } + if (unlikely (((Elf_Data_Scn *) dbg->sectiondata[sec_index])->s->index + != (sym.st_shndx == SHN_XINDEX ? shndx : sym.st_shndx))) + { + __libdw_seterrno (DWARF_E_RELWRONGSEC); + return -1; + } + addend += sym.st_value; + } + } + if (result >= 0) + *val = addend; + return result; +} diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index f1887aed5..92be9488a 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,8 @@ +2010-06-21 Roland McGrath + + * dwfl_module_getsrc_file.: Update for Dwarf_Line member change. + * dwfl_lineinfo.c: Likewise. Use dwarf_lineaddr. + 2010-06-20 Roland McGrath * relocate.c (struct reloc_symtab_cache): New members diff --git a/libdwfl/dwfl_lineinfo.c b/libdwfl/dwfl_lineinfo.c index 0d8a6887e..920a2de9c 100644 --- a/libdwfl/dwfl_lineinfo.c +++ b/libdwfl/dwfl_lineinfo.c @@ -1,5 +1,5 @@ /* Get information from a source line record returned by libdwfl. - Copyright (C) 2005, 2006 Red Hat, Inc. + Copyright (C) 2005-2010 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -67,7 +67,7 @@ dwfl_lineinfo (Dwfl_Line *line, Dwarf_Addr *addr, int *linep, int *colp, if (colp != NULL) *colp = info->column; - struct Dwarf_Fileinfo_s *file = &info->files->info[info->file]; + struct Dwarf_Fileinfo_s *file = &info->cu->files->info[info->file]; if (mtime != NULL) *mtime = file->mtime; if (length != NULL) diff --git a/libdwfl/dwfl_module_getsrc_file.c b/libdwfl/dwfl_module_getsrc_file.c index 9d0c786b9..fe305c682 100644 --- a/libdwfl/dwfl_module_getsrc_file.c +++ b/libdwfl/dwfl_module_getsrc_file.c @@ -1,5 +1,5 @@ /* Find matching source locations in a module. - Copyright (C) 2005 Red Hat, Inc. + Copyright (C) 2005-2010 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -81,7 +81,7 @@ dwfl_module_getsrc_file (Dwfl_Module *mod, { inline const char *INTUSE(dwarf_line_file) (const Dwarf_Line *line) { - return line->files->info[line->file].name; + return line->cu->files->info[line->file].name; } inline Dwarf_Line *dwfl_line (const Dwfl_Line *line) { @@ -101,7 +101,7 @@ dwfl_module_getsrc_file (Dwfl_Module *mod, { Dwarf_Line *line = &cu->die.cu->lines->info[cnt]; - if (unlikely (line->file >= line->files->nfiles)) + if (unlikely (line->file >= line->cu->files->nfiles)) { __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF)); return -1;