aboutsummaryrefslogtreecommitdiff
path: root/tccelf.c
diff options
context:
space:
mode:
authorMichael Matz <matz@suse.de>2016-10-08 02:44:17 +0200
committerMichael Matz <matz@suse.de>2016-12-15 17:49:53 +0100
commitddd461dcc866d17f1c89137dc275ce1da95e7be2 (patch)
tree1585ee439f895aeb049e8d3792748451ca768d86 /tccelf.c
parentf081acbfba84ffdf1e479f932906bf10f88cd1c2 (diff)
downloadtinycc-ddd461dcc866d17f1c89137dc275ce1da95e7be2.tar.gz
tinycc-ddd461dcc866d17f1c89137dc275ce1da95e7be2.tar.bz2
Fix initializing members multiple times
When intializing members where the initializer needs relocations and the member is initialized multiple times we can't allow that to lead to multiple relocations to the same place. The last one must win.
Diffstat (limited to 'tccelf.c')
-rw-r--r--tccelf.c37
1 files changed, 37 insertions, 0 deletions
diff --git a/tccelf.c b/tccelf.c
index c97d742..003797c 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,