Lookup dynamically linked global variables.
[centaur.git] / src / libelfu / modelops / dynlookup.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
23 /* Hazard a guess where a function may be found in the PLT */
24 int elfu_mDynLookupPltAddrByName(ElfuElf *me, char *name, GElf_Addr *result)
25 {
26   ElfuScn *relplt;
27   ElfuScn *plt;
28   ElfuRel *rel;
29   GElf_Word j;
30
31   relplt = elfu_mScnForall(me, subFindByName, ".rel.plt", NULL);
32   if (!relplt) {
33     /* x86-64 uses .rela.plt instead */
34     relplt = elfu_mScnForall(me, subFindByName, ".rela.plt", NULL);
35   }
36   if (!relplt) {
37     ELFU_WARN("elfu_mDynLookupPltAddr: Could not find .rel.plt section in destination ELF.\n");
38     return -1;
39   }
40
41   plt = elfu_mScnForall(me, subFindByName, ".plt", NULL);
42   if (!plt) {
43     ELFU_WARN("elfu_mDynLookupPltAddr: Could not find .plt section in destination ELF.\n");
44     return -1;
45   }
46
47
48   /* Look up name. If the j-th entry in .rel.plt has the name we are
49    * looking for, we assume that the (j+1)-th entry in .plt is machine
50    * code to jump to the function.
51    * Your mileage may vary, but it works on my GNU binaries. */
52   assert(relplt->linkptr);
53   j = 0;
54   CIRCLEQ_FOREACH(rel, &relplt->reltab.rels, elem) {
55     GElf_Word i;
56     ElfuSym *sym;
57     char *symname;
58
59     j++;
60
61     /* We only consider runtime relocations for functions.
62      * Technically, these relocations write the functions' addresses
63      * to the GOT, not the PLT, after the dynamic linker has found
64      * them. */
65     if ((me->elfclass == ELFCLASS32 && rel->type != R_386_JMP_SLOT)
66         || (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_JUMP_SLOT)) {
67       continue;
68     }
69
70     /* Get the (rel->sym)-th symbol from the symbol table that
71      * .rel.plt points to. */
72     sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab.syms);
73     for (i = 1; i < rel->sym; i++) {
74       sym = CIRCLEQ_NEXT(sym, elem);
75     }
76
77     symname = ELFU_SYMSTR(relplt->linkptr, sym->name);
78     if (!strcmp(symname, name)) {
79       /* If this is the symbol we are looking for, then in an x86 binary
80        * the jump to the dynamic symbol is probably at offset (j * 16)
81        * from the start of the PLT, where j is the PLT entry and 16 is
82        * the number of bytes the machine code in a PLT entry take. */
83       GElf_Addr addr = plt->shdr.sh_addr + (16 * j);
84       ELFU_DEBUG("elfu_mDynLookupPltAddr: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
85       *result = addr;
86       return 0;
87     }
88   }
89
90   ELFU_WARN("elfu_mDynLookupPltAddr: Could not find symbol '%s' in destination ELF.\n", name);
91
92   return -1;
93 }
94
95
96
97 /* Hazard a guess where a global variable may be found in .bss,
98  * based on dynamic linking information in .rel.dyn */
99 int elfu_mDynLookupReldynAddrByName(ElfuElf *me, char *name, GElf_Addr *result)
100 {
101   ElfuScn *reldyn;
102   ElfuRel *rel;
103   GElf_Word j;
104
105   reldyn = elfu_mScnForall(me, subFindByName, ".rel.dyn", NULL);
106   if (!reldyn) {
107     /* x86-64 uses .rela.dyn instead */
108     reldyn = elfu_mScnForall(me, subFindByName, ".rela.dyn", NULL);
109   }
110   if (!reldyn) {
111     ELFU_WARN("elfu_mDynLookupReldynAddrByName: Could not find .rel.dyn section in destination ELF.\n");
112     return -1;
113   }
114
115
116   assert(reldyn->linkptr);
117   j = 0;
118   CIRCLEQ_FOREACH(rel, &reldyn->reltab.rels, elem) {
119     ElfuSym *sym;
120     char *symname;
121
122     j++;
123
124     /* We only consider COPY relocations for global variables here.
125      * Technically, these relocations write the variables' contents
126      * to .bss. */
127     if ((me->elfclass == ELFCLASS32 && rel->type != R_386_COPY)
128         || (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_COPY)) {
129       continue;
130     }
131
132     /* Get the (rel->sym)-th symbol from the symbol table that
133      * .rel.dyn points to. */
134     sym = elfu_mSymtabIndexToSym(reldyn->linkptr, rel->sym);
135     assert(sym);
136
137     symname = elfu_mSymtabSymToName(reldyn->linkptr, sym);
138     if (!strcmp(symname, name)) {
139       GElf_Addr addr = rel->offset;
140       ELFU_DEBUG("elfu_mDynLookupReldynAddrByName: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
141       *result = addr;
142       return 0;
143     }
144   }
145
146   ELFU_WARN("elfu_mDynLookupReldynAddrByName: Could not find or use symbol '%s' in destination ELF.\n", name);
147   ELFU_WARN("      NOTE: Only R_*_COPY relocations are resolved to global variables.\n");
148
149   return -1;
150 }