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