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