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