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