sods.c revision 29042
168651Skris/*
268651Skris * Copyright (C) 1996-1997 John D. Polstra.  All rights reserved.
368651Skris *
468651Skris * Redistribution and use in source and binary forms, with or without
568651Skris * modification, are permitted provided that the following conditions
668651Skris * are met:
768651Skris * 1. Redistributions of source code must retain the above copyright
8296465Sdelphij *    notice, this list of conditions and the following disclaimer.
968651Skris * 2. Redistributions in binary form must reproduce the above copyright
1068651Skris *    notice, this list of conditions and the following disclaimer in the
1168651Skris *    documentation and/or other materials provided with the distribution.
1268651Skris *
1368651Skris * THIS SOFTWARE IS PROVIDED BY JOHN D. POLSTRA AND CONTRIBUTORS ``AS IS'' AND
1468651Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15296465Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1668651Skris * ARE DISCLAIMED.  IN NO EVENT SHALL JOHN D. POLSTRA OR CONTRIBUTORS BE LIABLE
1768651Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1868651Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1968651Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2068651Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2168651Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22296465Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2368651Skris * SUCH DAMAGE.
2468651Skris *
2568651Skris * $Id: sods.c,v 1.4 1997/02/22 15:46:44 peter Exp $
2668651Skris */
2768651Skris
2868651Skris#include <assert.h>
2968651Skris#include <ctype.h>
3068651Skris#include <fcntl.h>
3168651Skris#include <stdarg.h>
3268651Skris#include <stdio.h>
3368651Skris#include <stdlib.h>
3468651Skris#include <string.h>
3568651Skris#include <unistd.h>
3668651Skris
37296465Sdelphij#include <sys/types.h>
3868651Skris#include <sys/mman.h>
3968651Skris#include <sys/stat.h>
40296465Sdelphij
4168651Skris#include <a.out.h>
4268651Skris#include <link.h>
4368651Skris#include <stab.h>
4468651Skris
4568651Skris#define PAGE_SIZE	4096	/* i386 specific */
4668651Skris
4768651Skris#ifndef N_SETA
4868651Skris#define	N_SETA	0x14		/* Absolute set element symbol */
4968651Skris#endif				/* This is input to LD, in a .o file.  */
5068651Skris
5168651Skris#ifndef N_SETT
52296465Sdelphij#define	N_SETT	0x16		/* Text set element symbol */
5368651Skris#endif				/* This is input to LD, in a .o file.  */
5468651Skris
5568651Skris#ifndef N_SETD
5668651Skris#define	N_SETD	0x18		/* Data set element symbol */
5768651Skris#endif				/* This is input to LD, in a .o file. */
5868651Skris
5968651Skris#ifndef N_SETB
6068651Skris#define	N_SETB	0x1A		/* Bss set element symbol */
6168651Skris#endif				/* This is input to LD, in a .o file. */
6268651Skris
63160814Ssimon#ifndef N_SETV
6468651Skris#define N_SETV	0x1C		/* Pointer to set vector in data area. */
6568651Skris#endif				/* This is output from LD. */
6668651Skris
6768651Skris#ifdef STANDALONE
6868651Skrisstatic
6968651Skris#endif
7068651Skrisvoid dump_file(const char *);
7168651Skris
7268651Skrisstatic void dump_rels(const char *, const struct relocation_info *,
7368651Skris    unsigned long, const char *(*)(unsigned long), unsigned char *);
7468651Skrisstatic void dump_segs();
75296465Sdelphijstatic void dump_sods();
7668651Skrisstatic void dump_sym(const struct nlist *);
7768651Skrisstatic void dump_syms();
78296465Sdelphij
7968651Skrisstatic void dump_rtsyms();
8068651Skris
8168651Skrisstatic void error(const char *, ...);
8268651Skrisstatic const char *rtsym_name(unsigned long);
8368651Skrisstatic const char *sym_name(unsigned long);
8468651Skris
85109998Smarkm#ifdef STANDALONE
86109998Smarkmstatic
87109998Smarkm#endif
88109998Smarkmint error_count;
89109998Smarkm
9068651Skris/*
91296465Sdelphij * Variables ending in _base are pointers to things in our address space,
9268651Skris * i.e., in the file itself.
9368651Skris *
94296465Sdelphij * Variables ending in _addr are adjusted according to where things would
95296465Sdelphij * actually appear in memory if the file were loaded.
96296465Sdelphij */
97296465Sdelphijstatic const char *file_base;
98296465Sdelphijstatic const char *text_base;
99296465Sdelphijstatic const char *data_base;
100296465Sdelphijstatic const struct relocation_info *rel_base;
101296465Sdelphijstatic const struct nlist *sym_base;
102296465Sdelphijstatic const char *str_base;
103296465Sdelphij
104296465Sdelphijstatic const struct relocation_info *rtrel_base;
10568651Skrisstatic const struct nzlist *rtsym_base;
10668651Skrisstatic const char *rtstr_base;
107296465Sdelphij
108296465Sdelphijstatic const struct exec *ex;
109296465Sdelphijstatic const struct _dynamic *dyn;
110296465Sdelphijstatic const struct section_dispatch_table *sdt;
111296465Sdelphij
112296465Sdelphijstatic const char *text_addr;
113296465Sdelphijstatic const char *data_addr;
114296465Sdelphij
115296465Sdelphijstatic unsigned long rel_count;
116296465Sdelphijstatic unsigned long sym_count;
117296465Sdelphij
11868651Skrisstatic unsigned long rtrel_count;
11968651Skrisstatic unsigned long rtsym_count;
120296465Sdelphij
121296465Sdelphij/* Dynamically allocated flags, 1 byte per symbol, to record whether each
122296465Sdelphij   symbol was referenced by a relocation entry. */
123296465Sdelphijstatic unsigned char *sym_used;
12468651Skrisstatic unsigned char *rtsym_used;
125296465Sdelphij
126296465Sdelphijstatic unsigned long origin;	/* What values are relocated relative to */
127296465Sdelphij
12868651Skris#ifdef STANDALONE
12968651Skrismain(int argc, char *argv[])
130296465Sdelphij{
131296465Sdelphij    int i;
13268651Skris
133296465Sdelphij    for (i = 1;  i < argc;  ++i)
134296465Sdelphij	dump_file(argv[i]);
135296465Sdelphij
136296465Sdelphij    return error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
137296465Sdelphij}
138296465Sdelphij#endif
139296465Sdelphij
140296465Sdelphij#ifdef STANDALONE
141296465Sdelphijstatic
14268651Skris#endif
143296465Sdelphijvoid
144296465Sdelphijdump_file(const char *fname)
145296465Sdelphij{
14668651Skris    int fd;
147296465Sdelphij    struct stat sb;
148296465Sdelphij    caddr_t objbase;
149296465Sdelphij
15068651Skris    if (stat(fname, &sb) == -1) {
151296465Sdelphij	error("Cannot stat \"%s\"", fname);
152296465Sdelphij	return;
15368651Skris    }
15468651Skris
155296465Sdelphij    if ((sb.st_mode & S_IFMT) != S_IFREG) {
156296465Sdelphij	error("\"%s\" is not a regular file", fname);
157296465Sdelphij	return;
15868651Skris    }
159296465Sdelphij
160296465Sdelphij    if ((fd = open(fname, O_RDONLY, 0)) == -1) {
161296465Sdelphij	error("Cannot open \"%s\"", fname);
16268651Skris	return;
163296465Sdelphij    }
164296465Sdelphij
16568651Skris    objbase = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
16668651Skris    if (objbase == (caddr_t) -1) {
167296465Sdelphij	error("Cannot mmap \"%s\"", fname);
168296465Sdelphij	close(fd);
169296465Sdelphij	return;
170296465Sdelphij    }
171296465Sdelphij
172296465Sdelphij    close(fd);
173296465Sdelphij
17468651Skris    file_base = (const char *) objbase;	/* Makes address arithmetic easier */
17568651Skris
176296465Sdelphij    ex = (const struct exec *) file_base;
177296465Sdelphij
178296465Sdelphij    printf("%s: a_midmag = 0x%lx\n", fname, ex->a_midmag);
179296465Sdelphij    printf("  magic = 0x%lx = 0%lo, netmagic = 0x%lx = 0%lo\n",
180296465Sdelphij	N_GETMAGIC(*ex), N_GETMAGIC(*ex),
181296465Sdelphij	N_GETMAGIC_NET(*ex), N_GETMAGIC_NET(*ex));
18268651Skris
183109998Smarkm    if (N_BADMAG(*ex)) {
184296465Sdelphij	error("%s: Bad magic number", fname);
185296465Sdelphij	munmap(objbase, sb.st_size);
186296465Sdelphij	return;
187109998Smarkm    }
188109998Smarkm
189296465Sdelphij    printf("  a_text   = 0x%lx\n", ex->a_text);
190109998Smarkm    printf("  a_data   = 0x%lx\n", ex->a_data);
191296465Sdelphij    printf("  a_bss    = 0x%lx\n", ex->a_bss);
192109998Smarkm    printf("  a_syms   = 0x%lx\n", ex->a_syms);
193296465Sdelphij    printf("  a_entry  = 0x%lx\n", ex->a_entry);
194296465Sdelphij    printf("  a_trsize = 0x%lx\n", ex->a_trsize);
195296465Sdelphij    printf("  a_drsize = 0x%lx\n", ex->a_drsize);
196296465Sdelphij
197296465Sdelphij    text_base = file_base + N_TXTOFF(*ex);
198296465Sdelphij    data_base = file_base + N_DATOFF(*ex);
199296465Sdelphij    rel_base = (const struct relocation_info *) (file_base + N_RELOFF(*ex));
200109998Smarkm    sym_base = (const struct nlist *) (file_base + N_SYMOFF(*ex));
201296465Sdelphij    str_base = file_base + N_STROFF(*ex);
202296465Sdelphij
203109998Smarkm    rel_count = (ex->a_trsize + ex->a_drsize) / sizeof rel_base[0];
204296465Sdelphij    assert(rel_count * sizeof rel_base[0] == ex->a_trsize + ex->a_drsize);
205296465Sdelphij    sym_count = ex->a_syms / sizeof sym_base[0];
206109998Smarkm    assert(sym_count * sizeof sym_base[0] == ex->a_syms);
207109998Smarkm
208296465Sdelphij    if (sym_count != 0) {
209109998Smarkm	sym_used = (unsigned char *) calloc(sym_count, sizeof(unsigned char));
210296465Sdelphij	assert(sym_used != NULL);
211296465Sdelphij    }
212296465Sdelphij
213296465Sdelphij    printf("  Entry = 0x%lx\n", ex->a_entry);
214296465Sdelphij    printf("  Text offset = %x, address = %x\n", N_TXTOFF(*ex),
215296465Sdelphij	N_TXTADDR(*ex));
216296465Sdelphij    printf("  Data offset = %lx, address = %lx\n", N_DATOFF(*ex),
217296465Sdelphij	N_DATADDR(*ex));
218296465Sdelphij
219296465Sdelphij    /*
220296465Sdelphij     * In an executable program file, everything is relocated relative to
221296465Sdelphij     * the assumed run-time load address, i.e., N_TXTADDR(*ex), i.e., 0x1000.
222296465Sdelphij     *
223296465Sdelphij     * In a shared library file, everything is relocated relative to the
22468651Skris     * start of the file, i.e., N_TXTOFF(*ex), i.e., 0.
225296465Sdelphij     *
226296465Sdelphij     * The way to tell the difference is by looking at ex->a_entry.   If it
227296465Sdelphij     * is >= 0x1000, then we have an executable program.  Otherwise, we
228296465Sdelphij     * have a shared library.
22968651Skris     *
230296465Sdelphij     * When a program is executed, the entire file is mapped into memory,
231296465Sdelphij     * including the a.out header and so forth.  But it is not mapped at
232296465Sdelphij     * address 0; rather it is mapped at address 0x1000.  The first page
233296465Sdelphij     * of the user's address space is left unmapped in order to catch null
234296465Sdelphij     * pointer dereferences.
235296465Sdelphij     *
23668651Skris     * In this program, when we map in an executable program, we have to
237296465Sdelphij     * simulate the empty page by decrementing our assumed base address by
238296465Sdelphij     * a pagesize.
239296465Sdelphij     */
240296465Sdelphij
24168651Skris    text_addr = text_base;
242296465Sdelphij    data_addr = data_base;
243296465Sdelphij    origin = 0;
244296465Sdelphij
245296465Sdelphij    if (ex->a_entry >= PAGE_SIZE) {	/* Executable, not a shared library */
246296465Sdelphij	/*
247296465Sdelphij	 * The fields in the object have already been relocated on the
24868651Skris	 * assumption that the object will be loaded at N_TXTADDR(*ex).
249296465Sdelphij	 * We have to compensate for that.
250296465Sdelphij	 */
251296465Sdelphij	text_addr -= PAGE_SIZE;
252296465Sdelphij	data_addr -= PAGE_SIZE;
253296465Sdelphij	origin = PAGE_SIZE;
254296465Sdelphij	printf("  Program, origin = %lx\n", origin);
255296465Sdelphij    } else if (N_GETFLAG(*ex) & EX_DYNAMIC)
256296465Sdelphij	printf("  Shared library, origin = %lx\n", origin);
257296465Sdelphij    else
258296465Sdelphij	printf("  Object file, origin = %lx\n", origin);
259296465Sdelphij
260296465Sdelphij    if (N_GETFLAG(*ex) & EX_DYNAMIC) {
261296465Sdelphij	dyn = (const struct _dynamic *) data_base;
262296465Sdelphij	printf("  Dynamic version = %d\n", dyn->d_version);
263296465Sdelphij
264296465Sdelphij	sdt = (const struct section_dispatch_table *)
265296465Sdelphij	    (text_addr + (unsigned long) dyn->d_un.d_sdt);
266296465Sdelphij
267296465Sdelphij	rtrel_base =
268296465Sdelphij	    (const struct relocation_info *) (text_addr + sdt->sdt_rel);
269296465Sdelphij	rtrel_count = (sdt->sdt_hash - sdt->sdt_rel) / sizeof rtrel_base[0];
270296465Sdelphij	assert(rtrel_count * sizeof rtrel_base[0] ==
271296465Sdelphij	    sdt->sdt_hash - sdt->sdt_rel);
272296465Sdelphij
273296465Sdelphij	rtsym_base = (const struct nzlist *) (text_addr + sdt->sdt_nzlist);
274296465Sdelphij	rtsym_count = (sdt->sdt_strings - sdt->sdt_nzlist) /
275296465Sdelphij	    sizeof rtsym_base[0];
276296465Sdelphij	assert(rtsym_count * sizeof rtsym_base[0] ==
277296465Sdelphij	    sdt->sdt_strings - sdt->sdt_nzlist);
278296465Sdelphij
27968651Skris	if (rtsym_count != 0) {
280296465Sdelphij	    rtsym_used = (unsigned char *) calloc(rtsym_count,
28168651Skris		sizeof(unsigned char));
282296465Sdelphij	    assert(rtsym_used != NULL);
283296465Sdelphij	}
28468651Skris
285296465Sdelphij	rtstr_base = text_addr + sdt->sdt_strings;
286296465Sdelphij    }
287296465Sdelphij
288296465Sdelphij    dump_segs();
289296465Sdelphij    dump_sods();
290296465Sdelphij    dump_rels("Relocations", rel_base, rel_count, sym_name, sym_used);
291296465Sdelphij    dump_syms();
292296465Sdelphij
293296465Sdelphij    dump_rels("Run-time relocations", rtrel_base, rtrel_count, rtsym_name,
294296465Sdelphij	rtsym_used);
295296465Sdelphij    dump_rtsyms();
296296465Sdelphij
297296465Sdelphij    if (rtsym_used != NULL) {
298296465Sdelphij	free(rtsym_used);
299296465Sdelphij	rtsym_used = NULL;
300296465Sdelphij    }
301296465Sdelphij    if (sym_used != NULL) {
30268651Skris	free(sym_used);
303296465Sdelphij	sym_used = NULL;
304296465Sdelphij    }
305296465Sdelphij    munmap(objbase, sb.st_size);
306296465Sdelphij}
307296465Sdelphij
308296465Sdelphijstatic void
30968651Skrisdump_rels(const char *label, const struct relocation_info *base,
310296465Sdelphij    unsigned long count, const char *(*name)(unsigned long),
311296465Sdelphij    unsigned char *sym_used_flags)
312296465Sdelphij{
313296465Sdelphij    unsigned long i;
314296465Sdelphij
315296465Sdelphij    printf("  %s:\n", label);
316296465Sdelphij    for (i = 0;  i < count;  ++i) {
317296465Sdelphij	const struct relocation_info *r = &base[i];
318296465Sdelphij	unsigned int size;
319296465Sdelphij	char contents[16];
320296465Sdelphij
321296465Sdelphij	size = 1u << r->r_length;
322296465Sdelphij
323296465Sdelphij	if (origin <= r->r_address
324296465Sdelphij	  && r->r_address < origin + ex->a_text + ex->a_data
325296465Sdelphij	  && 1 <= size && size <= 4) {
326296465Sdelphij	    /*
327296465Sdelphij	     * XXX - This can cause unaligned accesses.  OK for the
328296465Sdelphij	     * i386, not so for other architectures.
329296465Sdelphij	     */
330296465Sdelphij	    switch (size) {
331296465Sdelphij	    case 1:
332296465Sdelphij		snprintf(contents, sizeof contents, "      [%02x]",
333296465Sdelphij		  *(unsigned char *)(text_addr + r->r_address));
334296465Sdelphij		break;
335296465Sdelphij	    case 2:
336296465Sdelphij		snprintf(contents, sizeof contents, "    [%04x]",
337296465Sdelphij		  *(unsigned short *)(text_addr + r->r_address));
338296465Sdelphij		break;
339296465Sdelphij	    case 4:
340296465Sdelphij		snprintf(contents, sizeof contents, "[%08lx]",
341296465Sdelphij		  *(unsigned long *)(text_addr + r->r_address));
342296465Sdelphij		break;
343296465Sdelphij	    }
344296465Sdelphij	} else
345296465Sdelphij	    snprintf(contents, sizeof contents, "          ");
346296465Sdelphij
347296465Sdelphij	printf("    %6lu %8x/%u %s %c%c%c%c%c%c", i,
348296465Sdelphij	    r->r_address, size,
349296465Sdelphij	    contents,
350296465Sdelphij	    r->r_extern   ? 'e' : '-',
351296465Sdelphij	    r->r_jmptable ? 'j' : '-',
352296465Sdelphij	    r->r_relative ? 'r' : '-',
353296465Sdelphij	    r->r_baserel  ? 'b' : '-',
354296465Sdelphij	    r->r_pcrel    ? 'p' : '-',
355296465Sdelphij	    r->r_copy     ? 'c' : '-');
356296465Sdelphij
357296465Sdelphij	if (r->r_extern || r->r_baserel || r->r_jmptable || r->r_copy) {
358296465Sdelphij	    printf(" %4u %s", r->r_symbolnum, name(r->r_symbolnum));
359296465Sdelphij	    sym_used_flags[r->r_symbolnum] = 1;
360296465Sdelphij	}
361296465Sdelphij
362296465Sdelphij	printf("\n");
36368651Skris    }
364296465Sdelphij}
365296465Sdelphij
366296465Sdelphijstatic void
367296465Sdelphijdump_rtsyms()
368296465Sdelphij{
369296465Sdelphij    unsigned long i;
370296465Sdelphij
371296465Sdelphij    printf("  Run-time symbols:\n");
372296465Sdelphij    for (i = 0;  i < rtsym_count;  ++i) {
373296465Sdelphij	printf("    %6lu%c ", i, rtsym_used[i] ? '*' : ' ');
374296465Sdelphij	dump_sym(&rtsym_base[i].nlist);
375296465Sdelphij	printf("/%-5ld %s\n", rtsym_base[i].nz_size, rtsym_name(i));
376296465Sdelphij    }
377296465Sdelphij}
378296465Sdelphij
37968651Skrisstatic void
380296465Sdelphijdump_segs()
381296465Sdelphij{
382296465Sdelphij    printf("  Text segment starts at address %lx\n", origin + N_TXTOFF(*ex));
383296465Sdelphij    if (N_GETFLAG(*ex) & EX_DYNAMIC) {
384296465Sdelphij	printf("    rel starts at %lx\n", sdt->sdt_rel);
385296465Sdelphij	printf("    hash starts at %lx\n", sdt->sdt_hash);
386296465Sdelphij	printf("    nzlist starts at %lx\n", sdt->sdt_nzlist);
387296465Sdelphij	printf("    strings starts at %lx\n", sdt->sdt_strings);
388296465Sdelphij    }
389296465Sdelphij
390296465Sdelphij    printf("  Data segment starts at address %lx\n", origin + N_DATOFF(*ex));
391296465Sdelphij    if (N_GETFLAG(*ex) & EX_DYNAMIC) {
392296465Sdelphij	printf("    _dynamic starts at %lx\n", origin + N_DATOFF(*ex));
393296465Sdelphij	printf("    so_debug starts at %lx\n", (unsigned long) dyn->d_debug);
39468651Skris	printf("    sdt starts at %lx\n", (unsigned long) dyn->d_un.d_sdt);
395296465Sdelphij	printf("    got starts at %lx\n", sdt->sdt_got);
396296465Sdelphij	printf("    plt starts at %lx\n", sdt->sdt_plt);
397296465Sdelphij	printf("    rest of stuff starts at %lx\n",
398296465Sdelphij	    sdt->sdt_plt + sdt->sdt_plt_sz);
39968651Skris    }
400296465Sdelphij}
401296465Sdelphij
402296465Sdelphijstatic void
403296465Sdelphijdump_sods()
404296465Sdelphij{
405296465Sdelphij    long sod_offset;
406296465Sdelphij    long paths_offset;
407296465Sdelphij
408296465Sdelphij    if (dyn == NULL)		/* Not a shared object */
409296465Sdelphij	return;
410296465Sdelphij
411296465Sdelphij    sod_offset = sdt->sdt_sods;
41268651Skris    printf("  Shared object dependencies:\n");
413296465Sdelphij    while (sod_offset != 0) {
414296465Sdelphij	const struct sod *sodp = (const struct sod *) (text_addr + sod_offset);
415296465Sdelphij	const char *name = (const char *) (text_addr + sodp->sod_name);
416296465Sdelphij
417296465Sdelphij	if (sodp->sod_library)
418296465Sdelphij	    printf("    -l%-16s version %d.%d\n", name, sodp->sod_major,
419296465Sdelphij		sodp->sod_minor);
420296465Sdelphij	else
421296465Sdelphij	    printf("    %s\n", name);
422296465Sdelphij	sod_offset = sodp->sod_next;
423296465Sdelphij    }
424296465Sdelphij    paths_offset = sdt->sdt_paths;
425296465Sdelphij    printf("  Shared object additional paths:\n");
426296465Sdelphij    if (paths_offset != 0) {
427296465Sdelphij	char *path = (char *)(text_addr + paths_offset);
428296465Sdelphij	printf("    %s\n", path);
429296465Sdelphij    } else {
430296465Sdelphij	printf("    (none)\n");
431296465Sdelphij    }
432296465Sdelphij}
433296465Sdelphij
434296465Sdelphijstatic void
435296465Sdelphijdump_sym(const struct nlist *np)
436296465Sdelphij{
437296465Sdelphij    char type[8];
438296465Sdelphij    char weak;
439296465Sdelphij    char *p;
440296465Sdelphij
441296465Sdelphij    switch (np->n_type & ~N_EXT) {
442296465Sdelphij    case N_UNDF:	strcpy(type, "undf");  break;
443296465Sdelphij    case N_ABS:		strcpy(type, "abs");  break;
44468651Skris    case N_TEXT:	strcpy(type, "text");  break;
44568651Skris    case N_DATA:	strcpy(type, "data");  break;
446296465Sdelphij    case N_BSS:		strcpy(type, "bss");  break;
447296465Sdelphij    case N_INDR:	strcpy(type, "indr");  break;
448296465Sdelphij    case N_SIZE:	strcpy(type, "size");  break;
449296465Sdelphij    case N_COMM:	strcpy(type, "comm");  break;
450296465Sdelphij    case N_SETA:	strcpy(type, "seta");  break;
451296465Sdelphij    case N_SETT:	strcpy(type, "sett");  break;
452296465Sdelphij    case N_SETD:	strcpy(type, "setd");  break;
453296465Sdelphij    case N_SETB:	strcpy(type, "setb");  break;
454296465Sdelphij    case N_SETV:	strcpy(type, "setv");  break;
455296465Sdelphij    case N_FN:		strcpy(type, np->n_type&N_EXT ? "fn" : "warn");  break;
456296465Sdelphij    case N_GSYM:	strcpy(type, "gsym");  break;
45768651Skris    case N_FNAME:	strcpy(type, "fname");  break;
458296465Sdelphij    case N_FUN:		strcpy(type, "fun");  break;
459296465Sdelphij    case N_STSYM:	strcpy(type, "stsym");  break;
460296465Sdelphij    case N_LCSYM:	strcpy(type, "lcsym");  break;
461296465Sdelphij    case N_MAIN:	strcpy(type, "main");  break;
462296465Sdelphij    case N_PC:		strcpy(type, "pc");  break;
463296465Sdelphij    case N_RSYM:	strcpy(type, "rsym");  break;
464296465Sdelphij    case N_SLINE:	strcpy(type, "sline");  break;
465296465Sdelphij    case N_DSLINE:	strcpy(type, "dsline");  break;
466296465Sdelphij    case N_BSLINE:	strcpy(type, "bsline");  break;
467296465Sdelphij    case N_SSYM:	strcpy(type, "ssym");  break;
468296465Sdelphij    case N_SO:		strcpy(type, "so");  break;
469296465Sdelphij    case N_LSYM:	strcpy(type, "lsym");  break;
470296465Sdelphij    case N_BINCL:	strcpy(type, "bincl");  break;
471296465Sdelphij    case N_SOL:		strcpy(type, "sol");  break;
472296465Sdelphij    case N_PSYM:	strcpy(type, "psym");  break;
473296465Sdelphij    case N_EINCL:	strcpy(type, "eincl");  break;
474296465Sdelphij    case N_ENTRY:	strcpy(type, "entry");  break;
475296465Sdelphij    case N_LBRAC:	strcpy(type, "lbrac");  break;
476296465Sdelphij    case N_EXCL:	strcpy(type, "excl");  break;
477296465Sdelphij    case N_RBRAC:	strcpy(type, "rbrac");  break;
478296465Sdelphij    case N_BCOMM:	strcpy(type, "bcomm");  break;
479296465Sdelphij    case N_ECOMM:	strcpy(type, "ecomm");  break;
480296465Sdelphij    case N_ECOML:	strcpy(type, "ecoml");  break;
48168651Skris    case N_LENG:	strcpy(type, "leng");  break;
48268651Skris    default:		snprintf(type, sizeof type, "0x%02x", np->n_type);
483296465Sdelphij    }
484296465Sdelphij
485296465Sdelphij    if (np->n_type & N_EXT && type[0] != '0')
486296465Sdelphij	for (p = type;  *p != '\0';  ++p)
48768651Skris	    *p = toupper(*p);
488296465Sdelphij
489296465Sdelphij    weak = N_BIND(np) == BIND_WEAK ? 'w' : ' ';
49068651Skris
491296465Sdelphij    printf("%c%-5s %8lx", weak, type, np->n_value);
492296465Sdelphij}
493296465Sdelphij
49468651Skrisstatic void
495296465Sdelphijdump_syms()
496296465Sdelphij{
497296465Sdelphij    unsigned long i;
498296465Sdelphij
499296465Sdelphij    printf("  Symbols:\n");
500296465Sdelphij    for (i = 0;  i < sym_count;  ++i) {
501296465Sdelphij	printf("    %6lu%c ", i, sym_used[i] ? '*' : ' ');
502296465Sdelphij	dump_sym(&sym_base[i]);
503296465Sdelphij	printf(" %s\n", sym_name(i));
504296465Sdelphij    }
505296465Sdelphij}
506296465Sdelphij
507296465Sdelphijstatic void
508296465Sdelphijerror(const char *format, ...)
509296465Sdelphij{
510296465Sdelphij    va_list ap;
511296465Sdelphij
512296465Sdelphij    va_start(ap, format);
513296465Sdelphij    vfprintf(stderr, format, ap);
514296465Sdelphij    va_end(ap);
515296465Sdelphij    putc('\n', stderr);
516296465Sdelphij
517296465Sdelphij    ++error_count;
518296465Sdelphij}
519296465Sdelphij
520296465Sdelphijstatic const char *
521296465Sdelphijrtsym_name(unsigned long n)
522296465Sdelphij{
523296465Sdelphij    assert(n < rtsym_count);
524296465Sdelphij    if (rtsym_base[n].nz_strx == 0)
525296465Sdelphij	return "";
526296465Sdelphij    return rtstr_base + rtsym_base[n].nz_strx;
527296465Sdelphij}
528296465Sdelphij
529296465Sdelphijstatic const char *
530296465Sdelphijsym_name(unsigned long n)
531296465Sdelphij{
532296465Sdelphij    assert(n < sym_count);
533296465Sdelphij    if (sym_base[n].n_un.n_strx == 0)
534296465Sdelphij	return "";
535296465Sdelphij    return str_base + sym_base[n].n_un.n_strx;
536296465Sdelphij}
537296465Sdelphij