Do not insert STRTAB sections explicitly
[centaur.git] / src / model / relocate.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <libelfu/libelfu.h>
5
6
7 static void* subFindByName(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
8 {
9   char *name = (char*)aux1;
10   (void)aux2;
11
12   if (elfu_mScnName(me, ms)) {
13     if (!strcmp(elfu_mScnName(me, ms), name)) {
14       return ms;
15     }
16   }
17
18   /* Continue */
19   return NULL;
20 }
21
22 /* Hazard a guess where a function may be found in the PLT */
23 static GElf_Word pltLookupVal(ElfuElf *metarget, char *name)
24 {
25   ElfuScn *relplt;
26   ElfuScn *plt;
27   ElfuRel *rel;
28   GElf_Word j;
29
30   relplt = elfu_mScnForall(metarget, subFindByName, ".rel.plt", NULL);
31   if (!relplt) {
32     ELFU_WARN("dynsymLookupVal: Could not find .rel.plt section in destination ELF.\n");
33     return 0;
34   }
35
36   plt = elfu_mScnForall(metarget, subFindByName, ".plt", NULL);
37   if (!plt) {
38     ELFU_WARN("dynsymLookupVal: Could not find .plt section in destination ELF.\n");
39     return 0;
40   }
41
42
43   /* Look up name */
44   assert(relplt->reltab);
45   assert(relplt->linkptr);
46   assert(relplt->linkptr->symtab);
47   j = 0;
48   CIRCLEQ_FOREACH(rel, &relplt->reltab->rels, elem) {
49     GElf_Word i;
50     ElfuSym *sym;
51
52     j++;
53
54     if (rel->type != R_386_JMP_SLOT) {
55       continue;
56     }
57
58     sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab->syms);
59     for (i = 1; i < rel->sym; i++) {
60       sym = CIRCLEQ_NEXT(sym, elem);
61     }
62
63     if (!sym->name) {
64       continue;
65     }
66
67     if (!strcmp(sym->name, name)) {
68       /* If this is the symbol we are looking for, then in an x86 binary
69        * the jump to the dynamic symbol is probably at offset (j * 16)
70        * from the start of the PLT, where j is the PLT entry and 16 is
71        * the number of bytes the machine code in a PLT entry take. */
72       GElf_Addr addr = plt->shdr.sh_addr + (16 * j);
73       ELFU_DEBUG("dynsymLookupVal: Guessing symbol '%s' is in destination memory at %jx (PLT entry #%d).\n", name, addr, j);
74       return addr;
75     }
76   }
77
78   ELFU_WARN("dynsymLookupVal: Could not find symbol '%s' in destination ELF.\n", name);
79
80   return 0;
81 }
82
83
84 static GElf_Word symtabLookupVal(ElfuElf *metarget, ElfuScn *msst, GElf_Word entry)
85 {
86   GElf_Word i;
87   ElfuSym *sym;
88
89   assert(metarget);
90   assert(msst);
91   assert(msst->symtab);
92   assert(entry > 0);
93   assert(!CIRCLEQ_EMPTY(&msst->symtab->syms));
94
95   sym = CIRCLEQ_FIRST(&msst->symtab->syms);
96   for (i = 1; i < entry; i++) {
97     sym = CIRCLEQ_NEXT(sym, elem);
98   }
99
100   switch (sym->type) {
101     case STT_NOTYPE:
102     case STT_OBJECT:
103     case STT_FUNC:
104       if (sym->scnptr) {
105         assert(elfu_mScnByOldscn(metarget, sym->scnptr));
106         return elfu_mScnByOldscn(metarget, sym->scnptr)->shdr.sh_addr + sym->value;
107       } else if (sym->shndx == SHN_UNDEF) {
108         /* Look the symbol up in .dyn.plt. If it cannot be found there then
109          * .rel.dyn may need to be expanded with a COPY relocation so the
110          * dynamic linker fixes up the (TODO). */
111         return pltLookupVal(metarget, sym->name);
112       } else if (sym->shndx == SHN_ABS) {
113         return sym->value;
114       } else {
115         ELFU_WARN("symtabLookupVal: Symbol binding COMMON is not supported, using 0.\n");
116         return 0;
117       }
118       break;
119     case STT_SECTION:
120       assert(sym->scnptr);
121       assert(elfu_mScnByOldscn(metarget, sym->scnptr));
122       return elfu_mScnByOldscn(metarget, sym->scnptr)->shdr.sh_addr;
123     case STT_FILE:
124       ELFU_WARN("symtabLookupVal: Symbol type FILE is not supported, using 0.\n");
125       return 0;
126     default:
127       ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, sym->name);
128       return 0;
129   }
130 }
131
132 void elfu_mRelocate32(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt)
133 {
134   ElfuRel *rel;
135
136   assert(mstarget);
137   assert(msrt);
138
139   ELFU_DEBUG("Relocating in section of type %d size %jx\n",
140              mstarget->shdr.sh_type,
141              mstarget->shdr.sh_size);
142
143   CIRCLEQ_FOREACH(rel, &msrt->reltab->rels, elem) {
144     Elf32_Word *dest = (Elf32_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset);
145     Elf32_Word a = rel->addendUsed ? rel->addend : *dest;
146     Elf32_Addr p = mstarget->shdr.sh_addr + rel->offset;
147     Elf32_Addr s = symtabLookupVal(metarget, msrt->linkptr, rel->sym);
148     Elf32_Word newval = *dest;
149
150     switch(rel->type) {
151       case R_386_NONE:
152         ELFU_DEBUG("Skipping relocation: R_386_NONE");
153         break;
154       case R_386_32:
155         ELFU_DEBUG("Relocation: R_386_32");
156         newval = s + a;
157         break;
158       case R_386_PC32:
159         ELFU_DEBUG("Relocation: R_386_PC32");
160         newval = s + a - p;
161         break;
162
163       default:
164         ELFU_DEBUG("Skipping relocation: Unknown type %d", rel->type);
165     }
166     ELFU_DEBUG(", overwriting %x with %x.\n", *dest, newval);
167     *dest = newval;
168   }
169 }