GPLv2 release
[centaur.git] / src / libelfu / modelops / relocate.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 /* Apply relocation information from section *msrt to data in
23  * section *mstarget (which is stored in *metarget). */
24 int elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt)
25 {
26   ElfuRel *rel;
27   ElfuSym *sym;
28
29   assert(mstarget);
30   assert(msrt);
31
32   ELFU_DEBUG("Relocating in section of type %u size %x\n",
33              mstarget->shdr.sh_type,
34              (unsigned)mstarget->shdr.sh_size);
35
36   CIRCLEQ_FOREACH(rel, &msrt->reltab.rels, elem) {
37     Elf32_Word *dest32 = (Elf32_Word*)(mstarget->databuf + rel->offset);
38     Elf64_Word *dest64 = (Elf64_Word*)(mstarget->databuf + rel->offset);
39     GElf_Addr s;
40     int haveSymval = 0;
41
42     sym = elfu_mSymtabIndexToSym(msrt->linkptr, rel->sym);
43     assert(sym);
44
45     haveSymval = !elfu_mSymtabLookupSymToAddr(metarget,
46                                               msrt->linkptr,
47                                               sym,
48                                               &s);
49     if (!haveSymval) {
50       if (sym->shndx == SHN_UNDEF) {
51         haveSymval = !elfu_mDynLookupPltAddrByName(metarget,
52                                      elfu_mSymtabSymToName(msrt->linkptr, sym),
53                                      &s);
54         if (!haveSymval) {
55           haveSymval = !elfu_mDynLookupReldynAddrByName(metarget,
56                                        elfu_mSymtabSymToName(msrt->linkptr, sym),
57                                        &s);
58         }
59       }
60     }
61
62     if (metarget->elfclass == ELFCLASS32) {
63       Elf32_Word a32 = rel->addendUsed ? rel->addend : *dest32;
64       Elf32_Addr p32 = mstarget->shdr.sh_addr + rel->offset;
65       Elf32_Addr s32 = (Elf32_Addr)s;
66
67       switch(metarget->ehdr.e_machine) {
68         case EM_386:
69           switch(rel->type) {
70             case R_386_NONE:
71               ELFU_DEBUG("Skipping relocation: R_386_NONE\n");
72               break;
73             case R_386_32:
74               if (!haveSymval) {
75                 goto MISSINGSYM;
76               }
77               *dest32 = s32 + a32;
78               break;
79             case R_386_PC32:
80               if (!haveSymval) {
81                 goto MISSINGSYM;
82               }
83               *dest32 = s32 + a32 - p32;
84               break;
85             default:
86               ELFU_DEBUG("elfu_mRelocate: Skipping unknown relocation type %d\n", rel->type);
87           }
88           break;
89         default:
90           ELFU_WARN("elfu_mRelocate: Unknown machine type. Aborting.\n");
91       }
92     } else if (metarget->elfclass == ELFCLASS64) {
93       Elf64_Word a64 = rel->addendUsed ? rel->addend : *dest64;
94       Elf64_Addr p64 = mstarget->shdr.sh_addr + rel->offset;
95       Elf64_Addr s64 = (Elf64_Addr)s;
96
97       switch(metarget->ehdr.e_machine) {
98         case EM_X86_64:
99           switch(rel->type) {
100             case R_X86_64_NONE:
101               ELFU_DEBUG("Skipping relocation: R_386_NONE\n");
102               break;
103             case R_X86_64_64:
104               if (!haveSymval) {
105                 goto MISSINGSYM;
106               }
107               *dest64 = s64 + a64;
108               break;
109             case R_X86_64_PC32:
110               if (!haveSymval) {
111                 goto MISSINGSYM;
112               }
113               *dest32 = s64 + a64 - p64;
114               break;
115             case R_X86_64_32:
116               if (!haveSymval) {
117                 goto MISSINGSYM;
118               }
119               *dest32 = s64 + a64;
120               break;
121             default:
122               ELFU_DEBUG("elfu_mRelocate: Skipping unknown relocation type %d", rel->type);
123           }
124           break;
125         default:
126           ELFU_WARN("elfu_mRelocate: Unknown machine type. Aborting.\n");
127       }
128     }
129   }
130
131   return 0;
132
133   MISSINGSYM:
134   ELFU_WARN("elfu_mRelocate: Could not resolve symbol %s. Aborting.\n",
135             elfu_mSymtabSymToName(msrt->linkptr, sym));
136   return -1;
137
138 }