GPLv2 release
[centaur.git] / src / libelfu / modelops / dynlookup.c
1 /* This file is part of centaur.
2  *
3  * centaur is free software: you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License 2 as
5  * published by the Free Software Foundation.
6
7  * centaur is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11
12  * You should have received a copy of the GNU General Public License
13  * along with centaur.  If not, see <http://www.gnu.org/licenses/>.
14  */
15
16 #include <assert.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <libelfu/libelfu.h>
20
21
22 static void* subFindByName(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
23 {
24   char *name = (char*)aux1;
25   (void)aux2;
26
27   if (elfu_mScnName(me, ms)) {
28     if (!strcmp(elfu_mScnName(me, ms), name)) {
29       return ms;
30     }
31   }
32
33   /* Continue */
34   return NULL;
35 }
36
37
38 /* Hazard a guess where a function may be found in the PLT */
39 int elfu_mDynLookupPltAddrByName(ElfuElf *me, char *name, GElf_Addr *result)
40 {
41   ElfuScn *relplt;
42   ElfuScn *plt;
43   ElfuRel *rel;
44   GElf_Word j;
45
46   relplt = elfu_mScnForall(me, subFindByName, ".rel.plt", NULL);
47   if (!relplt) {
48     /* x86-64 uses .rela.plt instead */
49     relplt = elfu_mScnForall(me, subFindByName, ".rela.plt", NULL);
50   }
51   if (!relplt) {
52     ELFU_WARN("elfu_mDynLookupPltAddr: Could not find .rel.plt section in destination ELF.\n");
53     return -1;
54   }
55
56   plt = elfu_mScnForall(me, subFindByName, ".plt", NULL);
57   if (!plt) {
58     ELFU_WARN("elfu_mDynLookupPltAddr: Could not find .plt section in destination ELF.\n");
59     return -1;
60   }
61
62
63   /* Look up name. If the j-th entry in .rel.plt has the name we are
64    * looking for, we assume that the (j+1)-th entry in .plt is machine
65    * code to jump to the function.
66    * Your mileage may vary, but it works on my GNU binaries. */
67   assert(relplt->linkptr);
68   j = 0;
69   CIRCLEQ_FOREACH(rel, &relplt->reltab.rels, elem) {
70     GElf_Word i;
71     ElfuSym *sym;
72     char *symname;
73
74     j++;
75
76     /* We only consider runtime relocations for functions.
77      * Technically, these relocations write the functions' addresses
78      * to the GOT, not the PLT, after the dynamic linker has found
79      * them. */
80     if ((me->elfclass == ELFCLASS32 && rel->type != R_386_JMP_SLOT)
81         || (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_JUMP_SLOT)) {
82       continue;
83     }
84
85     /* Get the (rel->sym)-th symbol from the symbol table that
86      * .rel.plt points to. */
87     sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab.syms);
88     for (i = 1; i < rel->sym; i++) {
89       sym = CIRCLEQ_NEXT(sym, elem);
90     }
91
92     symname = ELFU_SYMSTR(relplt->linkptr, sym->name);
93     if (!strcmp(symname, name)) {
94       /* If this is the symbol we are looking for, then in an x86 binary
95        * the jump to the dynamic symbol is probably at offset (j * 16)
96        * from the start of the PLT, where j is the PLT entry and 16 is
97        * the number of bytes the machine code in a PLT entry take. */
98       GElf_Addr addr = plt->shdr.sh_addr + (16 * j);
99       ELFU_DEBUG("elfu_mDynLookupPltAddr: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
100       *result = addr;
101       return 0;
102     }
103   }
104
105   ELFU_WARN("elfu_mDynLookupPltAddr: Could not find symbol '%s' in destination ELF.\n", name);
106
107   return -1;
108 }
109
110
111
112 /* Hazard a guess where a global variable may be found in .bss,
113  * based on dynamic linking information in .rel.dyn */
114 int elfu_mDynLookupReldynAddrByName(ElfuElf *me, char *name, GElf_Addr *result)
115 {
116   ElfuScn *reldyn;
117   ElfuRel *rel;
118   GElf_Word j;
119
120   reldyn = elfu_mScnForall(me, subFindByName, ".rel.dyn", NULL);
121   if (!reldyn) {
122     /* x86-64 uses .rela.dyn instead */
123     reldyn = elfu_mScnForall(me, subFindByName, ".rela.dyn", NULL);
124   }
125   if (!reldyn) {
126     ELFU_WARN("elfu_mDynLookupReldynAddrByName: Could not find .rel.dyn section in destination ELF.\n");
127     return -1;
128   }
129
130
131   assert(reldyn->linkptr);
132   j = 0;
133   CIRCLEQ_FOREACH(rel, &reldyn->reltab.rels, elem) {
134     ElfuSym *sym;
135     char *symname;
136
137     j++;
138
139     /* We only consider COPY relocations for global variables here.
140      * Technically, these relocations write the variables' contents
141      * to .bss. */
142     if ((me->elfclass == ELFCLASS32 && rel->type != R_386_COPY)
143         || (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_COPY)) {
144       continue;
145     }
146
147     /* Get the (rel->sym)-th symbol from the symbol table that
148      * .rel.dyn points to. */
149     sym = elfu_mSymtabIndexToSym(reldyn->linkptr, rel->sym);
150     assert(sym);
151
152     symname = elfu_mSymtabSymToName(reldyn->linkptr, sym);
153     if (!strcmp(symname, name)) {
154       GElf_Addr addr = rel->offset;
155       ELFU_DEBUG("elfu_mDynLookupReldynAddrByName: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
156       *result = addr;
157       return 0;
158     }
159   }
160
161   ELFU_WARN("elfu_mDynLookupReldynAddrByName: Could not find or use symbol '%s' in destination ELF.\n", name);
162   ELFU_WARN("      NOTE: Only R_*_COPY relocations are resolved to global variables.\n");
163
164   return -1;
165 }