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