Introduce 32/64bit macros to use the same code on both platforms
[opensuse:ptools.git] / libproc / symbols.c
1 /*
2  * symbols.c - A symbol table implementation from pstack
3  *
4  * This file is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 /*
19  * FIXME: This code can only handle native ELF!
20  */
21
22 #include <fcntl.h>
23 #include <link.h>
24 #include <malloc.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <unistd.h>
30
31 #include "proc_service.h"
32 #include "symbols.h"
33
34 struct Elf_Ehdr {
35         union {
36                 Elf32_Ehdr e32;
37                 Elf64_Ehdr e64;
38         } e_un;
39         int is_64bit;
40 };
41
42 struct Elf_Shdr {
43         union {
44                 Elf32_Shdr s32;
45                 Elf64_Shdr s64;
46         } s_un;
47         int is_64bit;
48 };
49
50 struct Elf_Sym {
51         union {
52                 Elf32_Sym s32;
53                 Elf64_Sym s64;
54         } s_un;
55 };
56
57 #define Elf_Ehdr(x,name) ((x)->is_64bit ? \
58                           (x)->e_un.e64.name :  \
59                           (x)->e_un.e32.name)
60
61 #define Elf_Shdr(x,name) ((x)->is_64bit ? \
62                           (x)->s_un.s64.name :  \
63                           (x)->s_un.s32.name)
64
65 #define Elf_Sym(x,name) ((x)->is_64bit ? \
66                          (x)->s_un.s64.name :   \
67                          (x)->s_un.s32.name)
68
69 struct Symbols {
70         struct Symbols *next;
71         char *name;
72         ElfW(Sym) *symbols;
73         int nsyms;
74         char *strings;
75         int strslen, noffsets;
76         ElfW(Addr) baseAddr;
77         ElfW(Dyn) *dynamic;
78         int ndyns;
79         int is_64bit;
80 };
81
82 static Symbols_t allSyms;
83
84 static Symbols_t newSyms(const char *name)
85 {
86         Symbols_t syms = (Symbols_t) calloc(1, sizeof(struct Symbols));
87
88         if (!syms)
89                 ps_abort("Out of memory");
90         syms->next = allSyms;
91         allSyms = syms;
92         syms->name = strdup(name);
93         return syms;
94 }
95
96 static void deleteSyms(Symbols_t syms)
97 {
98         Symbols_t s2;
99
100         if (syms == allSyms)
101                 allSyms = syms->next;
102         else {
103                 for (s2 = allSyms; s2 && s2->next != syms; s2 = s2->next);
104                 if (s2)
105                         s2->next = syms->next;
106         }
107         if (syms->symbols)
108                 free(syms->symbols);
109         if (syms->strings)
110                 free(syms->strings);
111         if (syms->dynamic)
112                 free(syms->dynamic);
113         if (syms->name)
114                 free(syms->name);
115         free(syms);
116 }
117
118 const Elf32_Sym *lookupSym32InTable(const char *name, Symbols_t syms)
119 {
120         Elf32_Sym *sym;
121         int i;
122
123         for (i = 0, sym = syms->symbols; i < syms->nsyms; i++, sym++) {
124                 if (!strcmp(name, &syms->strings[sym->st_name]))
125                         return sym;
126         }
127
128         return 0;
129 }
130
131 const Elf64_Sym *lookupSym64InTable(const char *name, Symbols_t syms)
132 {
133         Elf64_Sym *sym;
134         int i;
135
136         for (i = 0, sym = syms->symbols; i < syms->nsyms; i++, sym++) {
137                 if (!strcmp(name, &syms->strings[sym->st_name]))
138                         return sym;
139         }
140
141         return 0;
142 }
143
144 void findCodeAddress(ElfW(Addr) addr, ElfW(Sym) ** ans, Symbols_t * symtab)
145 {
146         ElfW(Sym) *sym;
147         Symbols_t tab;
148         int i;
149
150         for (tab = allSyms, *ans = 0, *symtab = 0; tab; tab = tab->next) {
151                 if (addr < tab->baseAddr)
152                         continue;
153                 for (sym = tab->symbols, i = 0; i < tab->nsyms; i++, sym++) {
154                         if (sym->st_value <= addr
155                             && sym->st_shndx != SHN_UNDEF
156                             && sym->st_shndx < tab->noffsets
157                             && ELF32_ST_TYPE(sym->st_info) == STT_FUNC
158                             && (!*ans || (*ans)->st_value < sym->st_value))
159                                 *ans = sym, *symtab = tab;
160                 }
161         }
162 }
163
164 const Elf64_Addr *findLocalSym(const char *name, Symbols_t syms)
165 {
166         union {
167                 Elf32_Sym *s32;
168                 Elf64_Sym *s64;
169         } sym_un;
170
171         if (syms->is_64bit)
172                 sym_un.s64 = lookupSym64InTable(name, syms);
173         else
174                 sym_un.s32 = lookupSym32InTable(name, syms);
175
176         if (syms->is_64bit)
177                 return (!sym_un.s64 || sym_un.s64->st_shndx == SHN_UNDEF ||
178                 sym_un.s64->st_shndx >= syms->noffsets) ?
179                         0 :
180                         sym_un.s64->st_value;
181         else
182                 return (!sym_un.s32 || sym_un.s32->st_shndx == SHN_UNDEF ||
183                 sym_un.s32->st_shndx >= syms->noffsets) ?
184                         0 :
185                         sym_un.s32->st_value;
186 }
187
188 static void verify_ident(struct Elf_Ehdr *hdr)
189 {
190         if (memcmp(&hdr->e_un.e32.e_ident[EI_MAG0], ELFMAG, SELFMAG))
191                 ps_abort("Bad magic number.");
192         if ((hdr->e_un.e32.e_ident[EI_CLASS] != ELFCLASS32) &&
193             (hdr->e_un.e32.e_ident[EI_CLASS] != ELFCLASS64))
194                 ps_abort("only 32 bit and 64 bit objects supported.");
195
196         hdr->is_64bit = (hdr->e_un.e32.e_ident[EI_CLASS] == ELFCLASS64);
197         ps_debug("This is a %dbit process\n", hdr->is_64bit ? 64 : 32);
198
199         if (hdr->e_un.e32.e_ident[EI_DATA] != ELFDATA2LSB)
200                 ps_abort("big endian object files not supported.");
201         if (hdr->e_un.e32.e_ident[EI_VERSION] != EV_CURRENT ||
202             hdr->e_un.e32.e_version != EV_CURRENT)
203                 ps_abort("Unsupported ELF format version.");
204         if ((hdr->e_un.e32.e_machine != EM_386) &&
205             (hdr->e_un.e32.e_machine != EM_X86_64))
206                 ps_abort("Not an IA32 or AMD64 executable.");
207 }
208
209 static int find_symtabs(struct Elf_Ehdr *hdr, int fd, Symbols_t syms)
210 {
211         int i, idx, spot;
212         struct Elf_Shdr shdr;
213
214         spot = Elf_Ehdr(hdr,e_shoff);
215         if (lseek(fd, spot, SEEK_SET) != spot)
216                 ps_abort("seek failed.");
217
218         memset(&shdr, 0, sizeof(shdr));
219
220         shdr.is_64bit = hdr->is_64bit;
221         syms->is_64bit = hdr->is_64bit;
222
223         syms->noffsets = Elf_Ehdr(hdr,e_shnum);
224
225         for (idx = 0; idx < Elf_Ehdr(hdr,e_shnum); idx++) {
226                 if (read(fd, &shdr, Elf_Ehdr(hdr,e_shentsize))
227                     != Elf_Ehdr(hdr,e_shentsize))
228                         ps_abort("premature eof.");
229                 spot += Elf_Ehdr(hdr,e_shentsize);
230
231                 ps_debug("sh_type = %x\n", Elf_Shdr(&shdr,sh_type));
232
233                 switch (Elf_Shdr(&shdr,sh_type)) {
234                 case SHT_SYMTAB:
235                         syms->nsyms = Elf_Shdr(&shdr,sh_size) /
236                                 (shdr.is_64bit ? sizeof(Elf64_Sym) :
237                                  sizeof(Elf32_Sym));
238
239                         syms->symbols = malloc(Elf_Shdr(&shdr,sh_size));
240                         if (!syms->symbols)
241                                 ps_abort("Could not allocate symbol table.");
242
243                         if (lseek(fd, Elf_Shdr(&shdr,sh_offset), SEEK_SET) !=
244                             Elf_Shdr(&shdr,sh_offset)
245                             || read(fd, syms->symbols,
246                                     Elf_Shdr(&shdr,sh_size))
247                             != Elf_Shdr(&shdr,sh_size))
248                                 ps_abort("Could not read symbol table.");
249
250                         i = Elf_Ehdr(hdr,e_shoff) + Elf_Shdr(&shdr,sh_link) * Elf_Ehdr(hdr,e_shentsize);
251                         if (lseek(fd, i, SEEK_SET) != i)
252                                 ps_abort("Could not seek and find.");
253                         if (read(fd, &shdr, Elf_Ehdr(hdr,e_shentsize)) !=
254                             Elf_Ehdr(hdr,e_shentsize))
255                                 ps_abort("Could not read string table section header.");
256                         if (!(syms->strings = malloc(Elf_Shdr(&shdr,sh_size))))
257                                 ps_abort("Could not allocate string table.");
258                         if (lseek(fd, Elf_Shdr(&shdr,sh_offset), SEEK_SET) !=
259                             Elf_Shdr(&shdr,sh_offset)
260                             || read(fd, syms->strings,
261                                     Elf_Shdr(&shdr,sh_size)) != Elf_Shdr(&shdr,sh_size))
262                                 ps_abort("Could not read string table.");
263                         lseek(fd, spot, SEEK_SET);
264                         break;
265                 case SHT_DYNAMIC:
266                         syms->ndyns = Elf_Shdr(&shdr,sh_size) /
267                                 (shdr.is_64bit ? sizeof(Elf64_Dyn) :
268                                  sizeof(Elf32_Dyn));
269                         if (!
270                             (syms->dynamic = malloc(Elf_Shdr(&shdr,sh_size))))
271                                 ps_abort("Out of memory.");
272                         if (lseek(fd, Elf_Shdr(&shdr,sh_offset), SEEK_SET) !=
273                             Elf_Shdr(&shdr,sh_offset)
274                             || read(fd, syms->dynamic,
275                                     Elf_Shdr(&shdr,sh_size)) != Elf_Shdr(&shdr,sh_size))
276                                 ps_abort("Could not read dynamic table.");
277                         lseek(fd, spot, SEEK_SET);
278                         break;
279                 }
280         }
281
282         ps_debug("Loaded %d symbols (%d dynamic)\n", syms->nsyms + syms->ndyns,
283                  syms->ndyns);
284
285         return (syms->nsyms > 0);
286 }
287
288 static void resolveSymbols(Symbols_t syms, ElfW(Addr) baseaddr)
289 {
290         ElfW(Sym) *sym;
291         int i;
292
293         syms->baseAddr = baseaddr;
294
295         for (i = 0, sym = syms->symbols; i < syms->nsyms; i++, sym++) {
296                 if (sym->st_shndx && sym->st_shndx < syms->noffsets) {
297                         sym->st_value += baseaddr;
298                 }
299         }
300 }
301
302 Symbols_t loadSyms(const char *fname, unsigned long baseaddr)
303 {
304         struct Elf_Ehdr hdr;
305         int fd;
306         Symbols_t syms;
307
308         syms = newSyms(fname);
309         if ((fd = open(fname, O_RDONLY)) < 0) {
310                 fprintf(stderr, "'%s': ", fname);
311                 perror("opening object file");
312                 ps_abort("Could not open object file.");
313         }
314         read(fd, &hdr, sizeof(hdr));
315         verify_ident(&hdr);
316         if (!find_symtabs(&hdr, fd, syms)) {
317                 deleteSyms(syms);
318                 syms = 0;
319         } else
320                 resolveSymbols(syms, baseaddr);
321         close(fd);
322
323         return syms;
324 }
325
326 static void resetData(void)
327 {
328         Symbols_t syms, ns;
329
330         for (syms = allSyms; syms; syms = ns) {
331                 ns = syms->next;
332                 deleteSyms(syms);
333         }
334 }
335
336 int ps_symbols_init(void)
337 {
338         return 0;
339 }
340
341 void ps_symbols_exit(void)
342 {
343         resetData();
344 }