diff --git a/tcc.h b/tcc.h index fb104fa5..93544f0a 100644 --- a/tcc.h +++ b/tcc.h @@ -1384,6 +1384,8 @@ ST_FUNC void tcc_add_runtime(TCCState *s1); ST_FUNC void build_got_entries(TCCState *s1); ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc); +ST_FUNC void squeeze_multi_relocs(Section *sec, size_t oldrelocoffset); + ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err); #if defined TCC_IS_NATIVE || defined TCC_TARGET_PE ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name); diff --git a/tccelf.c b/tccelf.c index c97d7423..003797c9 100644 --- a/tccelf.c +++ b/tccelf.c @@ -535,6 +535,43 @@ ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, put_elf_reloca(symtab, s, offset, type, symbol, 0); } +/* Remove relocations for section S->reloc starting at oldrelocoffset + that are to the same place, retaining the last of them. As side effect + the relocations are sorted. Possibly reduces the number of relocs. */ +ST_FUNC void squeeze_multi_relocs(Section *s, size_t oldrelocoffset) +{ + Section *sr = s->reloc; + ElfW_Rel *r, *dest; + ssize_t a; + ElfW(Addr) addr; + + if (oldrelocoffset + sizeof(*r) >= sr->data_offset) + return; + /* The relocs we're dealing with are the result of initializer parsing. + So they will be mostly in order and there aren't many of them. + Secondly we need a stable sort (which qsort isn't). We use + a simple insertion sort. */ + for (a = oldrelocoffset + sizeof(*r); a < sr->data_offset; a += sizeof(*r)) { + ssize_t i = a - sizeof(*r); + addr = ((ElfW_Rel*)(sr->data + a))->r_offset; + for (; i >= (ssize_t)oldrelocoffset && + ((ElfW_Rel*)(sr->data + i))->r_offset > addr; i -= sizeof(*r)) { + ElfW_Rel tmp = *(ElfW_Rel*)(sr->data + a); + *(ElfW_Rel*)(sr->data + a) = *(ElfW_Rel*)(sr->data + i); + *(ElfW_Rel*)(sr->data + i) = tmp; + } + } + + r = (ElfW_Rel*)(sr->data + oldrelocoffset); + dest = r; + for (; r < (ElfW_Rel*)(sr->data + sr->data_offset); r++) { + if (dest->r_offset != r->r_offset) + dest++; + *dest = *r; + } + sr->data_offset = (unsigned char*)dest - sr->data + sizeof(*r); +} + /* put stab debug information */ ST_FUNC void put_stabs(const char *str, int type, int other, int desc, diff --git a/tccgen.c b/tccgen.c index 5c2acf8b..5c749712 100644 --- a/tccgen.c +++ b/tccgen.c @@ -6019,6 +6019,12 @@ static void init_putv(CType *type, Section *sec, unsigned long c) continue; if (rel->r_offset < esym->st_value) break; + /* Note: if the same fields are initialized multiple + times (possible with designators) then we possibly + add multiple relocations for the same offset here. + That would lead to wrong code, the last reloc needs + to win. We clean this up later after the whole + initializer is parsed. */ put_elf_reloca(symtab_section, sec, c + rel->r_offset - esym->st_value, ELFW(R_TYPE)(rel->r_info), @@ -6602,7 +6608,12 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, vla_sp_loc = addr; vlas_in_scope++; } else if (has_init) { + size_t oldreloc_offset = 0; + if (sec && sec->reloc) + oldreloc_offset = sec->reloc->data_offset; decl_initializer(type, sec, addr, 1, 0); + if (sec && sec->reloc) + squeeze_multi_relocs(sec, oldreloc_offset); /* patch flexible array member size back to -1, */ /* for possible subsequent similar declarations */ if (flexible_array) diff --git a/tests/tests2/86-struct-init.c b/tests/tests2/86-struct-init.c index 8d277194..fd212ba9 100644 --- a/tests/tests2/86-struct-init.c +++ b/tests/tests2/86-struct-init.c @@ -199,6 +199,25 @@ void test_compound_with_relocs (void) p = local_wrap[1].func; p(); } +void sys_ni(void) { printf("ni\n"); } +void sys_one(void) { printf("one\n"); } +void sys_two(void) { printf("two\n"); } +void sys_three(void) { printf("three\n"); } +typedef void (*fptr)(void); +const fptr table[3] = { + [0 ... 2] = &sys_ni, + [0] = sys_one, + [1] = sys_two, + [2] = sys_three, +}; + +void test_multi_relocs(void) +{ + int i; + for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) + table[i](); +} + int main() { print(ce); @@ -224,5 +243,6 @@ int main() foo(&gw, &phdr); //printf("q: %s\n", q); test_compound_with_relocs(); + test_multi_relocs(); return 0; } diff --git a/tests/tests2/86-struct-init.expect b/tests/tests2/86-struct-init.expect index 1498a3d3..adda76d4 100644 --- a/tests/tests2/86-struct-init.expect +++ b/tests/tests2/86-struct-init.expect @@ -35,3 +35,6 @@ lv2: 1 2 3 4 68 69 68 69 0 0 0 0 0 0 0 0 0 0 0 0 2f 30 lv3: 7 8 9 a 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32 lt2: 0 9 9 9 43 43 43 43 42 42 42 0 0 0 0 0 1 flow: 9 8 7 6 0 0 0 0 0 0 0 0 0 0 0 0 6 5 4 3 0 0 0 0 0 0 0 0 0 0 0 0 +one +two +three