1/* 2 * Copyright (C) 1996-1997 John D. Polstra. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY JOHN D. POLSTRA AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL JOHN D. POLSTRA OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD$"); 28 29#include <sys/types.h> 30#include <sys/mman.h> 31#include <sys/stat.h> 32 33#include <machine/elf.h> 34 35#include <arpa/inet.h> 36 37#include <a.out.h> 38#include <assert.h> 39#include <ctype.h> 40#include <err.h> 41#include <fcntl.h> 42#include <sys/link_aout.h> 43#include <stab.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <unistd.h> 48 49#include "extern.h" 50 51#define PAGE_SIZE 4096 /* i386 specific */ 52 53#ifndef N_SETA 54#define N_SETA 0x14 /* Absolute set element symbol */ 55#endif /* This is input to LD, in a .o file. */ 56 57#ifndef N_SETT 58#define N_SETT 0x16 /* Text set element symbol */ 59#endif /* This is input to LD, in a .o file. */ 60 61#ifndef N_SETD 62#define N_SETD 0x18 /* Data set element symbol */ 63#endif /* This is input to LD, in a .o file. */ 64 65#ifndef N_SETB 66#define N_SETB 0x1A /* Bss set element symbol */ 67#endif /* This is input to LD, in a .o file. */ 68 69#ifndef N_SETV 70#define N_SETV 0x1C /* Pointer to set vector in data area. */ 71#endif /* This is output from LD. */ 72 73#ifdef STANDALONE 74static 75#endif 76 77static void dump_rels(const char *, const struct relocation_info *, 78 unsigned long, const char *(*)(unsigned long), unsigned char *); 79static void dump_segs(void); 80static void dump_sods(void); 81static void dump_sym(const struct nlist *); 82static void dump_syms(void); 83 84static void dump_rtsyms(void); 85 86static const char *rtsym_name(unsigned long); 87static const char *sym_name(unsigned long); 88 89#ifdef STANDALONE 90static 91#endif 92int error_count; 93 94/* 95 * Variables ending in _base are pointers to things in our address space, 96 * i.e., in the file itself. 97 * 98 * Variables ending in _addr are adjusted according to where things would 99 * actually appear in memory if the file were loaded. 100 */ 101static const char *file_base; 102static const char *text_base; 103static const char *data_base; 104static const struct relocation_info *rel_base; 105static const struct nlist *sym_base; 106static const char *str_base; 107 108static const struct relocation_info *rtrel_base; 109static const struct nzlist *rtsym_base; 110static const char *rtstr_base; 111 112static const struct exec *ex; 113static const struct _dynamic *dyn; 114static const struct section_dispatch_table *sdt; 115 116static const char *text_addr; 117static const char *data_addr; 118 119static unsigned long rel_count; 120static unsigned long sym_count; 121 122static unsigned long rtrel_count; 123static unsigned long rtsym_count; 124 125/* Dynamically allocated flags, 1 byte per symbol, to record whether each 126 symbol was referenced by a relocation entry. */ 127static unsigned char *sym_used; 128static unsigned char *rtsym_used; 129 130static unsigned long origin; /* What values are relocated relative to */ 131 132#ifdef STANDALONE 133int 134main(int argc, char *argv[]) 135{ 136 int i; 137 138 for (i = 1; i < argc; ++i) 139 dump_file(argv[i]); 140 141 return error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 142} 143#endif 144 145static inline const void *align_struct(const void *expr) 146{ 147 assert(!(((int)expr) & 3)); 148 return expr; 149} 150 151static inline const void *align_long(const void *expr) 152{ 153 assert(!(((int)expr) & 3)); 154 return expr; 155} 156 157static inline const void *align_short(const void *expr) 158{ 159 assert(!(((int)expr) & 1)); 160 return expr; 161} 162 163#ifdef STANDALONE 164static 165#endif 166void 167dump_file(const char *fname) 168{ 169 int fd; 170 struct stat sb; 171 caddr_t objbase; 172 173 if (stat(fname, &sb) == -1) { 174 warnx("cannot stat \"%s\"", fname); 175 ++error_count; 176 return; 177 } 178 179 if ((sb.st_mode & S_IFMT) != S_IFREG) { 180 warnx("\"%s\" is not a regular file", fname); 181 ++error_count; 182 return; 183 } 184 185 if ((fd = open(fname, O_RDONLY, 0)) == -1) { 186 warnx("cannot open \"%s\"", fname); 187 ++error_count; 188 return; 189 } 190 191 objbase = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); 192 if (objbase == (caddr_t) -1) { 193 warnx("cannot mmap \"%s\"", fname); 194 ++error_count; 195 close(fd); 196 return; 197 } 198 199 close(fd); 200 201 file_base = (const char *) objbase; /* Makes address arithmetic easier */ 202 203 if (IS_ELF(*(const Elf32_Ehdr*) align_struct(file_base))) { 204 warnx("%s: this is an ELF program; use objdump to examine", fname); 205 ++error_count; 206 munmap(objbase, sb.st_size); 207 return; 208 } 209 210 ex = (const struct exec *) align_struct(file_base); 211 212 printf("%s: a_midmag = 0x%lx\n", fname, (long)ex->a_midmag); 213 printf(" magic = 0x%lx = 0%lo, netmagic = 0x%lx = 0%lo\n", 214 (long)N_GETMAGIC(*ex), (long)N_GETMAGIC(*ex), 215 (long)N_GETMAGIC_NET(*ex), (long)N_GETMAGIC_NET(*ex)); 216 217 if (N_BADMAG(*ex)) { 218 warnx("%s: bad magic number", fname); 219 ++error_count; 220 munmap(objbase, sb.st_size); 221 return; 222 } 223 224 printf(" a_text = 0x%lx\n", (long)ex->a_text); 225 printf(" a_data = 0x%lx\n", (long)ex->a_data); 226 printf(" a_bss = 0x%lx\n", (long)ex->a_bss); 227 printf(" a_syms = 0x%lx\n", (long)ex->a_syms); 228 printf(" a_entry = 0x%lx\n", (long)ex->a_entry); 229 printf(" a_trsize = 0x%lx\n", (long)ex->a_trsize); 230 printf(" a_drsize = 0x%lx\n", (long)ex->a_drsize); 231 232 text_base = file_base + N_TXTOFF(*ex); 233 data_base = file_base + N_DATOFF(*ex); 234 rel_base = (const struct relocation_info *) 235 align_struct(file_base + N_RELOFF(*ex)); 236 sym_base = (const struct nlist *) align_struct(file_base + N_SYMOFF(*ex)); 237 str_base = file_base + N_STROFF(*ex); 238 239 rel_count = (ex->a_trsize + ex->a_drsize) / sizeof rel_base[0]; 240 assert(rel_count * sizeof rel_base[0] == ex->a_trsize + ex->a_drsize); 241 sym_count = ex->a_syms / sizeof sym_base[0]; 242 assert(sym_count * sizeof sym_base[0] == ex->a_syms); 243 244 if (sym_count != 0) { 245 sym_used = (unsigned char *) calloc(sym_count, sizeof(unsigned char)); 246 assert(sym_used != NULL); 247 } 248 249 printf(" Entry = 0x%lx\n", (long)ex->a_entry); 250 printf(" Text offset = %x, address = %lx\n", N_TXTOFF(*ex), 251 (long)N_TXTADDR(*ex)); 252 printf(" Data offset = %lx, address = %lx\n", (long)N_DATOFF(*ex), 253 (long)N_DATADDR(*ex)); 254 255 /* 256 * In an executable program file, everything is relocated relative to 257 * the assumed run-time load address, i.e., N_TXTADDR(*ex), i.e., 0x1000. 258 * 259 * In a shared library file, everything is relocated relative to the 260 * start of the file, i.e., N_TXTOFF(*ex), i.e., 0. 261 * 262 * The way to tell the difference is by looking at ex->a_entry. If it 263 * is >= 0x1000, then we have an executable program. Otherwise, we 264 * have a shared library. 265 * 266 * When a program is executed, the entire file is mapped into memory, 267 * including the a.out header and so forth. But it is not mapped at 268 * address 0; rather it is mapped at address 0x1000. The first page 269 * of the user's address space is left unmapped in order to catch null 270 * pointer dereferences. 271 * 272 * In this program, when we map in an executable program, we have to 273 * simulate the empty page by decrementing our assumed base address by 274 * a pagesize. 275 */ 276 277 text_addr = text_base; 278 data_addr = data_base; 279 origin = 0; 280 281 if (ex->a_entry >= PAGE_SIZE) { /* Executable, not a shared library */ 282 /* 283 * The fields in the object have already been relocated on the 284 * assumption that the object will be loaded at N_TXTADDR(*ex). 285 * We have to compensate for that. 286 */ 287 text_addr -= PAGE_SIZE; 288 data_addr -= PAGE_SIZE; 289 origin = PAGE_SIZE; 290 printf(" Program, origin = %lx\n", origin); 291 } else if (N_GETFLAG(*ex) & EX_DYNAMIC) 292 printf(" Shared library, origin = %lx\n", origin); 293 else 294 printf(" Object file, origin = %lx\n", origin); 295 296 if (N_GETFLAG(*ex) & EX_DYNAMIC) { 297 dyn = (const struct _dynamic *) align_struct(data_base); 298 printf(" Dynamic version = %d\n", dyn->d_version); 299 300 sdt = (const struct section_dispatch_table *) 301 align_struct(text_addr + (unsigned long) dyn->d_un.d_sdt); 302 303 rtrel_base = (const struct relocation_info *) 304 align_struct(text_addr + sdt->sdt_rel); 305 rtrel_count = (sdt->sdt_hash - sdt->sdt_rel) / sizeof rtrel_base[0]; 306 assert(rtrel_count * sizeof rtrel_base[0] == 307 (size_t)(sdt->sdt_hash - sdt->sdt_rel)); 308 309 rtsym_base = (const struct nzlist *) 310 align_struct(text_addr + sdt->sdt_nzlist); 311 rtsym_count = (sdt->sdt_strings - sdt->sdt_nzlist) / 312 sizeof rtsym_base[0]; 313 assert(rtsym_count * sizeof rtsym_base[0] == 314 (size_t)(sdt->sdt_strings - sdt->sdt_nzlist)); 315 316 if (rtsym_count != 0) { 317 rtsym_used = (unsigned char *) calloc(rtsym_count, 318 sizeof(unsigned char)); 319 assert(rtsym_used != NULL); 320 } 321 322 rtstr_base = text_addr + sdt->sdt_strings; 323 } 324 325 dump_segs(); 326 dump_sods(); 327 dump_rels("Relocations", rel_base, rel_count, sym_name, sym_used); 328 dump_syms(); 329 330 dump_rels("Run-time relocations", rtrel_base, rtrel_count, rtsym_name, 331 rtsym_used); 332 dump_rtsyms(); 333 334 if (rtsym_used != NULL) { 335 free(rtsym_used); 336 rtsym_used = NULL; 337 } 338 if (sym_used != NULL) { 339 free(sym_used); 340 sym_used = NULL; 341 } 342 munmap(objbase, sb.st_size); 343} 344 345static void 346dump_rels(const char *label, const struct relocation_info *base, 347 unsigned long count, const char *(*name)(unsigned long), 348 unsigned char *sym_used_flags) 349{ 350 unsigned long i; 351 352 printf(" %s:\n", label); 353 for (i = 0; i < count; ++i) { 354 const struct relocation_info *r = &base[i]; 355 unsigned int size; 356 char contents[16]; 357 358 size = 1u << r->r_length; 359 360 if (origin <= (unsigned long)r->r_address 361 && (unsigned long)r->r_address < origin + ex->a_text + ex->a_data 362 && 1 <= size && size <= 4) { 363 /* 364 * XXX - This can cause unaligned accesses. OK for the 365 * i386, not so for other architectures. 366 */ 367 switch (size) { 368 case 1: 369 snprintf(contents, sizeof contents, " [%02x]", 370 *(unsigned const char *)(text_addr + r->r_address)); 371 break; 372 case 2: 373 snprintf(contents, sizeof contents, " [%04x]", 374 *(unsigned const short *) 375 align_short(text_addr + r->r_address)); 376 break; 377 case 4: 378 snprintf(contents, sizeof contents, "[%08lx]", 379 *(unsigned const long *) 380 align_long(text_addr + r->r_address)); 381 break; 382 } 383 } else 384 snprintf(contents, sizeof contents, " "); 385 386 printf(" %6lu %8x/%u %s %c%c%c%c%c%c", i, 387 r->r_address, size, 388 contents, 389 r->r_extern ? 'e' : '-', 390 r->r_jmptable ? 'j' : '-', 391 r->r_relative ? 'r' : '-', 392 r->r_baserel ? 'b' : '-', 393 r->r_pcrel ? 'p' : '-', 394 r->r_copy ? 'c' : '-'); 395 396 if (r->r_extern || r->r_baserel || r->r_jmptable || r->r_copy) { 397 printf(" %4u %s", r->r_symbolnum, name(r->r_symbolnum)); 398 sym_used_flags[r->r_symbolnum] = 1; 399 } 400 401 printf("\n"); 402 } 403} 404 405static void 406dump_rtsyms(void) 407{ 408 unsigned long i; 409 410 printf(" Run-time symbols:\n"); 411 for (i = 0; i < rtsym_count; ++i) { 412 printf(" %6lu%c ", i, rtsym_used[i] ? '*' : ' '); 413 dump_sym(&rtsym_base[i].nlist); 414 printf("/%-5ld %s\n", rtsym_base[i].nz_size, rtsym_name(i)); 415 } 416} 417 418static void 419dump_segs(void) 420{ 421 printf(" Text segment starts at address %lx\n", origin + N_TXTOFF(*ex)); 422 if (N_GETFLAG(*ex) & EX_DYNAMIC) { 423 printf(" rel starts at %lx\n", sdt->sdt_rel); 424 printf(" hash starts at %lx\n", sdt->sdt_hash); 425 printf(" nzlist starts at %lx\n", sdt->sdt_nzlist); 426 printf(" strings starts at %lx\n", sdt->sdt_strings); 427 } 428 429 printf(" Data segment starts at address %lx\n", origin + N_DATOFF(*ex)); 430 if (N_GETFLAG(*ex) & EX_DYNAMIC) { 431 printf(" _dynamic starts at %lx\n", origin + N_DATOFF(*ex)); 432 printf(" so_debug starts at %lx\n", (unsigned long) dyn->d_debug); 433 printf(" sdt starts at %lx\n", (unsigned long) dyn->d_un.d_sdt); 434 printf(" got starts at %lx\n", sdt->sdt_got); 435 printf(" plt starts at %lx\n", sdt->sdt_plt); 436 printf(" rest of stuff starts at %lx\n", 437 sdt->sdt_plt + sdt->sdt_plt_sz); 438 } 439} 440 441static void 442dump_sods(void) 443{ 444 long sod_offset; 445 long paths_offset; 446 447 if (dyn == NULL) /* Not a shared object */ 448 return; 449 450 sod_offset = sdt->sdt_sods; 451 printf(" Shared object dependencies:\n"); 452 while (sod_offset != 0) { 453 const struct sod *sodp = (const struct sod *) align_struct((text_addr + sod_offset)); 454 const char *name = (const char *) (text_addr + sodp->sod_name); 455 456 if (sodp->sod_library) 457 printf(" -l%-16s version %d.%d\n", name, sodp->sod_major, 458 sodp->sod_minor); 459 else 460 printf(" %s\n", name); 461 sod_offset = sodp->sod_next; 462 } 463 paths_offset = sdt->sdt_paths; 464 printf(" Shared object additional paths:\n"); 465 if (paths_offset != 0) { 466 printf(" %s\n", (const char *)(text_addr + paths_offset)); 467 } else { 468 printf(" (none)\n"); 469 } 470} 471 472static void 473dump_sym(const struct nlist *np) 474{ 475 char type[8]; 476 char aux[8]; 477 char weak; 478 char *p; 479 480 switch (np->n_type & ~N_EXT) { 481 case N_UNDF: strcpy(type, "undf"); break; 482 case N_ABS: strcpy(type, "abs"); break; 483 case N_TEXT: strcpy(type, "text"); break; 484 case N_DATA: strcpy(type, "data"); break; 485 case N_BSS: strcpy(type, "bss"); break; 486 case N_INDR: strcpy(type, "indr"); break; 487 case N_SIZE: strcpy(type, "size"); break; 488 case N_COMM: strcpy(type, "comm"); break; 489 case N_SETA: strcpy(type, "seta"); break; 490 case N_SETT: strcpy(type, "sett"); break; 491 case N_SETD: strcpy(type, "setd"); break; 492 case N_SETB: strcpy(type, "setb"); break; 493 case N_SETV: strcpy(type, "setv"); break; 494 case N_FN: strcpy(type, np->n_type&N_EXT ? "fn" : "warn"); break; 495 case N_GSYM: strcpy(type, "gsym"); break; 496 case N_FNAME: strcpy(type, "fname"); break; 497 case N_FUN: strcpy(type, "fun"); break; 498 case N_STSYM: strcpy(type, "stsym"); break; 499 case N_LCSYM: strcpy(type, "lcsym"); break; 500 case N_MAIN: strcpy(type, "main"); break; 501 case N_PC: strcpy(type, "pc"); break; 502 case N_RSYM: strcpy(type, "rsym"); break; 503 case N_SLINE: strcpy(type, "sline"); break; 504 case N_DSLINE: strcpy(type, "dsline"); break; 505 case N_BSLINE: strcpy(type, "bsline"); break; 506 case N_SSYM: strcpy(type, "ssym"); break; 507 case N_SO: strcpy(type, "so"); break; 508 case N_LSYM: strcpy(type, "lsym"); break; 509 case N_BINCL: strcpy(type, "bincl"); break; 510 case N_SOL: strcpy(type, "sol"); break; 511 case N_PSYM: strcpy(type, "psym"); break; 512 case N_EINCL: strcpy(type, "eincl"); break; 513 case N_ENTRY: strcpy(type, "entry"); break; 514 case N_LBRAC: strcpy(type, "lbrac"); break; 515 case N_EXCL: strcpy(type, "excl"); break; 516 case N_RBRAC: strcpy(type, "rbrac"); break; 517 case N_BCOMM: strcpy(type, "bcomm"); break; 518 case N_ECOMM: strcpy(type, "ecomm"); break; 519 case N_ECOML: strcpy(type, "ecoml"); break; 520 case N_LENG: strcpy(type, "leng"); break; 521 default: 522 snprintf(type, sizeof type, "%#02x", np->n_type); 523 break; 524 } 525 526 if (np->n_type & N_EXT && type[0] != '0') 527 for (p = type; *p != '\0'; ++p) 528 *p = toupper(*p); 529 530 switch (N_AUX(np)) { 531 case 0: strcpy(aux, ""); break; 532 case AUX_OBJECT: strcpy(aux, "objt"); break; 533 case AUX_FUNC: strcpy(aux, "func"); break; 534 default: snprintf(aux, sizeof aux, "%#01x", N_AUX(np)); break; 535 } 536 537 weak = N_BIND(np) == BIND_WEAK ? 'w' : ' '; 538 539 printf("%c%-6s %-4s %8lx", weak, type, aux, np->n_value); 540} 541 542static void 543dump_syms(void) 544{ 545 unsigned long i; 546 547 printf(" Symbols:\n"); 548 for (i = 0; i < sym_count; ++i) { 549 printf(" %6lu%c ", i, sym_used[i] ? '*' : ' '); 550 dump_sym(&sym_base[i]); 551 printf(" %s\n", sym_name(i)); 552 } 553} 554 555static const char * 556rtsym_name(unsigned long n) 557{ 558 assert(n < rtsym_count); 559 if (rtsym_base[n].nz_strx == 0) 560 return ""; 561 return rtstr_base + rtsym_base[n].nz_strx; 562} 563 564static const char * 565sym_name(unsigned long n) 566{ 567 assert(n < sym_count); 568 if (sym_base[n].n_un.n_strx == 0) 569 return ""; 570 return str_base + sym_base[n].n_un.n_strx; 571} 572