Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: elfsym.cc | |
4 | // Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
5 | // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
6 | // | |
7 | // The above named program is free software; you can redistribute it and/or | |
8 | // modify it under the terms of the GNU General Public | |
9 | // License version 2 as published by the Free Software Foundation. | |
10 | // | |
11 | // The above named program is distributed in the hope that it will be | |
12 | // useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | // General Public License for more details. | |
15 | // | |
16 | // You should have received a copy of the GNU General Public | |
17 | // License along with this work; if not, write to the Free Software | |
18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | // | |
20 | // ========== Copyright Header End ============================================ | |
21 | /////////////////////////////////////////////////////////////// | |
22 | // | |
23 | // elfsym: Extract Symbols for .text, .data and .bss | |
24 | // sections of ELF file using libelf interface. | |
25 | // | |
26 | // Note: use 'Extended ELF Section indexes' if required, | |
27 | // decode a corresponding SHT_SYMTAB_SHNDX | |
28 | // section. | |
29 | // | |
30 | // Copyright 2006 Sun Microsystems, Inc. All rights reserved. | |
31 | // Use is subject to license terms. | |
32 | // | |
33 | ||
34 | ||
35 | #include <stdio.h> | |
36 | #include <libelf.h> | |
37 | #include <gelf.h> | |
38 | #include <fcntl.h> | |
39 | #include <unistd.h> | |
40 | #include <stdlib.h> | |
41 | #include <string.h> | |
42 | #include "ui.h" | |
43 | ||
44 | // only one routine in exported interface | |
45 | int load_symbols ( char* elf_fname, uint64_t text_base, uint64_t data_base, uint32_t context ); | |
46 | ||
47 | // set level of debug info | |
48 | static int Verbose = 0; | |
49 | ||
50 | ||
51 | ||
52 | ||
53 | // If you need to build a standalong elf_file utility | |
54 | // set STANDALONG to be defined, build it with command | |
55 | // CC -o <util_name> elf_file.cc | |
56 | // usage: <util_name> <path/elf_file_name> | |
57 | // output elf symbol info to the terminal | |
58 | #ifndef STANDALONG | |
59 | ||
60 | #include "symbols.h" | |
61 | ||
62 | // imported symtable | |
63 | extern SymTable *g_sym_table; // symbol table | |
64 | ||
65 | #else // STANDALONG | |
66 | ||
67 | int main(int argc, char **argv) | |
68 | { | |
69 | int i; | |
70 | ||
71 | Verbose = 1; | |
72 | ||
73 | if (argc != 2) | |
74 | { | |
75 | ui->output("usage: %s elf_file \n", argv[0]); | |
76 | return(1); | |
77 | } | |
78 | ||
79 | char *fname = argv[1]; | |
80 | ||
81 | return load_symbols(fname,0,0,Symbol::ANY_CONTEXT); | |
82 | ||
83 | } | |
84 | ||
85 | #endif // STANDALONG | |
86 | ||
87 | ||
88 | ||
89 | ||
90 | ||
91 | ////////////////////////////////////////////////////// | |
92 | // | |
93 | // Extract defined symbols from | |
94 | // .symtab and .dynsym sections | |
95 | // | |
96 | static void get_symbols | |
97 | ( | |
98 | Elf *elf, | |
99 | char *file, | |
100 | uint64_t text_base, | |
101 | uint64_t data_base, | |
102 | uint32_t context | |
103 | ) | |
104 | { | |
105 | Elf_Scn *scn; | |
106 | GElf_Shdr shdr; | |
107 | GElf_Ehdr ehdr; | |
108 | ||
109 | ||
110 | // get module name by stripping path from file name | |
111 | char *path_name=strtok(file, "/"); | |
112 | char *module_name = file; | |
113 | while (path_name=strtok(NULL, "/")) | |
114 | { | |
115 | module_name = path_name; | |
116 | } | |
117 | ||
118 | ||
119 | ||
120 | ||
121 | if (gelf_getehdr(elf, &ehdr) == 0) | |
122 | { | |
123 | ui->error( "%s: elf_getehdr() failed: %s\n", | |
124 | file, elf_errmsg(0)); | |
125 | return; | |
126 | } | |
127 | ||
128 | ||
129 | // check the file type | |
130 | char *filetype = "Other"; | |
131 | switch (ehdr.e_type) | |
132 | { | |
133 | case ET_REL: | |
134 | filetype = "Reallocatable"; | |
135 | break; | |
136 | case ET_EXEC: | |
137 | filetype = "Executable"; | |
138 | break; | |
139 | case ET_DYN: | |
140 | filetype = "Shared Object"; | |
141 | break; | |
142 | default: | |
143 | break; | |
144 | } | |
145 | ui->output("loading symbols for %s - %s file.\n", module_name, filetype); | |
146 | ||
147 | ||
148 | size_t shstrndx; | |
149 | if (elf_getshstrndx(elf, &shstrndx) == 0) | |
150 | { | |
151 | ui->error( "%s: elf_getshstrndx() failed: %s\n", | |
152 | file, elf_errmsg(0)); | |
153 | return; | |
154 | } | |
155 | ||
156 | // find .text, .data and .bss section headers | |
157 | GElf_Shdr text_shdr; | |
158 | GElf_Shdr data_shdr; | |
159 | GElf_Shdr bss_shdr; | |
160 | text_shdr.sh_type = SHT_NULL; | |
161 | data_shdr.sh_type = SHT_NULL; | |
162 | bss_shdr.sh_type = SHT_NULL; | |
163 | scn = 0; | |
164 | uint32_t idx = 1; | |
165 | uint32_t text_idx, data_idx, bss_idx; | |
166 | ||
167 | while ((scn = elf_nextscn(elf, scn)) != 0) | |
168 | { | |
169 | if (gelf_getshdr(scn, &shdr) == 0) | |
170 | { | |
171 | (void) ui->error( | |
172 | "%s: elf_getshdr() failed: %s\n", | |
173 | file, elf_errmsg(0)); | |
174 | return; | |
175 | } | |
176 | ||
177 | // get section name | |
178 | char *sname = elf_strptr(elf, shstrndx, shdr.sh_name); | |
179 | ||
180 | if (strcmp(sname, ".data")==0) | |
181 | { | |
182 | data_shdr = shdr; | |
183 | data_idx = idx; | |
184 | } | |
185 | else if (strcmp(sname, ".bss" )==0) | |
186 | { | |
187 | bss_shdr = shdr; | |
188 | bss_idx = idx; | |
189 | } | |
190 | else if (strcmp(sname, ".text")==0) | |
191 | { | |
192 | text_shdr = shdr; | |
193 | text_idx = idx; | |
194 | } | |
195 | idx++; | |
196 | continue; | |
197 | ||
198 | } | |
199 | ||
200 | if (text_shdr.sh_type ==SHT_NULL) | |
201 | { | |
202 | ui->error(" there is no text section \n"); | |
203 | return; | |
204 | } | |
205 | else if (Verbose) | |
206 | ui->output(".text #%d sh_addr=0x%llx, sh_size=0x%llx, sh_offset=0x%llx, sh_addralign=0x%llx \n", | |
207 | text_idx,text_shdr.sh_addr, text_shdr.sh_size, text_shdr.sh_offset, text_shdr.sh_addralign ); | |
208 | ||
209 | if (Verbose && data_shdr.sh_type !=SHT_NULL) | |
210 | ui->output(".data #%d sh_addr=0x%llx, sh_size=0x%llx, sh_offset=0x%llx, sh_addralign=0x%llx \n", | |
211 | data_idx,data_shdr.sh_addr, data_shdr.sh_size, data_shdr.sh_offset, data_shdr.sh_addralign ); | |
212 | ||
213 | if (Verbose && bss_shdr.sh_type !=SHT_NULL) | |
214 | ui->output(".bss #%d sh_addr=0x%llx, sh_size=0x%llx, sh_offset=0x%llx, sh_addralign=0x%llx \n", | |
215 | bss_idx,bss_shdr.sh_addr, bss_shdr.sh_size, bss_shdr.sh_offset, bss_shdr.sh_addralign ); | |
216 | ||
217 | // uninitialized data (bss) segment is located immediately | |
218 | // after initialized data segment | |
219 | uint64_t bss_offset = 0; | |
220 | ||
221 | if ( | |
222 | (data_shdr.sh_type !=SHT_NULL) // data section is present | |
223 | && (bss_shdr.sh_addr == 0) // not absolute address | |
224 | ) | |
225 | { | |
226 | bss_offset = (bss_shdr.sh_offset - data_shdr.sh_offset) & | |
227 | ~(uint64_t(bss_shdr.sh_addralign) - 1); | |
228 | } | |
229 | ||
230 | if (Verbose) | |
231 | ui->output(".bss addr offset = 0x%llx \n", bss_offset); | |
232 | ||
233 | ||
234 | ||
235 | // find .dynsym and .symtab sections | |
236 | scn = 0; | |
237 | while ((scn = elf_nextscn(elf, scn)) != 0) | |
238 | { | |
239 | uint_t symcnt; | |
240 | uint_t ndx; | |
241 | ||
242 | Elf_Data *symdata; | |
243 | Elf_Data *shndxdata; | |
244 | ||
245 | if (gelf_getshdr(scn, &shdr) == 0) | |
246 | { | |
247 | ui->error("%s: elf_getshdr() failed: %s\n", | |
248 | file, elf_errmsg(0)); | |
249 | return; | |
250 | } | |
251 | ||
252 | ||
253 | if ((shdr.sh_type != SHT_SYMTAB) && | |
254 | (shdr.sh_type != SHT_DYNSYM)) | |
255 | continue; | |
256 | ||
257 | // Get the data associated with the Symbol | |
258 | // section. | |
259 | if ((symdata = elf_getdata(scn, 0)) == 0) | |
260 | { | |
261 | ui->error("%s: elf_getdata() failed: %s\n", | |
262 | file, elf_errmsg(0)); | |
263 | return; | |
264 | } | |
265 | ||
266 | #ifdef STANDALONG | |
267 | // Print symbol table title and header for symbol display | |
268 | ui->output("\nSymTab: %s:%s\n", file, | |
269 | elf_strptr(elf, shstrndx, shdr.sh_name)); | |
270 | ui->output(" index value size type " | |
271 | "bind oth shndx name\n"); | |
272 | #endif | |
273 | ||
274 | // iterate over the symbol table | |
275 | shndxdata = 0; | |
276 | uint_t nosymshndx = 0; | |
277 | symcnt = shdr.sh_size / shdr.sh_entsize; | |
278 | for (ndx = 0; ndx < symcnt; ndx++) | |
279 | { | |
280 | GElf_Sym sym; | |
281 | Elf32_Word shndx; | |
282 | uint_t type; | |
283 | uint_t bind; | |
284 | uint_t specshndx; | |
285 | ||
286 | // Get a symbol entry | |
287 | if (gelf_getsymshndx(symdata, shndxdata, ndx, | |
288 | &sym, &shndx) == NULL) | |
289 | { | |
290 | (void) ui->error( | |
291 | "%s: gelf_getsymshndx() failed: %s\n", | |
292 | file, elf_errmsg(0)); | |
293 | return; | |
294 | } | |
295 | // Check to see if this symbol's st_shndx | |
296 | // is using the 'Extended SHNDX table' for | |
297 | // a SYMTAB. | |
298 | // If it is - and we havn't searched before, | |
299 | // go find the associated SHT_SYMTAB_SHNDX | |
300 | // section. | |
301 | if ((sym.st_shndx == SHN_XINDEX) && | |
302 | (shndxdata == 0) && (nosymshndx == 0)) | |
303 | { | |
304 | Elf_Scn *_scn; | |
305 | GElf_Shdr _shdr; | |
306 | GElf_Word symscnndx; | |
307 | _scn = 0; | |
308 | specshndx = 0; | |
309 | symscnndx = elf_ndxscn(scn); | |
310 | ||
311 | while ((_scn = elf_nextscn(elf, _scn)) != 0) | |
312 | { | |
313 | if (gelf_getshdr(_scn, &_shdr) == 0) | |
314 | break; | |
315 | ||
316 | // We've found the Symtab SHNDX table | |
317 | // if it's of type SHT_SYMTAB_SHNDX | |
318 | // and it's shdr.sh_link points to the | |
319 | // section index for the current symbol | |
320 | // table. | |
321 | if ((_shdr.sh_type == | |
322 | SHT_SYMTAB_SHNDX) && | |
323 | (_shdr.sh_link == symscnndx)) | |
324 | { | |
325 | if ((shndxdata = | |
326 | elf_getdata(_scn, 0)) != 0) | |
327 | break; | |
328 | } | |
329 | } | |
330 | // Get a symbol entry | |
331 | if (shndxdata && | |
332 | (gelf_getsymshndx(symdata, shndxdata, ndx, | |
333 | &sym, &shndx) == NULL)) | |
334 | { | |
335 | (void) ui->error( | |
336 | "%s: gelf_getsymshndx() " | |
337 | "failed: %s\n", | |
338 | file, elf_errmsg(0)); | |
339 | return; | |
340 | } | |
341 | // No Symtab SHNDX table was found. We could | |
342 | // give a fatal error here - instead we'll | |
343 | // just mark that fact and display as much of | |
344 | // the symbol table as we can. Any symbol | |
345 | // displayed with a XINDX section index has | |
346 | // a bogus value - skip it | |
347 | if (shndxdata == 0) | |
348 | nosymshndx = 1; | |
349 | } | |
350 | ||
351 | // Decode the type & binding information | |
352 | type = GELF_ST_TYPE(sym.st_info); | |
353 | bind = GELF_ST_BIND(sym.st_info); | |
354 | ||
355 | ||
356 | // section index to which this symbol refer to | |
357 | specshndx = 0; | |
358 | if (sym.st_shndx < SHN_LORESERVE) | |
359 | shndx = sym.st_shndx; | |
360 | else if ((sym.st_shndx != SHN_XINDEX) || | |
361 | (shndxdata == NULL)) | |
362 | { | |
363 | shndx = sym.st_shndx; | |
364 | specshndx = 1; | |
365 | } | |
366 | ||
367 | // skip zero size symbols | |
368 | if (sym.st_size == 0) | |
369 | continue; | |
370 | ||
371 | uint64_t va = sym.st_value; | |
372 | ||
373 | // check if it is in text section | |
374 | if ((text_shdr.sh_type != SHT_NULL) && (shndx == text_idx)) | |
375 | { | |
376 | // text symbol | |
377 | va += text_base; | |
378 | } | |
379 | else if ( type == STT_OBJECT ) //data symbol | |
380 | { | |
381 | if ((data_shdr.sh_type != SHT_NULL) && (shndx == data_idx)) | |
382 | { | |
383 | // initialized data | |
384 | va += data_base; | |
385 | } | |
386 | else if ((bss_shdr.sh_type != SHT_NULL) && (shndx == bss_idx)) | |
387 | { | |
388 | // uninitialized data | |
389 | if ( bind == STB_GLOBAL ) | |
390 | { | |
391 | va += data_base; | |
392 | } | |
393 | else if (bind == STB_LOCAL) | |
394 | { | |
395 | // local uninitialized data is located at the end | |
396 | // of data segment | |
397 | va += (data_base + bss_offset); | |
398 | } | |
399 | else | |
400 | continue; | |
401 | } | |
402 | else | |
403 | continue; | |
404 | } | |
405 | else | |
406 | continue; | |
407 | ||
408 | ||
409 | // append symbol | |
410 | char *sym_name = elf_strptr(elf, shdr.sh_link, sym.st_name); | |
411 | ||
412 | ||
413 | #ifndef STANDALONG | |
414 | ||
415 | // check if symbol is already there | |
416 | Symbol *s = g_sym_table->find(sym_name); | |
417 | ||
418 | if (s == NULL || s->vaddr() != va || s->size() != sym.st_size) | |
419 | { | |
420 | g_sym_table->add(module_name, sym_name, va, sym.st_size, context); | |
421 | } | |
422 | ||
423 | #else // STANDALONG | |
424 | ||
425 | // prepare sym type info | |
426 | const int INTSTRLEN = 32; | |
427 | char bindbuf[INTSTRLEN]; | |
428 | char typebuf[INTSTRLEN]; | |
429 | char shndxbuf[INTSTRLEN]; | |
430 | const char *bindstr; | |
431 | const char *typestr; | |
432 | const char *shndxstr; | |
433 | ||
434 | static const char *symbind[STB_NUM] = { | |
435 | /* STB_LOCL */ "LOCL", | |
436 | /* STB_GLOBAL */ "GLOB", | |
437 | /* STB_WEAK */ "WEAK"}; | |
438 | ||
439 | static const char *symtype[STT_NUM] = { | |
440 | /* STT_NOTYPE */ "NOTY", | |
441 | /* STT_OBJECT */ "OBJT", | |
442 | /* STT_FUNC */ "FUNC", | |
443 | /* STT_SECTION */ "SECT", | |
444 | /* STT_FILE */ "FILE", | |
445 | /* STT_COMMON */ "COMM", | |
446 | /* STT_TLS */ "TLS"}; | |
447 | ||
448 | ||
449 | ||
450 | ||
451 | if (type < STT_NUM) | |
452 | typestr = symtype[type]; | |
453 | else | |
454 | { | |
455 | snprintf(typebuf, INTSTRLEN, "%d", type); | |
456 | typestr = typebuf; | |
457 | } | |
458 | ||
459 | if (bind < STB_NUM) | |
460 | bindstr = symbind[bind]; | |
461 | else | |
462 | { | |
463 | snprintf(bindbuf, INTSTRLEN, "%d", bind); | |
464 | bindstr = bindbuf; | |
465 | } | |
466 | ||
467 | ||
468 | if (shndx == SHN_UNDEF) | |
469 | { | |
470 | shndxstr = (const char *)"UNDEF"; | |
471 | ||
472 | } | |
473 | else if (specshndx) | |
474 | { | |
475 | if (shndx == SHN_ABS) | |
476 | shndxstr = (const char *)"ABS"; | |
477 | else if (shndx == SHN_COMMON) | |
478 | shndxstr = (const char *)"COMM"; | |
479 | else if (shndx == SHN_XINDEX) | |
480 | shndxstr = (const char *)"XIND"; | |
481 | else | |
482 | { | |
483 | snprintf(shndxbuf, INTSTRLEN, "%ld", shndx); | |
484 | shndxstr = shndxbuf; | |
485 | } | |
486 | } | |
487 | else | |
488 | { | |
489 | (void) snprintf(shndxbuf, INTSTRLEN, | |
490 | "%ld", shndx); | |
491 | shndxstr = shndxbuf; | |
492 | } | |
493 | ||
494 | //char *this_sh_name = elf_strptr(elf, shstrndx, shndx); | |
495 | ||
496 | // display the symbol entry | |
497 | ui->otput("[%3d] 0x%08llx 0x%08llx %-4s %-6s %2d %5s %s \n", | |
498 | ndx, va /* sym.st_value */, sym.st_size, | |
499 | typestr, bindstr, sym.st_other, shndxstr, | |
500 | sym_name); | |
501 | ||
502 | #endif // STANDALONG | |
503 | } | |
504 | } | |
505 | } | |
506 | ||
507 | ////////////////////////////////////////////////////////////////// | |
508 | // | |
509 | // open an ELF file using | |
510 | // elf_begin(ELF_C_READ) and examine search the ELF file | |
511 | // for either a SHT_SYMTAB or SHT_DYNSYM symbol table. | |
512 | // If it finds either - it will extract the contents of | |
513 | // the symbol tables. | |
514 | ||
515 | static void process_elf | |
516 | ( | |
517 | Elf *elf, | |
518 | char *file, | |
519 | int fd, | |
520 | int member, | |
521 | uint64_t text_base, | |
522 | uint64_t data_base, | |
523 | uint32_t context | |
524 | ) | |
525 | { | |
526 | Elf_Cmd cmd; | |
527 | Elf *_elf; | |
528 | ||
529 | switch (elf_kind(elf)) | |
530 | { | |
531 | case ELF_K_ELF: | |
532 | // This is an ELF file | |
533 | get_symbols(elf, file, text_base, data_base,context); | |
534 | break; | |
535 | case ELF_K_AR: | |
536 | // Archives contain multiple ELF files, which can each | |
537 | // in turn be examined with libelf. | |
538 | // Iterate over each member of the | |
539 | // archive and recursivly call process_elf() for processing. | |
540 | cmd = ELF_C_READ; | |
541 | while ((_elf = elf_begin(fd, cmd, elf)) != 0) | |
542 | { | |
543 | Elf_Arhdr *arhdr; | |
544 | char buffer[1024]; | |
545 | ||
546 | arhdr = elf_getarhdr(_elf); | |
547 | ||
548 | // Build up file names based off of | |
549 | // 'archivename(membername)'. | |
550 | snprintf(buffer, 1024, "%s(%s)", file, arhdr->ar_name); | |
551 | ||
552 | // recursivly process the ELF members. | |
553 | process_elf(_elf, buffer, fd, 1, text_base, data_base, context); | |
554 | cmd = elf_next(_elf); | |
555 | elf_end(_elf); | |
556 | } | |
557 | break; | |
558 | default: | |
559 | if (!member) | |
560 | ui->error("%s: unexpected elf_kind(): 0x%x\n", | |
561 | file, elf_kind(elf)); | |
562 | return; | |
563 | } | |
564 | } | |
565 | ||
566 | ||
567 | ||
568 | ||
569 | //////////////////////////////////////////////////////////////////// | |
570 | int load_symbols | |
571 | ( | |
572 | char* elf_fname, | |
573 | uint64_t text_base, | |
574 | uint64_t data_base, | |
575 | uint32_t context | |
576 | ) | |
577 | { | |
578 | ||
579 | /* | |
580 | * Initialize the elf library, must be called before elf_begin() | |
581 | * can be called. | |
582 | */ | |
583 | if (elf_version(EV_CURRENT) == EV_NONE) | |
584 | { | |
585 | ui->error( "elf_version() failed: %s\n", elf_errmsg(0)); | |
586 | return(1); | |
587 | } | |
588 | ||
589 | int fd; | |
590 | Elf *elf; | |
591 | ||
592 | if ((fd = open(elf_fname, O_RDONLY)) == -1) | |
593 | { | |
594 | ui->perror("open"); | |
595 | return 1; | |
596 | } | |
597 | ||
598 | // open an Elf descriptor Read-Only | |
599 | if ((elf = elf_begin(fd, ELF_C_READ, 0)) == NULL) | |
600 | { | |
601 | ui->error( "elf_begin() failed: %s\n", elf_errmsg(0)); | |
602 | close(fd); | |
603 | return 1; | |
604 | } | |
605 | ||
606 | // Process elf descriptor | |
607 | process_elf(elf, elf_fname, fd, 0, text_base, data_base, context); | |
608 | ||
609 | elf_end(elf); | |
610 | close(fd); | |
611 | ||
612 | return 0; | |
613 | } | |
614 | ||
615 | ||
616 |