aboutsummaryrefslogtreecommitdiff
path: root/tccelf.c
diff options
context:
space:
mode:
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,