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