Basic detour support
[centaur.git] / src / modelops / symtab.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 *me, char *name)
24 {
25   ElfuScn *relplt;
26   ElfuScn *plt;
27   ElfuRel *rel;
28   GElf_Word j;
29
30   relplt = elfu_mScnForall(me, 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(me, 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. If the j-th entry in .rel.plt has the name we are
44    * looking for, we assume that the (j+1)-th entry in .plt is machine
45    * code to jump to the function.
46    * Your mileage may vary, but it works on my GNU binaries. */
47   assert(relplt->linkptr);
48   j = 0;
49   CIRCLEQ_FOREACH(rel, &relplt->reltab.rels, elem) {
50     GElf_Word i;
51     ElfuSym *sym;
52     char *symname;
53
54     j++;
55
56     /* We only consider runtime relocations for functions.
57      * Technically, these relocations write the functions' addresses
58      * to the GOT, not the PLT, after the dynamic linker has found
59      * them. */
60     if ((me->elfclass == ELFCLASS32 && rel->type != R_386_JMP_SLOT)
61         || (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_JUMP_SLOT)) {
62       continue;
63     }
64
65     /* Get the (rel->sym)-th symbol from the symbol table that
66      * .rel.plt points to. */
67     sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab.syms);
68     for (i = 1; i < rel->sym; i++) {
69       sym = CIRCLEQ_NEXT(sym, elem);
70     }
71
72     symname = ELFU_SYMSTR(relplt->linkptr, sym->name);
73     if (!strcmp(symname, name)) {
74       /* If this is the symbol we are looking for, then in an x86 binary
75        * the jump to the dynamic symbol is probably at offset (j * 16)
76        * from the start of the PLT, where j is the PLT entry and 16 is
77        * the number of bytes the machine code in a PLT entry take. */
78       GElf_Addr addr = plt->shdr.sh_addr + (16 * j);
79       ELFU_DEBUG("dynsymLookupVal: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
80       return addr;
81     }
82   }
83
84   ELFU_WARN("dynsymLookupVal: Could not find symbol '%s' in destination ELF.\n", name);
85
86   return 0;
87 }
88
89
90
91 /* Look up a value in the symbol table section *msst which is in *me.
92  * If it is not found there, see if we can find it in *me's PLT. */
93 GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry)
94 {
95   GElf_Word i;
96   ElfuSym *sym;
97   char *symname;
98
99   assert(me);
100   assert(msst);
101   assert(entry > 0);
102   assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
103
104   sym = CIRCLEQ_FIRST(&msst->symtab.syms);
105   for (i = 1; i < entry; i++) {
106     sym = CIRCLEQ_NEXT(sym, elem);
107   }
108   symname = ELFU_SYMSTR(msst, sym->name);
109
110   switch (sym->type) {
111     case STT_NOTYPE:
112     case STT_OBJECT:
113     case STT_FUNC:
114       if (sym->scnptr) {
115         ElfuScn *newscn = elfu_mScnByOldscn(me, sym->scnptr);
116         assert(newscn);
117         return newscn->shdr.sh_addr + sym->value;
118       } else if (sym->shndx == SHN_UNDEF) {
119         /* Look the symbol up in .rel.plt. If it cannot be found there then
120          * .rel.dyn may need to be expanded with a COPY relocation so the
121          * dynamic linker fixes up the (TODO). */
122         return pltLookupVal(me, symname);
123       } else if (sym->shndx == SHN_ABS) {
124         return sym->value;
125       } else {
126         ELFU_WARN("symtabLookupVal: Symbol binding COMMON is not supported, using 0.\n");
127         return 0;
128       }
129       break;
130     case STT_SECTION:
131       assert(sym->scnptr);
132       assert(elfu_mScnByOldscn(me, sym->scnptr));
133       return elfu_mScnByOldscn(me, sym->scnptr)->shdr.sh_addr;
134     case STT_FILE:
135       ELFU_WARN("symtabLookupVal: Symbol type FILE is not supported, using 0.\n");
136       return 0;
137     default:
138       ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, symname);
139       return 0;
140   }
141 }
142
143
144 /* Look up a value in the symbol table section *msst which is in *me. */
145 GElf_Word elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name)
146 {
147   ElfuSym *sym;
148
149   assert(me);
150   assert(msst);
151   assert(name);
152   assert(strlen(name) > 0);
153   assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
154
155   CIRCLEQ_FOREACH(sym, &msst->symtab.syms, elem) {
156     char *symname = ELFU_SYMSTR(msst, sym->name);
157
158     if (!strcmp(symname, name)) {
159       goto SYMBOL_FOUND;
160     }
161   }
162   return 0;
163
164
165   SYMBOL_FOUND:
166
167   switch (sym->type) {
168     case STT_NOTYPE:
169     case STT_OBJECT:
170     case STT_FUNC:
171       if (sym->scnptr) {
172         GElf_Addr a = sym->value;
173         a += me->ehdr.e_type == ET_REL ? sym->scnptr->shdr.sh_addr : 0;
174         return a;
175       } else if (sym->shndx == SHN_UNDEF) {
176         return 0;
177       } else if (sym->shndx == SHN_ABS) {
178         return sym->value;
179       } else {
180         ELFU_WARN("elfu_mSymtabLookupAddrByName: Symbol binding COMMON is not supported, using 0.\n");
181         return 0;
182       }
183       break;
184     default:
185       return 0;
186   }
187 }
188
189
190
191 /* Convert symtab from memory model to elfclass specific format */
192 void elfu_mSymtabFlatten(ElfuElf *me)
193 {
194   ElfuSym *sym;
195   size_t numsyms = 0;
196
197   elfu_mLayoutAuto(me);
198
199   /* Update section indexes and count symbols */
200   CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
201     if (sym->scnptr) {
202       sym->shndx = elfu_mScnIndex(me, sym->scnptr);
203     }
204
205     numsyms++;
206   }
207
208   /* Copy symbols to elfclass-specific format */
209   if (me->elfclass == ELFCLASS32) {
210     size_t newsize = (numsyms + 1) * sizeof(Elf32_Sym);
211     size_t i;
212
213     if (me->symtab->data.d_buf) {
214       free(me->symtab->data.d_buf);
215     }
216     me->symtab->data.d_buf = malloc(newsize);
217     assert(me->symtab->data.d_buf);
218
219     me->symtab->data.d_size = newsize;
220     me->symtab->shdr.sh_size = newsize;
221     memset(me->symtab->data.d_buf, 0, newsize);
222
223     i = 1;
224     CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
225       Elf32_Sym *es = ((Elf32_Sym*)me->symtab->data.d_buf) + i;
226
227       es->st_name = sym->name;
228       es->st_value = sym->value;
229       es->st_size = sym->size;
230       es->st_info = ELF32_ST_INFO(sym->bind, sym->type);
231       es->st_other = sym->other;
232       es->st_shndx = sym->shndx;
233
234       i++;
235     }
236   } else if (me->elfclass == ELFCLASS64) {
237     size_t newsize = (numsyms + 1) * sizeof(Elf64_Sym);
238     size_t i;
239
240     if (me->symtab->data.d_buf) {
241       free(me->symtab->data.d_buf);
242     }
243     me->symtab->data.d_buf = malloc(newsize);
244     assert(me->symtab->data.d_buf);
245
246     me->symtab->data.d_size = newsize;
247     me->symtab->shdr.sh_size = newsize;
248     memset(me->symtab->data.d_buf, 0, newsize);
249
250     i = 1;
251     CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
252       Elf64_Sym *es = ((Elf64_Sym*)me->symtab->data.d_buf) + i;
253
254       es->st_name = sym->name;
255       es->st_value = sym->value;
256       es->st_size = sym->size;
257       es->st_info = ELF64_ST_INFO(sym->bind, sym->type);
258       es->st_other = sym->other;
259       es->st_shndx = sym->shndx;
260
261       i++;
262     }
263   } else {
264     /* Unknown elfclass */
265     assert(0);
266   }
267
268   elfu_mLayoutAuto(me);
269 }