GPLv2 release
[centaur.git] / src / libelfu / model / symtab.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 int elfu_mSymtabLookupSymToAddr(ElfuElf *me, ElfuScn *msst, ElfuSym *sym, GElf_Addr *result)
23 {
24   char *symname = ELFU_SYMSTR(msst, sym->name);
25
26   switch (sym->type) {
27     case STT_NOTYPE:
28     case STT_OBJECT:
29     case STT_FUNC:
30       if (sym->shndx == SHN_UNDEF) {
31         return -1;
32       } else if (sym->shndx == SHN_ABS) {
33         *result = sym->value;
34         return 0;
35       } else if (sym->shndx == SHN_COMMON) {
36         return -1;
37       } else {
38         ElfuScn *newscn;
39
40         assert (sym->scnptr);
41         newscn = elfu_mScnByOldscn(me, sym->scnptr);
42         assert(newscn);
43         *result = newscn->shdr.sh_addr + sym->value;
44         return 0;
45       }
46       break;
47     case STT_SECTION:
48       assert(sym->scnptr);
49       assert(elfu_mScnByOldscn(me, sym->scnptr));
50       *result = elfu_mScnByOldscn(me, sym->scnptr)->shdr.sh_addr;
51       return 0;
52     case STT_FILE:
53       ELFU_WARN("elfu_mSymtabLookupSymToAddr: Symbol type FILE is not supported.\n");
54       return -1;
55     default:
56       ELFU_WARN("elfu_mSymtabLookupSymToAddr: Unknown symbol type %d for %s.\n", sym->type, symname);
57       return -1;
58   }
59 }
60
61
62
63 char* elfu_mSymtabSymToName(ElfuScn *msst, ElfuSym *sym)
64 {
65   assert(msst);
66   assert(sym);
67
68   return ELFU_SYMSTR(msst, sym->name);
69 }
70
71
72
73 ElfuSym* elfu_mSymtabIndexToSym(ElfuScn *msst, GElf_Word entry)
74 {
75   GElf_Word i;
76   ElfuSym *sym;
77
78   assert(msst);
79   assert(entry > 0);
80   assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
81
82   sym = CIRCLEQ_FIRST(&msst->symtab.syms);
83   for (i = 1; i < entry; i++) {
84     sym = CIRCLEQ_NEXT(sym, elem);
85   }
86
87   return sym;
88 }
89
90
91
92 /* Look up a value in the symbol table section *msst which is in *me. */
93 GElf_Addr elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name)
94 {
95   ElfuSym *sym;
96
97   assert(me);
98   assert(msst);
99   assert(name);
100   assert(strlen(name) > 0);
101   assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
102
103   CIRCLEQ_FOREACH(sym, &msst->symtab.syms, elem) {
104     char *symname = ELFU_SYMSTR(msst, sym->name);
105
106     if (!strcmp(symname, name)) {
107       goto SYMBOL_FOUND;
108     }
109   }
110   return 0;
111
112
113   SYMBOL_FOUND:
114
115   switch (sym->type) {
116     case STT_NOTYPE:
117     case STT_OBJECT:
118     case STT_FUNC:
119       if (sym->scnptr) {
120         GElf_Addr a = sym->value;
121         a += me->ehdr.e_type == ET_REL ? sym->scnptr->shdr.sh_addr : 0;
122         return a;
123       } else if (sym->shndx == SHN_UNDEF) {
124         return 0;
125       } else if (sym->shndx == SHN_ABS) {
126         return sym->value;
127       } else {
128         ELFU_WARN("elfu_mSymtabLookupAddrByName: Symbol binding COMMON is not supported, using 0.\n");
129         return 0;
130       }
131       break;
132     default:
133       return 0;
134   }
135 }
136
137
138
139 /* Convert symtab from memory model to elfclass specific format */
140 void elfu_mSymtabFlatten(ElfuElf *me)
141 {
142   ElfuSym *sym;
143   size_t numsyms = 0;
144
145   elfu_mLayoutAuto(me);
146
147   /* Update section indexes and count symbols */
148   CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
149     if (sym->scnptr) {
150       sym->shndx = elfu_mScnIndex(me, sym->scnptr);
151     }
152
153     numsyms++;
154   }
155
156   /* Copy symbols to elfclass-specific format */
157   if (me->elfclass == ELFCLASS32) {
158     size_t newsize = (numsyms + 1) * sizeof(Elf32_Sym);
159     size_t i;
160
161     if (me->symtab->databuf) {
162       free(me->symtab->databuf);
163     }
164     me->symtab->databuf = malloc(newsize);
165     assert(me->symtab->databuf);
166
167     me->symtab->shdr.sh_size = newsize;
168     memset(me->symtab->databuf, 0, newsize);
169
170     i = 1;
171     CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
172       Elf32_Sym *es = ((Elf32_Sym*)me->symtab->databuf) + i;
173
174       es->st_name = sym->name;
175       es->st_value = sym->value;
176       es->st_size = sym->size;
177       es->st_info = ELF32_ST_INFO(sym->bind, sym->type);
178       es->st_other = sym->other;
179       es->st_shndx = sym->shndx;
180
181       i++;
182     }
183   } else if (me->elfclass == ELFCLASS64) {
184     size_t newsize = (numsyms + 1) * sizeof(Elf64_Sym);
185     size_t i;
186
187     if (me->symtab->databuf) {
188       free(me->symtab->databuf);
189     }
190     me->symtab->databuf = malloc(newsize);
191     assert(me->symtab->databuf);
192
193     me->symtab->shdr.sh_size = newsize;
194     memset(me->symtab->databuf, 0, newsize);
195
196     i = 1;
197     CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
198       Elf64_Sym *es = ((Elf64_Sym*)me->symtab->databuf) + i;
199
200       es->st_name = sym->name;
201       es->st_value = sym->value;
202       es->st_size = sym->size;
203       es->st_info = ELF64_ST_INFO(sym->bind, sym->type);
204       es->st_other = sym->other;
205       es->st_shndx = sym->shndx;
206
207       i++;
208     }
209   } else {
210     /* Unknown elfclass */
211     assert(0);
212   }
213
214   elfu_mLayoutAuto(me);
215 }
216
217
218
219 void elfu_mSymtabAddGlobalDymtabIfNotPresent(ElfuElf *me)
220 {
221   assert(me);
222
223   if (!me->symtab) {
224     ElfuScn *symtab;
225     ElfuScn *strtab;
226
227     symtab = elfu_mScnAlloc();
228     assert(symtab);
229     strtab = elfu_mScnAlloc();
230     assert(strtab);
231
232     symtab->linkptr = strtab;
233     symtab->shdr.sh_entsize = me->elfclass == ELFCLASS32 ? sizeof(Elf32_Sym) : sizeof(Elf64_Sym);
234     symtab->shdr.sh_addralign = 4;
235     strtab->shdr.sh_addralign = 1;
236     symtab->shdr.sh_type = SHT_SYMTAB;
237     strtab->shdr.sh_type = SHT_STRTAB;
238
239     strtab->databuf = malloc(1);
240     assert(strtab->databuf);
241     strtab->databuf[0] = 0;
242
243     CIRCLEQ_INSERT_TAIL(&me->orphanScnList, symtab, elemChildScn);
244     CIRCLEQ_INSERT_TAIL(&me->orphanScnList, strtab, elemChildScn);
245
246     me->symtab = symtab;
247
248     if (me->shstrtab) {
249       symtab->shdr.sh_name = me->shstrtab->shdr.sh_size;
250       if (elfu_mScnAppendData(me->shstrtab, ".symtab", 8)) {
251         symtab->shdr.sh_name = 0;
252       }
253
254       strtab->shdr.sh_name = me->shstrtab->shdr.sh_size;
255       if (elfu_mScnAppendData(me->shstrtab, ".strtab", 8)) {
256         strtab->shdr.sh_name = 0;
257       }
258     }
259   }
260 }