# Contains DWARF 5 support and includes the following upstream patches: 6b0019cd0c5ae18a59affa49a857b4d610d403e4 a834fccf3c94f78ad6a1b35ae352b1ede183dde4 86408cd826c32229817071bd008d9856cda4aca5 0d1003bc723ba30bfe325bb51aeafe7dbfed6a5c diff --git a/tools/debugedit.c b/tools/debugedit.c index 6bea88551..b0849efd8 100644 --- a/tools/debugedit.c +++ b/tools/debugedit.c @@ -103,6 +103,8 @@ static bool need_string_replacement = false; /* Whether we need to do any updates of the string indexes (DW_FORM_strp) in debug_info for string indexes. */ static bool need_strp_update = false; +/* Likewise for DW_FORM_line_strp. */ +static bool need_line_strp_update = false; /* If the debug_line changes size we will need to update the DW_AT_stmt_list attributes indexes in the debug_info. */ static bool need_stmt_update = false; @@ -192,7 +194,7 @@ typedef struct const char *filename; int lastscn; size_t phnum; - struct strings strings; + struct strings debug_str, debug_line_str; struct debug_lines lines; GElf_Shdr shdr[0]; } DSO; @@ -233,7 +235,7 @@ typedef struct int shift = 0; \ do \ { \ - c = *ptr++; \ + c = *(ptr)++; \ ret |= (c & 0x7f) << shift; \ shift += 7; \ } while (c & 0x80); \ @@ -251,7 +253,7 @@ typedef struct valv >>= 7; \ if (valv) \ c |= 0x80; \ - *ptr++ = c; \ + *(ptr)++ = c; \ } \ while (valv); \ }) @@ -311,7 +313,7 @@ strptr (DSO *dso, int sec, off_t offset) } -#define read_8(ptr) *ptr++ +#define read_8(ptr) *(ptr)++ #define read_16(ptr) ({ \ uint16_t ret = do_read_16 (ptr); \ @@ -328,13 +330,13 @@ strptr (DSO *dso, int sec, off_t offset) REL *relptr, *relend; int reltype; -#define do_read_32_relocated(ptr) ({ \ - uint32_t dret = do_read_32 (ptr); \ +#define do_read_32_relocated(xptr) ({ \ + uint32_t dret = do_read_32 (xptr); \ if (relptr) \ { \ - while (relptr < relend && relptr->ptr < ptr) \ + while (relptr < relend && relptr->ptr < (xptr)) \ ++relptr; \ - if (relptr < relend && relptr->ptr == ptr) \ + if (relptr < relend && relptr->ptr == (xptr)) \ { \ if (reltype == SHT_REL) \ dret += relptr->addend; \ @@ -433,7 +435,8 @@ typedef struct debug_section int sec, relsec; REL *relbuf; REL *relend; - struct debug_section *next; /* Only happens for COMDAT .debug_macro. */ + /* Only happens for COMDAT .debug_macro and .debug_types. */ + struct debug_section *next; } debug_section; static debug_section debug_sections[] = @@ -452,6 +455,11 @@ static debug_section debug_sections[] = #define DEBUG_TYPES 11 #define DEBUG_MACRO 12 #define DEBUG_GDB_SCRIPT 13 +#define DEBUG_RNGLISTS 14 +#define DEBUG_LINE_STR 15 +#define DEBUG_ADDR 16 +#define DEBUG_STR_OFFSETS 17 +#define DEBUG_LOCLISTS 18 { ".debug_info", NULL, NULL, 0, 0, 0 }, { ".debug_abbrev", NULL, NULL, 0, 0, 0 }, { ".debug_line", NULL, NULL, 0, 0, 0 }, @@ -466,6 +474,11 @@ static debug_section debug_sections[] = { ".debug_types", NULL, NULL, 0, 0, 0 }, { ".debug_macro", NULL, NULL, 0, 0, 0 }, { ".debug_gdb_scripts", NULL, NULL, 0, 0, 0 }, + { ".debug_rnglists", NULL, NULL, 0, 0, 0 }, + { ".debug_line_str", NULL, NULL, 0, 0, 0 }, + { ".debug_addr", NULL, NULL, 0, 0, 0 }, + { ".debug_str_offsets", NULL, NULL, 0, 0, 0 }, + { ".debug_loclists", NULL, NULL, 0, 0, 0 }, { NULL, NULL, NULL, 0, 0, 0 } }; @@ -542,10 +555,11 @@ setup_relbuf (DSO *dso, debug_section *sec, int *reltype) /* Relocations against section symbols are uninteresting in REL. */ if (dso->shdr[i].sh_type == SHT_REL && sym.st_value == 0) continue; - /* Only consider relocations against .debug_str, .debug_line - and .debug_abbrev. */ + /* Only consider relocations against .debug_str, .debug_line, + .debug_line_str, and .debug_abbrev. */ if (sym.st_shndx != debug_sections[DEBUG_STR].sec && sym.st_shndx != debug_sections[DEBUG_LINE].sec + && sym.st_shndx != debug_sections[DEBUG_LINE_STR].sec && sym.st_shndx != debug_sections[DEBUG_ABBREV].sec) continue; rela.r_addend += sym.st_value; @@ -754,12 +768,29 @@ no_memory: } form = read_uleb128 (ptr); if (form == 2 - || (form > DW_FORM_flag_present && form != DW_FORM_ref_sig8)) + || (form > DW_FORM_flag_present + && !(form == DW_FORM_ref_sig8 + || form == DW_FORM_data16 + || form == DW_FORM_line_strp + || form == DW_FORM_implicit_const + || form == DW_FORM_addrx + || form == DW_FORM_loclistx + || form == DW_FORM_rnglistx + || form == DW_FORM_addrx1 + || form == DW_FORM_addrx2 + || form == DW_FORM_addrx3 + || form == DW_FORM_addrx4))) { - error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, form); + error (0, 0, "%s: Unknown DWARF DW_FORM_0x%x", dso->filename, + form); htab_delete (h); return NULL; } + if (form == DW_FORM_implicit_const) + { + /* It is SLEB128 but the value is dropped anyway. */ + read_uleb128 (ptr); + } t->attr[t->nattr].attr = attr; t->attr[t->nattr++].form = form; @@ -1022,17 +1053,20 @@ string_find_entry (struct strings *strings, size_t old_idx) a replacement file string has been recorded for it, otherwise returns false. */ static bool -record_file_string_entry_idx (struct strings *strings, size_t old_idx) +record_file_string_entry_idx (bool line_strp, DSO *dso, size_t old_idx) { + struct strings *strings = line_strp ? &dso->debug_line_str : &dso->debug_str; bool ret = false; struct stridxentry *entry = string_find_new_entry (strings, old_idx); if (entry != NULL) { - if (old_idx >= debug_sections[DEBUG_STR].size) - error (1, 0, "Bad string pointer index %zd", old_idx); + debug_section *sec = &debug_sections[line_strp + ? DEBUG_LINE_STR : DEBUG_STR]; + if (old_idx >= sec->size) + error (1, 0, "Bad string pointer index %zd (%s)", old_idx, sec->name); Strent *strent; - const char *old_str = (char *)debug_sections[DEBUG_STR].data + old_idx; + const char *old_str = (char *)sec->data + old_idx; const char *file = skip_dir_prefix (old_str, base_dir); if (file == NULL) { @@ -1076,15 +1110,18 @@ record_file_string_entry_idx (struct strings *strings, size_t old_idx) base_dir with dest_dir, just records the existing string associated with the index. */ static void -record_existing_string_entry_idx (struct strings *strings, size_t old_idx) +record_existing_string_entry_idx (bool line_strp, DSO *dso, size_t old_idx) { + struct strings *strings = line_strp ? &dso->debug_line_str : &dso->debug_str; struct stridxentry *entry = string_find_new_entry (strings, old_idx); if (entry != NULL) { - if (old_idx >= debug_sections[DEBUG_STR].size) - error (1, 0, "Bad string pointer index %zd", old_idx); + debug_section *sec = &debug_sections[line_strp + ? DEBUG_LINE_STR : DEBUG_STR]; + if (old_idx >= sec->size) + error (1, 0, "Bad string pointer index %zd (%s)", old_idx, sec->name); - const char *str = (char *)debug_sections[DEBUG_STR].data + old_idx; + const char *str = (char *)sec->data + old_idx; Strent *strent = strtab_add_len (strings->str_tab, str, strlen (str) + 1); if (strent == NULL) @@ -1217,13 +1254,28 @@ get_line_table (DSO *dso, size_t off, struct line_table **table) /* version */ t->version = read_16 (ptr); - if (t->version != 2 && t->version != 3 && t->version != 4) + if (t->version != 2 && t->version != 3 && t->version != 4 && t->version != 5) { error (0, 0, "%s: DWARF version %d unhandled", dso->filename, t->version); return false; } + if (t->version >= 5) + { + /* address_size */ + assert (ptr_size != 0); + if (ptr_size != read_8 (ptr)) + { + error (0, 0, "%s: .debug_line address size differs from .debug_info", + dso->filename); + return false; + } + + /* segment_selector_size */ + (void) read_8 (ptr); + } + /* header_length */ unsigned char *endprol = ptr + 4; t->header_length = read_32 (ptr); @@ -1269,7 +1321,9 @@ static int dirty_elf; static void dirty_section (unsigned int sec) { - elf_flagdata (debug_sections[sec].elf_data, ELF_C_SET, ELF_F_DIRTY); + for (struct debug_section *secp = &debug_sections[sec]; secp != NULL; + secp = secp->next) + elf_flagdata (secp->elf_data, ELF_C_SET, ELF_F_DIRTY); dirty_elf = 1; } @@ -1314,7 +1368,9 @@ edit_dwarf2_line (DSO *dso) linedata->d_size = dso->lines.debug_lines_len; linedata->d_buf = dso->lines.line_buf; + debug_sections[DEBUG_LINE].data = linedata->d_buf; debug_sections[DEBUG_LINE].size = linedata->d_size; + debug_sections[DEBUG_LINE].elf_data = linedata; /* Make sure the line tables are sorted on the old index. */ qsort (dso->lines.table, dso->lines.used, sizeof (struct line_table), @@ -1454,42 +1510,151 @@ edit_dwarf2_line (DSO *dso) } } -/* Called during phase zero for each debug_line table referenced from - .debug_info. Outputs all source files seen and records any - adjustments needed in the debug_list data structures. Returns true - if line_table needs to be rewrite either the dir or file paths. */ -static bool -read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir) +/* Record or adjust (according to phase) DW_FORM_strp or DW_FORM_line_strp. */ +static void +edit_strp (DSO *dso, bool line_strp, unsigned char *ptr, int phase, + bool handled_strp) { - unsigned char *ptr, *dir; - unsigned char **dirt; - uint32_t value, dirt_cnt; - size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir); - struct line_table *table; + unsigned char *ptr_orig = ptr; - if (get_line_table (dso, off, &table) == false - || table == NULL) + /* In the first pass we collect all strings, in the + second we put the new references back (if there are + any changes). */ + if (phase == 0) { - if (table != NULL) - error (0, 0, ".debug_line offset 0x%x referenced multiple times", - off); - return false; + /* handled_strp is set for attributes referring to + files. If it is set the string is already + recorded. */ + if (! handled_strp) + { + size_t idx = do_read_32_relocated (ptr); + record_existing_string_entry_idx (line_strp, dso, idx); + } + } + else if (line_strp + ? need_line_strp_update : need_strp_update) /* && phase == 1 */ + { + struct stridxentry *entry; + size_t idx, new_idx; + struct strings *strings = (line_strp + ? &dso->debug_line_str : &dso->debug_str); + idx = do_read_32_relocated (ptr); + entry = string_find_entry (strings, idx); + new_idx = strent_offset (entry->entry); + do_write_32_relocated (ptr, new_idx); } - /* Skip to the directory table. The rest of the header has already - been read and checked by get_line_table. */ - ptr = debug_sections[DEBUG_LINE].data + off; - ptr += (4 /* unit len */ - + 2 /* version */ - + 4 /* header len */ - + 1 /* min instr len */ - + (table->version >= 4) /* max op per instr, if version >= 4 */ - + 1 /* default is stmt */ - + 1 /* line base */ - + 1 /* line range */ - + 1 /* opcode base */ - + table->opcode_base - 1); /* opcode len table */ - dir = ptr; + assert (ptr == ptr_orig); +} + +/* Adjust *PTRP after the current *FORMP, update *FORMP for FORM_INDIRECT. */ +static enum { FORM_OK, FORM_ERROR, FORM_INDIRECT } +skip_form (DSO *dso, uint32_t *formp, unsigned char **ptrp) +{ + size_t len = 0; + + switch (*formp) + { + case DW_FORM_ref_addr: + if (cu_version == 2) + *ptrp += ptr_size; + else + *ptrp += 4; + break; + case DW_FORM_flag_present: + case DW_FORM_implicit_const: + break; + case DW_FORM_addr: + *ptrp += ptr_size; + break; + case DW_FORM_ref1: + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_strx1: + case DW_FORM_addrx1: + ++*ptrp; + break; + case DW_FORM_ref2: + case DW_FORM_data2: + case DW_FORM_strx2: + case DW_FORM_addrx2: + *ptrp += 2; + break; + case DW_FORM_strx3: + case DW_FORM_addrx3: + *ptrp += 3; + break; + case DW_FORM_ref4: + case DW_FORM_data4: + case DW_FORM_strx4: + case DW_FORM_addrx4: + case DW_FORM_sec_offset: + *ptrp += 4; + break; + case DW_FORM_ref8: + case DW_FORM_data8: + case DW_FORM_ref_sig8: + *ptrp += 8; + break; + case DW_FORM_data16: + *ptrp += 16; + break; + case DW_FORM_sdata: + case DW_FORM_ref_udata: + case DW_FORM_udata: + case DW_FORM_strx: + case DW_FORM_loclistx: + case DW_FORM_rnglistx: + case DW_FORM_addrx: + read_uleb128 (*ptrp); + break; + case DW_FORM_strp: + case DW_FORM_line_strp: + *ptrp += 4; + break; + case DW_FORM_string: + *ptrp = (unsigned char *) strchr ((char *)*ptrp, '\0') + 1; + break; + case DW_FORM_indirect: + *formp = read_uleb128 (*ptrp); + return FORM_INDIRECT; + case DW_FORM_block1: + len = *(*ptrp)++; + break; + case DW_FORM_block2: + len = read_16 (*ptrp); + *formp = DW_FORM_block1; + break; + case DW_FORM_block4: + len = read_32 (*ptrp); + *formp = DW_FORM_block1; + break; + case DW_FORM_block: + case DW_FORM_exprloc: + len = read_uleb128 (*ptrp); + *formp = DW_FORM_block1; + assert (len < UINT_MAX); + break; + default: + error (0, 0, "%s: Unknown DWARF DW_FORM_0x%x", dso->filename, *formp); + return FORM_ERROR; + } + + if (*formp == DW_FORM_block1) + *ptrp += len; + + return FORM_OK; +} + +/* Part of read_dwarf2_line processing DWARF-4. */ +static bool +read_dwarf4_line (DSO *dso, unsigned char *ptr, char *comp_dir, + struct line_table *table) +{ + unsigned char **dirt; + uint32_t value, dirt_cnt; + size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir); + unsigned char *dir = ptr; /* dir table: */ value = 1; @@ -1622,6 +1787,296 @@ read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir) read_uleb128 (ptr); } + return true; +} + +/* Called by read_dwarf5_line first for directories and then file + names as they both have the same format. */ +static bool +read_dwarf5_line_entries (DSO *dso, unsigned char **ptrp, + struct line_table *table, int phase, + char ***dirs, int *ndir, + const char *entry_name) +{ + /* directory_entry_format_count */ + /* file_name_entry_format_count */ + unsigned format_count = read_8 (*ptrp); + + unsigned char *formats = *ptrp; + + /* directory_entry_format */ + /* file_name_entry_format */ + for (unsigned formati = 0; formati < format_count; ++formati) + { + read_uleb128 (*ptrp); + read_uleb128 (*ptrp); + } + + /* directories_count */ + /* file_names_count */ + unsigned entry_count = read_uleb128 (*ptrp); + + bool collecting_dirs = dest_dir && phase == 0 && *dirs == NULL; + bool writing_files = dest_dir && phase == 0 && *dirs != NULL; + if (collecting_dirs) + { + *ndir = entry_count; + *dirs = malloc (entry_count * sizeof (char *)); + if (*dirs == NULL) + error (1, errno, "%s: Could not allocate debug_line dirs", + dso->filename); + } + + /* directories */ + /* file_names */ + for (unsigned entryi = 0; entryi < entry_count; ++entryi) + { + char *dir = NULL; + char *file = NULL;; + unsigned char *format_ptr = formats; + for (unsigned formati = 0; formati < format_count; ++formati) + { + unsigned lnct = read_uleb128 (format_ptr); + unsigned form = read_uleb128 (format_ptr); + bool handled_form = false; + bool handled_strp = false; + bool line_strp = form == DW_FORM_line_strp; + if (lnct == DW_LNCT_path) + { + switch (form) + { + case DW_FORM_strp: + case DW_FORM_line_strp: + if (dest_dir && phase == 0) + { + size_t idx = do_read_32_relocated (*ptrp); + if (record_file_string_entry_idx (line_strp, dso, idx)) + { + if (line_strp) + need_line_strp_update = true; + else + need_strp_update = true; + } + handled_strp = true; + if (collecting_dirs || writing_files) + { + debug_section *sec = &debug_sections[line_strp + ? DEBUG_LINE_STR : DEBUG_STR]; + if (collecting_dirs) + dir = (char *)sec->data + idx; + if (writing_files) + file = (char *)sec->data + idx; + } + } + break; + default: + error (0, 0, "%s: Unsupported " + ".debug_line %s %u path DW_FORM_0x%x", + dso->filename, entry_name, entryi, form); + return false; + } + } + if (writing_files && lnct == DW_LNCT_directory_index) + { + int dirndx; + switch (form) + { + case DW_FORM_udata: + handled_form = true; + dirndx = read_uleb128 (*ptrp); + break; + case DW_FORM_data1: + dirndx = **ptrp; + break; + case DW_FORM_data2: + dirndx = do_read_16 (*ptrp); + break; + case DW_FORM_data4: + dirndx = do_read_32 (*ptrp); + break; + default: + error (0, 0, "%s: Unsupported " + ".debug_line %s %u dirndx DW_FORM_0x%x", + dso->filename, entry_name, entryi, form); + return false; + } + + if (dirndx > *ndir) + { + error (0, 0, "%s: Bad dir number %u in .debug_line %s", + dso->filename, entryi, entry_name); + return false; + } + dir = (*dirs)[dirndx]; + } + + switch (form) + { + case DW_FORM_strp: + case DW_FORM_line_strp: + edit_strp (dso, line_strp, *ptrp, phase, handled_strp); + break; + } + + if (!handled_form) + { + switch (skip_form (dso, &form, ptrp)) + { + case FORM_OK: + break; + case FORM_ERROR: + return false; + case FORM_INDIRECT: + error (0, 0, "%s: Unsupported " + ".debug_line %s %u DW_FORM_indirect", + dso->filename, entry_name, entryi); + return false; + } + } + } + + if (collecting_dirs) + (*dirs)[entryi] = dir; + + if (writing_files) + { + char *comp_dir = (*dirs)[0]; + size_t comp_dir_len = strlen(comp_dir); + size_t file_len = strlen (file); + size_t dir_len = strlen (dir); + + char *s = malloc (comp_dir_len + 1 + file_len + 1 + dir_len + 1); + if (s == NULL) + { + error (0, ENOMEM, "%s: Reading file table", dso->filename); + return false; + } + if (file[0] == '/') + { + memcpy (s, file, file_len + 1); + } + else if (dir[0] == '/') + { + memcpy (s, dir, dir_len); + s[dir_len] = '/'; + memcpy (s + dir_len + 1, file, file_len + 1); + } + else + { + char *p = s; + if (comp_dir_len != 0) + { + memcpy (s, comp_dir, comp_dir_len); + s[comp_dir_len] = '/'; + p += comp_dir_len + 1; + } + memcpy (p, dir, dir_len); + p[dir_len] = '/'; + memcpy (p + dir_len + 1, file, file_len + 1); + } + canonicalize_path (s, s); + if (list_file_fd != -1) + { + const char *p = NULL; + if (base_dir == NULL) + p = s; + else + { + p = skip_dir_prefix (s, base_dir); + if (p == NULL && dest_dir != NULL) + p = skip_dir_prefix (s, dest_dir); + } + + if (p) + { + size_t size = strlen (p) + 1; + while (size > 0) + { + ssize_t ret = write (list_file_fd, p, size); + if (ret == -1) + break; + size -= ret; + p += ret; + } + } + } + + free (s); + } + } + + return true; +} + +/* Part of read_dwarf2_line processing DWARF-5. */ +static bool +read_dwarf5_line (DSO *dso, unsigned char *ptr, struct line_table *table, + int phase) +{ + char **dirs = NULL; + int ndir; + /* Skip header. */ + ptr += (4 /* unit len */ + + 2 /* version */ + + (table->version < 5 ? 0 : 0 + + 1 /* address_size */ + + 1 /* segment_selector*/) + + 4 /* header len */ + + 1 /* min instr len */ + + (table->version >= 4) /* max op per instr, if version >= 4 */ + + 1 /* default is stmt */ + + 1 /* line base */ + + 1 /* line range */ + + 1 /* opcode base */ + + table->opcode_base - 1); /* opcode len table */ + + bool retval = (read_dwarf5_line_entries (dso, &ptr, table, phase, + &dirs, &ndir, "directory") + && read_dwarf5_line_entries (dso, &ptr, table, phase, + &dirs, &ndir, "file name")); + free (dirs); + return retval; +} + +/* Called during phase zero for each debug_line table referenced from + .debug_info. Outputs all source files seen and records any + adjustments needed in the debug_list data structures. Returns true + if line_table needs to be rewrite either the dir or file paths. */ +static bool +read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir) +{ + unsigned char *ptr; + struct line_table *table; + + if (get_line_table (dso, off, &table) == false + || table == NULL) + return false; + + /* Skip to the directory table. The rest of the header has already + been read and checked by get_line_table. */ + ptr = debug_sections[DEBUG_LINE].data + off; + ptr += (4 /* unit len */ + + 2 /* version */ + + (table->version < 5 ? 0 : 0 + + 1 /* address_size */ + + 1 /* segment_selector*/) + + 4 /* header len */ + + 1 /* min instr len */ + + (table->version >= 4) /* max op per instr, if version >= 4 */ + + 1 /* default is stmt */ + + 1 /* line base */ + + 1 /* line range */ + + 1 /* opcode base */ + + table->opcode_base - 1); /* opcode len table */ + + /* DWARF version 5 line tables won't change size. But they might need + [line]strp recording/updates. Handle that part later. */ + if (table->version < 5) + { + if (! read_dwarf4_line (dso, ptr, comp_dir, table)) + return false; + } + dso->lines.debug_lines_len += 4 + table->unit_length + table->size_diff; return table->replace_dirs || table->replace_files; } @@ -1639,6 +2094,40 @@ find_new_list_offs (struct debug_lines *lines, size_t idx) return table->new_idx; } +/* Read DW_FORM_strp or DW_FORM_line_strp collecting compilation directory. */ +static void +edit_attributes_str_comp_dir (bool line_strp, DSO *dso, unsigned char **ptrp, + int phase, char **comp_dirp, bool *handled_strpp) +{ + const char *dir; + size_t idx = do_read_32_relocated (*ptrp); + /* In phase zero we collect the comp_dir. */ + if (phase == 0) + { + debug_section *sec = &debug_sections[line_strp + ? DEBUG_LINE_STR : DEBUG_STR]; + if (sec->data == NULL || idx >= sec->size) + error (1, 0, "%s: Bad string pointer index %zd for comp_dir (%s)", + dso->filename, idx, sec->name); + dir = (char *) sec->data + idx; + + free (*comp_dirp); + *comp_dirp = strdup (dir); + } + + if (dest_dir != NULL && phase == 0) + { + if (record_file_string_entry_idx (line_strp, dso, idx)) + { + if (line_strp) + need_line_strp_update = true; + else + need_strp_update = true; + } + *handled_strpp = true; + } +} + /* This scans the attributes of one DIE described by the given abbrev_tag. PTR points to the data in the debug_info. It will be advanced till all abbrev data is consumed. In phase zero data is collected, in phase one @@ -1657,7 +2146,6 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) for (i = 0; i < t->nattr; ++i) { uint32_t form = t->attr[i].form; - size_t len = 0; while (1) { /* Whether we already handled a string as file for this @@ -1743,38 +2231,24 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) } } } - else if (form == DW_FORM_strp && - debug_sections[DEBUG_STR].data) - { - const char *dir; - size_t idx = do_read_32_relocated (ptr); - /* In phase zero we collect the comp_dir. */ - if (phase == 0) - { - if (idx >= debug_sections[DEBUG_STR].size) - error (1, 0, - "%s: Bad string pointer index %zd for comp_dir", - dso->filename, idx); - dir = (char *) debug_sections[DEBUG_STR].data + idx; - - free (comp_dir); - comp_dir = strdup (dir); - } - - if (dest_dir != NULL && phase == 0) - { - if (record_file_string_entry_idx (&dso->strings, idx)) - need_strp_update = true; - handled_strp = true; - } - } + else if (form == DW_FORM_strp) + edit_attributes_str_comp_dir (false /* line_strp */, dso, + &ptr, phase, &comp_dir, + &handled_strp); + else if (form == DW_FORM_line_strp) + edit_attributes_str_comp_dir (true /* line_strp */, dso, &ptr, + phase, &comp_dir, &handled_strp); } else if ((t->tag == DW_TAG_compile_unit || t->tag == DW_TAG_partial_unit) - && t->attr[i].attr == DW_AT_name - && form == DW_FORM_strp - && debug_sections[DEBUG_STR].data) + && ((form == DW_FORM_strp + && debug_sections[DEBUG_STR].data) + || (form == DW_FORM_line_strp + && debug_sections[DEBUG_LINE_STR].data)) + && t->attr[i].attr == DW_AT_name) { + bool line_strp = form == DW_FORM_line_strp; + /* DW_AT_name is the primary file for this compile unit. If starting with / it is a full path name. Note that we don't handle DW_FORM_string in this @@ -1784,11 +2258,14 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) /* In phase zero we will look for a comp_dir to use. */ if (phase == 0) { - if (idx >= debug_sections[DEBUG_STR].size) + debug_section *sec = &debug_sections[line_strp + ? DEBUG_LINE_STR + : DEBUG_STR]; + if (idx >= sec->size) error (1, 0, - "%s: Bad string pointer index %zd for unit name", - dso->filename, idx); - char *name = (char *) debug_sections[DEBUG_STR].data + idx; + "%s: Bad string pointer index %zd for unit name (%s)", + dso->filename, idx, sec->name); + char *name = (char *) sec->data + idx; if (*name == '/' && comp_dir == NULL) { char *enddir = strrchr (name, '/'); @@ -1809,107 +2286,37 @@ edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) pass (1) stores it (the new index). */ if (dest_dir && phase == 0) { - if (record_file_string_entry_idx (&dso->strings, idx)) - need_strp_update = true; + if (record_file_string_entry_idx (line_strp, dso, idx)) + { + if (line_strp) + need_line_strp_update = true; + else + need_strp_update = true; + } handled_strp = true; } } switch (form) { - case DW_FORM_ref_addr: - if (cu_version == 2) - ptr += ptr_size; - else - ptr += 4; - break; - case DW_FORM_flag_present: - break; - case DW_FORM_addr: - ptr += ptr_size; - break; - case DW_FORM_ref1: - case DW_FORM_flag: - case DW_FORM_data1: - ++ptr; - break; - case DW_FORM_ref2: - case DW_FORM_data2: - ptr += 2; - break; - case DW_FORM_ref4: - case DW_FORM_data4: - case DW_FORM_sec_offset: - ptr += 4; - break; - case DW_FORM_ref8: - case DW_FORM_data8: - case DW_FORM_ref_sig8: - ptr += 8; - break; - case DW_FORM_sdata: - case DW_FORM_ref_udata: - case DW_FORM_udata: - read_uleb128 (ptr); - break; case DW_FORM_strp: - /* In the first pass we collect all strings, in the - second we put the new references back (if there are - any changes). */ - if (phase == 0) - { - /* handled_strp is set for attributes referring to - files. If it is set the string is already - recorded. */ - if (! handled_strp) - { - size_t idx = do_read_32_relocated (ptr); - record_existing_string_entry_idx (&dso->strings, idx); - } - } - else if (need_strp_update) /* && phase == 1 */ - { - struct stridxentry *entry; - size_t idx, new_idx; - idx = do_read_32_relocated (ptr); - entry = string_find_entry (&dso->strings, idx); - new_idx = strent_offset (entry->entry); - do_write_32_relocated (ptr, new_idx); - } - ptr += 4; + edit_strp (dso, false /* line_strp */, ptr, phase, handled_strp); break; - case DW_FORM_string: - ptr = (unsigned char *) strchr ((char *)ptr, '\0') + 1; + case DW_FORM_line_strp: + edit_strp (dso, true /* line_strp */, ptr, phase, handled_strp); break; - case DW_FORM_indirect: - form = read_uleb128 (ptr); - continue; - case DW_FORM_block1: - len = *ptr++; - break; - case DW_FORM_block2: - len = read_16 (ptr); - form = DW_FORM_block1; - break; - case DW_FORM_block4: - len = read_32 (ptr); - form = DW_FORM_block1; - break; - case DW_FORM_block: - case DW_FORM_exprloc: - len = read_uleb128 (ptr); - form = DW_FORM_block1; - assert (len < UINT_MAX); + } + + switch (skip_form (dso, &form, &ptr)) + { + case FORM_OK: break; - default: - error (0, 0, "%s: Unknown DWARF DW_FORM_%d", dso->filename, - form); + case FORM_ERROR: return NULL; + case FORM_INDIRECT: + continue; } - if (form == DW_FORM_block1) - ptr += len; - break; } } @@ -1964,6 +2371,163 @@ line_rel_cmp (const void *a, const void *b) return 0; } +static int +edit_info (DSO *dso, int phase, struct debug_section *sec) +{ + unsigned char *ptr, *endcu, *endsec; + uint32_t value; + htab_t abbrev; + struct abbrev_tag tag, *t; + + ptr = sec->data; + if (ptr == NULL) + return 0; + + setup_relbuf(dso, sec, &reltype); + endsec = ptr + sec->size; + while (ptr < endsec) + { + unsigned char *cu_start = ptr; + + /* header size, version, unit_type, ptr_size. */ + if (ptr + 4 + 2 + 1 + 1 > endsec) + { + error (0, 0, "%s: %s CU header too small", + dso->filename, sec->name); + return 1; + } + + endcu = ptr + 4; + endcu += read_32 (ptr); + if (endcu == ptr + 0xffffffff) + { + error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); + return 1; + } + + if (endcu > endsec) + { + error (0, 0, "%s: %s too small", dso->filename, sec->name); + return 1; + } + + cu_version = read_16 (ptr); + if (cu_version != 2 && cu_version != 3 && cu_version != 4 + && cu_version != 5) + { + error (0, 0, "%s: DWARF version %d unhandled", dso->filename, + cu_version); + return 1; + } + + int cu_ptr_size = 0; + + if (cu_version >= 5) + { + uint8_t unit_type = read_8 (ptr); + if (unit_type != DW_UT_compile && unit_type != DW_UT_partial) + { + error (0, 0, "%s: Unit type %u unhandled", dso->filename, + unit_type); + return 1; + } + + cu_ptr_size = read_8 (ptr); + } + + unsigned char *header_end = (cu_start + 23 + (cu_version < 5 ? 0 : 1)); + if (header_end > endsec) + { + error (0, 0, "%s: %s CU header too small", dso->filename, sec->name); + return 1; + } + + value = read_32_relocated (ptr); + if (value >= debug_sections[DEBUG_ABBREV].size) + { + if (debug_sections[DEBUG_ABBREV].data == NULL) + error (0, 0, "%s: .debug_abbrev not present", dso->filename); + else + error (0, 0, "%s: DWARF CU abbrev offset too large", + dso->filename); + return 1; + } + + if (cu_version < 5) + cu_ptr_size = read_8 (ptr); + + if (ptr_size == 0) + { + ptr_size = cu_ptr_size; + if (ptr_size != 4 && ptr_size != 8) + { + error (0, 0, "%s: Invalid DWARF pointer size %d", + dso->filename, ptr_size); + return 1; + } + } + else if (cu_ptr_size != ptr_size) + { + error (0, 0, "%s: DWARF pointer size differs between CUs", + dso->filename); + return 1; + } + + if (sec != &debug_sections[DEBUG_INFO]) + ptr += 12; /* Skip type_signature and type_offset. */ + + abbrev = read_abbrev (dso, + debug_sections[DEBUG_ABBREV].data + value); + if (abbrev == NULL) + return 1; + + while (ptr < endcu) + { + tag.entry = read_uleb128 (ptr); + if (tag.entry == 0) + continue; + t = htab_find_with_hash (abbrev, &tag, tag.entry); + if (t == NULL) + { + error (0, 0, "%s: Could not find DWARF abbreviation %d", + dso->filename, tag.entry); + htab_delete (abbrev); + return 1; + } + + ptr = edit_attributes (dso, ptr, t, phase); + if (ptr == NULL) + break; + } + + htab_delete (abbrev); + } + + return 0; +} + +/* Rebuild .debug_str. */ +static void +edit_dwarf2_any_str (DSO *dso, struct strings *strings, debug_section *secp) +{ + Strtab *strtab = strings->str_tab; + Elf_Data *strdata = secp->elf_data; + int strndx = secp->sec; + Elf_Scn *strscn = dso->scn[strndx]; + + /* Out with the old. */ + strdata->d_size = 0; + /* In with the new. */ + strdata = elf_newdata (strscn); + + /* We really should check whether we had enough memory, + but the old ebl version will just abort on out of + memory... */ + strtab_finalize (strtab, strdata); + secp->size = strdata->d_size; + strings->str_buf = strdata->d_buf; +} + static int edit_dwarf2 (DSO *dso) { @@ -1995,7 +2559,7 @@ edit_dwarf2 (DSO *dso) struct debug_section *debug_sec = &debug_sections[j]; if (debug_sections[j].data) { - if (j != DEBUG_MACRO) + if (j != DEBUG_MACRO && j != DEBUG_TYPES) { error (0, 0, "%s: Found two copies of %s section", dso->filename, name); @@ -2003,22 +2567,21 @@ edit_dwarf2 (DSO *dso) } else { - /* In relocatable files .debug_macro might - appear multiple times as COMDAT - section. */ + /* In relocatable files .debug_macro and .debug_types + might appear multiple times as COMDAT section. */ struct debug_section *sec; sec = calloc (sizeof (struct debug_section), 1); if (sec == NULL) error (1, errno, - "%s: Could not allocate more macro sections", - dso->filename); - sec->name = ".debug_macro"; + "%s: Could not allocate more %s sections", + dso->filename, name); + sec->name = name; - struct debug_section *macro_sec = debug_sec; - while (macro_sec->next != NULL) - macro_sec = macro_sec->next; + struct debug_section *multi_sec = debug_sec; + while (multi_sec->next != NULL) + multi_sec = multi_sec->next; - macro_sec->next = sec; + multi_sec->next = sec; debug_sec = sec; } } @@ -2055,23 +2618,23 @@ edit_dwarf2 (DSO *dso) + (dso->shdr[i].sh_type == SHT_RELA), debug_sections[j].name) == 0) { - if (j == DEBUG_MACRO) + if (j == DEBUG_MACRO || j == DEBUG_TYPES) { /* Pick the correct one. */ int rel_target = dso->shdr[i].sh_info; - struct debug_section *macro_sec = &debug_sections[j]; - while (macro_sec != NULL) + struct debug_section *multi_sec = &debug_sections[j]; + while (multi_sec != NULL) { - if (macro_sec->sec == rel_target) + if (multi_sec->sec == rel_target) { - macro_sec->relsec = i; + multi_sec->relsec = i; break; } - macro_sec = macro_sec->next; + multi_sec = multi_sec->next; } - if (macro_sec == NULL) - error (0, 1, "No .debug_macro reloc section: %s", - dso->filename); + if (multi_sec == NULL) + error (0, 1, "No %s reloc section: %s", + debug_sections[j].name, dso->filename); } else debug_sections[j].relsec = i; @@ -2100,388 +2663,338 @@ edit_dwarf2 (DSO *dso) return 1; } - if (debug_sections[DEBUG_INFO].data != NULL) - { - unsigned char *ptr, *endcu, *endsec; - uint32_t value; - htab_t abbrev; - struct abbrev_tag tag, *t; - int phase; - bool info_rel_updated = false; - bool macro_rel_updated = false; + if (debug_sections[DEBUG_INFO].data == NULL) + return 0; - for (phase = 0; phase < 2; phase++) - { - /* If we don't need to update anyhing, skip phase 1. */ - if (phase == 1 - && !need_strp_update - && !need_string_replacement - && !need_stmt_update) - break; + unsigned char *ptr, *endsec; + int phase; + bool info_rel_updated = false; + bool types_rel_updated = false; + bool macro_rel_updated = false; + bool line_rel_updated = false; - ptr = debug_sections[DEBUG_INFO].data; - setup_relbuf(dso, &debug_sections[DEBUG_INFO], &reltype); - rel_updated = false; - endsec = ptr + debug_sections[DEBUG_INFO].size; - while (ptr < endsec) - { - if (ptr + 11 > endsec) - { - error (0, 0, "%s: .debug_info CU header too small", - dso->filename); - return 1; - } + for (phase = 0; phase < 2; phase++) + { + /* If we don't need to update anyhing, skip phase 1. */ + if (phase == 1 + && !need_strp_update + && !need_line_strp_update + && !need_string_replacement + && !need_stmt_update) + break; - endcu = ptr + 4; - endcu += read_32 (ptr); - if (endcu == ptr + 0xffffffff) - { - error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); - return 1; - } + rel_updated = false; + if (edit_info (dso, phase, &debug_sections[DEBUG_INFO])) + return 1; - if (endcu > endsec) - { - error (0, 0, "%s: .debug_info too small", dso->filename); - return 1; - } + /* Remember whether any .debug_info relocations might need + to be updated. */ + info_rel_updated = rel_updated; - cu_version = read_16 (ptr); - if (cu_version != 2 && cu_version != 3 && cu_version != 4) - { - error (0, 0, "%s: DWARF version %d unhandled", dso->filename, - cu_version); - return 1; - } + rel_updated = false; + struct debug_section *types_sec = &debug_sections[DEBUG_TYPES]; + while (types_sec != NULL) + { + if (edit_info (dso, phase, types_sec)) + return 1; + types_sec = types_sec->next; + } - value = read_32_relocated (ptr); - if (value >= debug_sections[DEBUG_ABBREV].size) - { - if (debug_sections[DEBUG_ABBREV].data == NULL) - error (0, 0, "%s: .debug_abbrev not present", dso->filename); - else - error (0, 0, "%s: DWARF CU abbrev offset too large", - dso->filename); - return 1; - } + /* Remember whether any .debug_types relocations might need + to be updated. */ + types_rel_updated = rel_updated; + + /* We might have to recalculate/rewrite the debug_line + section. We need to do that before going into phase one + so we have all new offsets. We do this separately from + scanning the dirs/file names because the DW_AT_stmt_lists + might not be in order or skip some padding we might have + to (re)move. */ + if (phase == 0 && need_stmt_update) + { + edit_dwarf2_line (dso); - if (ptr_size == 0) + /* The line table programs will be moved + forward/backwards a bit in the new data. Update the + debug_line relocations to the new offsets. */ + int rndx = debug_sections[DEBUG_LINE].relsec; + if (rndx != 0) + { + LINE_REL *rbuf; + size_t rels; + Elf_Data *rdata = elf_getdata (dso->scn[rndx], NULL); + int rtype = dso->shdr[rndx].sh_type; + rels = dso->shdr[rndx].sh_size / dso->shdr[rndx].sh_entsize; + rbuf = malloc (rels * sizeof (LINE_REL)); + if (rbuf == NULL) + error (1, errno, "%s: Could not allocate line relocations", + dso->filename); + + /* Sort them by offset into section. */ + for (size_t i = 0; i < rels; i++) { - ptr_size = read_8 (ptr); - if (ptr_size != 4 && ptr_size != 8) + if (rtype == SHT_RELA) { - error (0, 0, "%s: Invalid DWARF pointer size %d", - dso->filename, ptr_size); - return 1; + GElf_Rela rela; + if (gelf_getrela (rdata, i, &rela) == NULL) + error (1, 0, "Couldn't get relocation: %s", + elf_errmsg (-1)); + rbuf[i].r_offset = rela.r_offset; + rbuf[i].ndx = i; } - } - else if (read_8 (ptr) != ptr_size) - { - error (0, 0, "%s: DWARF pointer size differs between CUs", - dso->filename); - return 1; - } - - abbrev = read_abbrev (dso, - debug_sections[DEBUG_ABBREV].data + value); - if (abbrev == NULL) - return 1; - - while (ptr < endcu) - { - tag.entry = read_uleb128 (ptr); - if (tag.entry == 0) - continue; - t = htab_find_with_hash (abbrev, &tag, tag.entry); - if (t == NULL) + else { - error (0, 0, "%s: Could not find DWARF abbreviation %d", - dso->filename, tag.entry); - htab_delete (abbrev); - return 1; + GElf_Rel rel; + if (gelf_getrel (rdata, i, &rel) == NULL) + error (1, 0, "Couldn't get relocation: %s", + elf_errmsg (-1)); + rbuf[i].r_offset = rel.r_offset; + rbuf[i].ndx = i; } - - ptr = edit_attributes (dso, ptr, t, phase); - if (ptr == NULL) - break; } + qsort (rbuf, rels, sizeof (LINE_REL), line_rel_cmp); - htab_delete (abbrev); - } - - /* Remember whether any .debug_info relocations might need - to be updated. */ - info_rel_updated = rel_updated; - - /* We might have to recalculate/rewrite the debug_line - section. We need to do that before going into phase one - so we have all new offsets. We do this separately from - scanning the dirs/file names because the DW_AT_stmt_lists - might not be in order or skip some padding we might have - to (re)move. */ - if (phase == 0 && need_stmt_update) - { - edit_dwarf2_line (dso); - - /* The line table programs will be moved - forward/backwards a bit in the new data. Update the - debug_line relocations to the new offsets. */ - int rndx = debug_sections[DEBUG_LINE].relsec; - if (rndx != 0) + size_t lndx = 0; + for (size_t i = 0; i < rels; i++) { - LINE_REL *rbuf; - size_t rels; - Elf_Data *rdata = elf_getdata (dso->scn[rndx], NULL); - int rtype = dso->shdr[rndx].sh_type; - rels = dso->shdr[rndx].sh_size / dso->shdr[rndx].sh_entsize; - rbuf = malloc (rels * sizeof (LINE_REL)); - if (rbuf == NULL) - error (1, errno, "%s: Could not allocate line relocations", - dso->filename); - - /* Sort them by offset into section. */ - for (size_t i = 0; i < rels; i++) + /* These relocations only happen in ET_REL files + and are section offsets. */ + GElf_Addr r_offset; + size_t ndx = rbuf[i].ndx; + + GElf_Rel rel; + GElf_Rela rela; + if (rtype == SHT_RELA) { - if (rtype == SHT_RELA) - { - GElf_Rela rela; - if (gelf_getrela (rdata, i, &rela) == NULL) - error (1, 0, "Couldn't get relocation: %s", - elf_errmsg (-1)); - rbuf[i].r_offset = rela.r_offset; - rbuf[i].ndx = i; - } - else - { - GElf_Rel rel; - if (gelf_getrel (rdata, i, &rel) == NULL) - error (1, 0, "Couldn't get relocation: %s", - elf_errmsg (-1)); - rbuf[i].r_offset = rel.r_offset; - rbuf[i].ndx = i; - } + if (gelf_getrela (rdata, ndx, &rela) == NULL) + error (1, 0, "Couldn't get relocation: %s", + elf_errmsg (-1)); + r_offset = rela.r_offset; } - qsort (rbuf, rels, sizeof (LINE_REL), line_rel_cmp); - - size_t lndx = 0; - for (size_t i = 0; i < rels; i++) + else { - /* These relocations only happen in ET_REL files - and are section offsets. */ - GElf_Addr r_offset; - size_t ndx = rbuf[i].ndx; - - GElf_Rel rel; - GElf_Rela rela; - if (rtype == SHT_RELA) - { - if (gelf_getrela (rdata, ndx, &rela) == NULL) - error (1, 0, "Couldn't get relocation: %s", - elf_errmsg (-1)); - r_offset = rela.r_offset; - } - else - { - if (gelf_getrel (rdata, ndx, &rel) == NULL) - error (1, 0, "Couldn't get relocation: %s", - elf_errmsg (-1)); - r_offset = rel.r_offset; - } + if (gelf_getrel (rdata, ndx, &rel) == NULL) + error (1, 0, "Couldn't get relocation: %s", + elf_errmsg (-1)); + r_offset = rel.r_offset; + } - while (lndx < dso->lines.used - && r_offset > (dso->lines.table[lndx].old_idx - + 4 - + dso->lines.table[lndx].unit_length)) - lndx++; + while (lndx < dso->lines.used + && r_offset > (dso->lines.table[lndx].old_idx + + 4 + + dso->lines.table[lndx].unit_length)) + lndx++; - if (lndx >= dso->lines.used) - error (1, 0, - ".debug_line relocation offset out of range"); + if (lndx >= dso->lines.used) + error (1, 0, + ".debug_line relocation offset out of range"); - /* Offset (pointing into the line program) moves - from old to new index including the header - size diff. */ - r_offset += (ssize_t)((dso->lines.table[lndx].new_idx - - dso->lines.table[lndx].old_idx) - + dso->lines.table[lndx].size_diff); + /* Offset (pointing into the line program) moves + from old to new index including the header + size diff. */ + r_offset += (ssize_t)((dso->lines.table[lndx].new_idx + - dso->lines.table[lndx].old_idx) + + dso->lines.table[lndx].size_diff); - if (rtype == SHT_RELA) - { - rela.r_offset = r_offset; - if (gelf_update_rela (rdata, ndx, &rela) == 0) - error (1, 0, "Couldn't update relocation: %s", - elf_errmsg (-1)); - } - else - { - rel.r_offset = r_offset; - if (gelf_update_rel (rdata, ndx, &rel) == 0) - error (1, 0, "Couldn't update relocation: %s", - elf_errmsg (-1)); - } + if (rtype == SHT_RELA) + { + rela.r_offset = r_offset; + if (gelf_update_rela (rdata, ndx, &rela) == 0) + error (1, 0, "Couldn't update relocation: %s", + elf_errmsg (-1)); + } + else + { + rel.r_offset = r_offset; + if (gelf_update_rel (rdata, ndx, &rel) == 0) + error (1, 0, "Couldn't update relocation: %s", + elf_errmsg (-1)); } - - elf_flagdata (rdata, ELF_C_SET, ELF_F_DIRTY); - free (rbuf); } + + elf_flagdata (rdata, ELF_C_SET, ELF_F_DIRTY); + free (rbuf); } + } - /* The .debug_macro section also contains offsets into the - .debug_str section and references to the .debug_line - tables, so we need to update those as well if we update - the strings or the stmts. */ - if ((need_strp_update || need_stmt_update) - && debug_sections[DEBUG_MACRO].data) + /* The .debug_macro section also contains offsets into the + .debug_str section and references to the .debug_line + tables, so we need to update those as well if we update + the strings or the stmts. */ + if ((need_strp_update || need_stmt_update) + && debug_sections[DEBUG_MACRO].data) + { + /* There might be multiple (COMDAT) .debug_macro sections. */ + struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; + while (macro_sec != NULL) { - /* There might be multiple (COMDAT) .debug_macro sections. */ - struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; - while (macro_sec != NULL) - { - setup_relbuf(dso, macro_sec, &reltype); - rel_updated = false; + setup_relbuf(dso, macro_sec, &reltype); + rel_updated = false; - ptr = macro_sec->data; - endsec = ptr + macro_sec->size; - int op = 0, macro_version, macro_flags; - int offset_len = 4, line_offset = 0; + ptr = macro_sec->data; + endsec = ptr + macro_sec->size; + int op = 0, macro_version, macro_flags; + int offset_len = 4, line_offset = 0; - while (ptr < endsec) + while (ptr < endsec) + { + if (!op) { - if (!op) - { - macro_version = read_16 (ptr); - macro_flags = read_8 (ptr); - if (macro_version < 4 || macro_version > 5) - error (1, 0, "unhandled .debug_macro version: %d", - macro_version); - if ((macro_flags & ~2) != 0) - error (1, 0, "unhandled .debug_macro flags: 0x%x", - macro_flags); - - offset_len = (macro_flags & 0x01) ? 8 : 4; - line_offset = (macro_flags & 0x02) ? 1 : 0; - - if (offset_len != 4) - error (0, 1, - "Cannot handle 8 byte macro offsets: %s", - dso->filename); - - /* Update the line_offset if it is there. */ - if (line_offset) - { - if (phase == 0) - ptr += offset_len; - else - { - size_t idx, new_idx; - idx = do_read_32_relocated (ptr); - new_idx = find_new_list_offs (&dso->lines, - idx); - write_32_relocated (ptr, new_idx); - } - } - } + macro_version = read_16 (ptr); + macro_flags = read_8 (ptr); + if (macro_version < 4 || macro_version > 5) + error (1, 0, "unhandled .debug_macro version: %d", + macro_version); + if ((macro_flags & ~2) != 0) + error (1, 0, "unhandled .debug_macro flags: 0x%x", + macro_flags); + + offset_len = (macro_flags & 0x01) ? 8 : 4; + line_offset = (macro_flags & 0x02) ? 1 : 0; + + if (offset_len != 4) + error (0, 1, + "Cannot handle 8 byte macro offsets: %s", + dso->filename); - op = read_8 (ptr); - if (!op) - continue; - switch(op) + /* Update the line_offset if it is there. */ + if (line_offset) { - case DW_MACRO_GNU_define: - case DW_MACRO_GNU_undef: - read_uleb128 (ptr); - ptr = ((unsigned char *) strchr ((char *) ptr, '\0') - + 1); - break; - case DW_MACRO_GNU_start_file: - read_uleb128 (ptr); - read_uleb128 (ptr); - break; - case DW_MACRO_GNU_end_file: - break; - case DW_MACRO_GNU_define_indirect: - case DW_MACRO_GNU_undef_indirect: - read_uleb128 (ptr); if (phase == 0) - { - size_t idx = read_32_relocated (ptr); - record_existing_string_entry_idx (&dso->strings, - idx); - } + ptr += offset_len; else { - struct stridxentry *entry; size_t idx, new_idx; idx = do_read_32_relocated (ptr); - entry = string_find_entry (&dso->strings, idx); - new_idx = strent_offset (entry->entry); + new_idx = find_new_list_offs (&dso->lines, + idx); write_32_relocated (ptr, new_idx); } - break; - case DW_MACRO_GNU_transparent_include: - ptr += offset_len; - break; - default: - error (1, 0, "Unhandled DW_MACRO op 0x%x", op); - break; } } - if (rel_updated) - macro_rel_updated = true; - macro_sec = macro_sec->next; + op = read_8 (ptr); + if (!op) + continue; + switch(op) + { + case DW_MACRO_GNU_define: + case DW_MACRO_GNU_undef: + read_uleb128 (ptr); + ptr = ((unsigned char *) strchr ((char *) ptr, '\0') + + 1); + break; + case DW_MACRO_GNU_start_file: + read_uleb128 (ptr); + read_uleb128 (ptr); + break; + case DW_MACRO_GNU_end_file: + break; + case DW_MACRO_GNU_define_indirect: + case DW_MACRO_GNU_undef_indirect: + read_uleb128 (ptr); + if (phase == 0) + { + size_t idx = read_32_relocated (ptr); + record_existing_string_entry_idx (false, dso, idx); + } + else + { + struct stridxentry *entry; + size_t idx, new_idx; + idx = do_read_32_relocated (ptr); + entry = string_find_entry (&dso->debug_str, idx); + new_idx = strent_offset (entry->entry); + write_32_relocated (ptr, new_idx); + } + break; + case DW_MACRO_GNU_transparent_include: + ptr += offset_len; + break; + default: + error (1, 0, "Unhandled DW_MACRO op 0x%x", op); + break; + } } - } - /* Same for the debug_str section. Make sure everything is - in place for phase 1 updating of debug_info - references. */ - if (phase == 0 && need_strp_update) - { - Strtab *strtab = dso->strings.str_tab; - Elf_Data *strdata = debug_sections[DEBUG_STR].elf_data; - int strndx = debug_sections[DEBUG_STR].sec; - Elf_Scn *strscn = dso->scn[strndx]; - - /* Out with the old. */ - strdata->d_size = 0; - /* In with the new. */ - strdata = elf_newdata (strscn); - - /* We really should check whether we had enough memory, - but the old ebl version will just abort on out of - memory... */ - strtab_finalize (strtab, strdata); - debug_sections[DEBUG_STR].size = strdata->d_size; - dso->strings.str_buf = strdata->d_buf; + if (rel_updated) + macro_rel_updated = true; + macro_sec = macro_sec->next; } - } - /* After phase 1 we might have rewritten the debug_info with - new strp, strings and/or linep offsets. */ - if (need_strp_update || need_string_replacement || need_stmt_update) - dirty_section (DEBUG_INFO); - if (need_strp_update || need_stmt_update) - dirty_section (DEBUG_MACRO); - if (need_stmt_update) - dirty_section (DEBUG_LINE); - /* Update any relocations addends we might have touched. */ - if (info_rel_updated) - update_rela_data (dso, &debug_sections[DEBUG_INFO]); + /* Now handle all the DWARF5 line tables, they contain strp + and/or line_strp entries that need to be registered/rewritten. */ + setup_relbuf(dso, &debug_sections[DEBUG_LINE], &reltype); + rel_updated = false; - if (macro_rel_updated) + /* edit_dwarf2_line will have set this up, unless there are no + moved/resized (DWARF4) lines. In which case we can just use + the original section data. new_idx will have been setup + correctly, even if it is the same as old_idx. */ + unsigned char *line_buf = (unsigned char *)dso->lines.line_buf; + if (line_buf == NULL) + line_buf = debug_sections[DEBUG_LINE].data; + for (int ldx = 0; ldx < dso->lines.used; ldx++) { - struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; - while (macro_sec != NULL) - { - update_rela_data (dso, macro_sec); - macro_sec = macro_sec->next; - } + struct line_table *t = &dso->lines.table[ldx]; + if (t->version >= 5) + read_dwarf5_line (dso, line_buf + t->new_idx, t, phase); } + if (rel_updated) + line_rel_updated = true; + + /* Same for the debug_str and debug_line_str sections. + Make sure everything is in place for phase 1 updating of debug_info + references. */ + if (phase == 0 && need_strp_update) + edit_dwarf2_any_str (dso, &dso->debug_str, + &debug_sections[DEBUG_STR]); + if (phase == 0 && need_line_strp_update) + edit_dwarf2_any_str (dso, &dso->debug_line_str, + &debug_sections[DEBUG_LINE_STR]); } + /* After phase 1 we might have rewritten the debug_info with + new strp, strings and/or linep offsets. */ + if (need_strp_update || need_line_strp_update + || need_string_replacement || need_stmt_update) { + dirty_section (DEBUG_INFO); + if (debug_sections[DEBUG_TYPES].data != NULL) + dirty_section (DEBUG_TYPES); + } + if (need_strp_update || need_stmt_update) + dirty_section (DEBUG_MACRO); + if (need_stmt_update || need_line_strp_update) + dirty_section (DEBUG_LINE); + + /* Update any relocations addends we might have touched. */ + if (info_rel_updated) + update_rela_data (dso, &debug_sections[DEBUG_INFO]); + if (types_rel_updated) + { + struct debug_section *types_sec = &debug_sections[DEBUG_TYPES]; + while (types_sec != NULL) + { + update_rela_data (dso, types_sec); + types_sec = types_sec->next; + } + } + + if (macro_rel_updated) + { + struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; + while (macro_sec != NULL) + { + update_rela_data (dso, macro_sec); + macro_sec = macro_sec->next; + } + } + + if (line_rel_updated) + update_rela_data (dso, &debug_sections[DEBUG_LINE]); + return 0; } @@ -2574,7 +3087,8 @@ fdopen_dso (int fd, const char *name) } dso->filename = (const char *) strdup (name); - setup_strings (&dso->strings); + setup_strings (&dso->debug_str); + setup_strings (&dso->debug_line_str); setup_lines (&dso->lines); return dso; @@ -2582,7 +3096,8 @@ error_out: if (dso) { free ((char *) dso->filename); - destroy_strings (&dso->strings); + destroy_strings (&dso->debug_str); + destroy_strings (&dso->debug_line_str); destroy_lines (&dso->lines); free (dso); } @@ -2863,7 +3378,9 @@ main (int argc, char *argv[]) in elfutils before 0.169 we will have to update and write out all section data if any data has changed (when ELF_F_LAYOUT was set). https://sourceware.org/bugzilla/show_bug.cgi?id=21199 */ - bool need_update = need_strp_update || need_stmt_update; + bool need_update = (need_strp_update + || need_line_strp_update + || need_stmt_update); #if !_ELFUTILS_PREREQ (0, 169) /* string replacements or build_id updates don't change section size. */ @@ -2935,10 +3452,12 @@ main (int argc, char *argv[]) GElf_Xword sec_size = shdr->sh_size; /* We might have changed the size (and content) of the - debug_str or debug_line section. */ + debug_str, debug_line_str or debug_line section. */ size_t secnum = elf_ndxscn (scn); if (secnum == debug_sections[DEBUG_STR].sec) sec_size = debug_sections[DEBUG_STR].size; + if (secnum == debug_sections[DEBUG_LINE_STR].sec) + sec_size = debug_sections[DEBUG_LINE_STR].size; if (secnum == debug_sections[DEBUG_LINE].sec) sec_size = debug_sections[DEBUG_LINE].size; @@ -3008,7 +3527,8 @@ main (int argc, char *argv[]) chmod (file, stat_buf.st_mode); free ((char *) dso->filename); - destroy_strings (&dso->strings); + destroy_strings (&dso->debug_str); + destroy_strings (&dso->debug_line_str); destroy_lines (&dso->lines); free (dso); @@ -3023,6 +3543,17 @@ main (int argc, char *argv[]) macro_sec = next; } + /* In case there were multiple (COMDAT) .debug_types sections, + free them. */ + struct debug_section *types_sec = &debug_sections[DEBUG_TYPES]; + types_sec = types_sec->next; + while (types_sec != NULL) + { + struct debug_section *next = types_sec->next; + free (types_sec); + types_sec = next; + } + poptFreeContext (optCon); return 0;