When -g is set, cleanup_sections() preserves all sections after relocate, including the SHT_RELX relocation sections. cleanup_symbols() drops all STB_LOCAL symbols from the symbol table, which renumbers the remaining globals. The kept relas still hold r_info indices from the pre-cleanup numbering, but nothing rewrites them. A later elf_output_obj() call ends up in sort_syms() / update_relocs(), which allocates old_to_new_syms[] sized for the post-cleanup (smaller) symtab and then indexes it with the stale (larger) sym_index values from those rela entries, reading past the end of the array. Fix: when do_debug is set, build an old->new index map while re-adding globals, then walk every retained SHT_RELX section linked to the symtab and rewrite its r_info entries. Locals (and the undef sym at 0) map to 0 by tcc_mallocz, so relocations against dropped locals now refer to SHN_UNDEF - the best we can do without preserving the locals themselves. Reproducible with libtcc_debug.c if the program contains enough static helpers / string literals for the symtab to actually shrink; observed under valgrind in jitc (https://github.com/cyanogilvie/jitc) when compiling re2c-generated state machines with -g.
Unified · Split
| @@ -260,22 +260,56 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) · 259 unmodified lines | |||
| 260 | } | 260 | } |
| 261 | 261 | ||
| 262 | /* ------------------------------------------------------------- */ | 262 | /* ------------------------------------------------------------- */ |
| 263 | /* remove all STB_LOCAL symbols */ | 263 | /* remove all STB_LOCAL symbols. When do_debug is set, cleanup_sections() |
| 264 | keeps the relocation sections alive for a later elf_output_obj() call; | ||
| 265 | the r_info indices in those rela entries refer to the pre-cleanup | ||
| 266 | symbol numbering, so we must build an old->new map and rewrite them. | ||
| 267 | Without that, sort_syms() in elf_output_obj() allocates an | ||
| 268 | old_to_new_syms[] array sized for the post-cleanup (smaller) symtab | ||
| 269 | and indexes it with the stale (larger) sym_index values, reading off | ||
| 270 | the end of the array. */ | ||
| 264 | static void cleanup_symbols(TCCState *s1) | 271 | static void cleanup_symbols(TCCState *s1) |
| 265 | { | 272 | { |
| 266 | Section *s = s1->symtab; | 273 | Section *s = s1->symtab; |
| 267 | int sym_index, end_sym = s->data_offset / sizeof (ElfSym); | 274 | int sym_index, end_sym = s->data_offset / sizeof (ElfSym); |
| 275 | int *old_to_new = s1->do_debug | ||
| 276 | ? tcc_mallocz(end_sym * sizeof(int)) | ||
| 277 | : NULL; | ||
| 268 | /* reset symtab */ | 278 | /* reset symtab */ |
| 269 | s->data_offset = s->link->data_offset = s->hash->data_offset = 0; | 279 | s->data_offset = s->link->data_offset = s->hash->data_offset = 0; |
| 270 | init_symtab(s); | 280 | init_symtab(s); |
| 271 | /* add global symbols again */ | 281 | /* add global symbols again, recording the new index of each */ |
| 272 | for (sym_index = 1; sym_index < end_sym; ++sym_index) { | 282 | for (sym_index = 1; sym_index < end_sym; ++sym_index) { |
| 273 | ElfW(Sym) *sym = &((ElfW(Sym) *)s->data)[sym_index]; | 283 | ElfW(Sym) *sym = &((ElfW(Sym) *)s->data)[sym_index]; |
| 274 | const char *name = (char *)s->link->data + sym->st_name; | 284 | const char *name = (char *)s->link->data + sym->st_name; |
| 285 | int new_idx; | ||
| 275 | if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) | 286 | if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) |
| 276 | continue; | 287 | continue; |
| 277 | //printf("sym %s\n", name); | 288 | //printf("sym %s\n", name); |
| 278 | put_elf_sym(s, sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx, name); | 289 | new_idx = put_elf_sym(s, sym->st_value, sym->st_size, |
| 290 | sym->st_info, sym->st_other, sym->st_shndx, name); | ||
| 291 | if (old_to_new) | ||
| 292 | old_to_new[sym_index] = new_idx; | ||
| 293 | } | ||
| 294 | if (old_to_new) { | ||
| 295 | int i; | ||
| 296 | for (i = 1; i < s1->nb_sections; i++) { | ||
| 297 | Section *sr = s1->sections[i]; | ||
| 298 | ElfW_Rel *rel; | ||
| 299 | if (sr->sh_type != SHT_RELX || sr->link != s) | ||
| 300 | continue; | ||
| 301 | for_each_elem(sr, 0, rel, ElfW_Rel) { | ||
| 302 | int old = ELFW(R_SYM)(rel->r_info); | ||
| 303 | int type = ELFW(R_TYPE)(rel->r_info); | ||
| 304 | /* Locals (and the undef sym at 0) map to 0 by | ||
| 305 | tcc_mallocz; relocations against dropped locals now | ||
| 306 | refer to SHN_UNDEF, which is the best we can do | ||
| 307 | without preserving the locals themselves. */ | ||
| 308 | int new_idx = (old > 0 && old < end_sym) ? old_to_new[old] : 0; | ||
| 309 | rel->r_info = ELFW(R_INFO)(new_idx, type); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | tcc_free(old_to_new); | ||
| 279 | } | 313 | } |
| 280 | } | 314 | } |
| 281 | 315 | ||