veench@cs.vu.nl (Veen van CH) (02/19/88)
In the following shar file are three C-programs and a header file.
The header file is far from complete and only contains the parts I needed
to write these programs.
The first program, ast, adds a symbol table to an executable file
and uses the symbol.out file produced when compiling with the -s option:
cc -s files.c libs.a > symbol.out
Nm displays the symbol table added to an executable file.
Strip removes the symbol table from an executable file.
Dick van Veen
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# a.out.h
# ast.c
# nm.c
# strip.c
# This archive created: Fri Feb 19 13:51:14 1988
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'a.out.h'
then
echo shar: "will not over-write existing file 'a.out.h'"
else
cat << \SHAR_EOF > 'a.out.h'
/* NOTE: this file is a temporary header file and should be replaced
* by a complete one.
*/
/* a.out header file */
struct exec { /* a.out header */
unsigned char a_magic[2]; /* magic number */
unsigned char a_flags; /* flags, see below */
unsigned char a_cpu; /* cpu id */
unsigned char a_hdrlen; /* length of header */
unsigned char a_unused; /* reserved for future use */
unsigned short a_version; /* version stamp */
/* not used */
long a_text; /* size of text segement in bytes */
long a_data; /* size of data segment in bytes */
long a_bss; /* size of bss segment in bytes */
long a_no_entry; /* in fact: entry point, a_entry */
long a_total; /* total memory allocated */
long a_syms; /* size of symbol table */
/* SHORT FORM ENDS HERE */
long a_trsize; /* text relocation size */
long a_drsize; /* data relocation size */
};
#define A_MAGIC0 (unsigned char) 0x01
#define A_MAGIC1 (unsigned char) 0x03
#define BADMAG(X) ((X).a_magic[0] != A_MAGIC0 ||\
(X).a_magic[1] != A_MAGIC1)
/* CPU Id of TARGET machine */
/* byte order coded in low order two bits */
#define A_NONE 0x00 /* unknown */
#define A_I8086 0x04 /* intel i8086/8088 */
#define A_M68K 0x0B /* motorola m68000 */
#define A_NS16K 0x0C /* national semiconductor 16032 */
#define A_BLR(cputype) ((cputype&0x01)!=0) /* TRUE if bytes left-to-right */
#define A_WLR(cputype) ((cputype&0x02)!=0) /* TRUE if words left-to-right */
/* flags: */
#define A_EXEC 0x10 /* executable */
#define A_SEP 0x20 /* separate I/D */
#define A_PURE 0x40 /* pure text */ /* not used */
#define A_TOVLY 0x80 /* text overlay */ /* not used */
/* offsets of various things: */
#define A_MINHDR 32
#define A_TEXTPOS(X) (((X).a_flags&A_TOFF)?(X).a_toffs:(long)(X).a_hdrlen)
#define A_DATAPOS(X) (A_TEXTPOS(X) + (X).a_text)
#define A_HASRELS(X) ((X).a_hdrlen > (unsigned char) A_MINHDR)
#define A_HASEXT(X) ((X).a_hdrlen > (unsigned char) (A_MINHDR + 8))
#define A_HASLNS(X) ((X).a_hdrlen > (unsigned char) (A_MINHDR + 16))
#define A_HASTOFF(X) ((X).a_hdrlen > (unsigned char) (A_MINHDR + 24))
#define A_TRELPOS(X) (A_DATAPOS(X) + (X).a_data)
#define A_DRELPOS(X) (A_TRELPOS(X) + (X).a_trsize)
#define A_SYMPOS(X) (A_TRELPOS(X) + (A_HASRELS(X) ? \
((X).a_trsize + (X).a_drsize) : 0))
struct reloc {
long r_vaddr; /* virtual address of reference */
unsigned short r_symndx; /* internal segnum or extern symbol num */
unsigned short r_type; /* relocation type */
};
/* r_tyep values: */
#define R_ABBS 0
#define R_RELLBYTE 2
#define R_PCRBYTE 3
#define R_RELWORD 4
#define R_PCRWORD 5
#define R_RELLONG 6
#define R_PCRLONG 7
#define R_REL3BYTE 8
#define R_KBRANCHE 9
/* r_symndx for internal segments */
#define S_ABS ((unsigned short)-1)
#define S_TEXT ((unsigned short)-2)
#define S_DATA ((unsigned short)-3)
#define S_BSS ((unsigned short)-4)
struct nlist { /* symbol table entry */
char n_name[8]; /* symbol name */
long n_value; /* value */
unsigned char n_sclass; /* storage class */
unsigned char n_numaux; /* number of auxiliary entries */
/* not used */
unsigned short n_type; /* language base and derived type */
/* not used */
};
/* low bits of storage class (section) */
#define N_SECT 07 /* section mask */
#define N_UNDF 00 /* undefined */
#define N_ABS 01 /* absolute */
#define N_TEXT 02 /* text */
#define N_DATA 03 /* data */
#define N_BSS 04 /* bss */
#define N_COMM 05 /* (common) */
/* high bits of storage class */
#define N_CLASS 0370 /* storage class mask */
#define C_NULL
#define C_EXT 0020 /* external symbol */
#define C_STAT 0030 /* static */
/* there are many others, but they are not supported */
SHAR_EOF
fi
if test -f 'ast.c'
then
echo shar: "will not over-write existing file 'ast.c'"
else
cat << \SHAR_EOF > 'ast.c'
/* ast - add symbol table. Author: Dick van Veen, veench@cs.vu.nl */
#include <a.out.h>
#include <stdio.h>
/*
* Since the a.out file in MINIX does not contain any symbol table,
* we use the symbol table produced with the -s option of asld.
*
* Read symbol table in memory, remove compiler generated labels,
* sort the labels and add it to the a.out file.
*
* When finally there comes a real as and ld, we may also get
* a symbol table in the a.out file, and we can forget this program.
*
*/
/*
* ast [flags] [file] [symbolfile]
*
* flags:
* -x do not preserve local symbols
* -X preserve local symbols except for those whose name begin
* with 'I', these are compiler generated.
*
* - when no symbol file is present, symbol.out is assumed.
* - when no file is present, a.out is assumed.
* - when one file name is present it must be the executable file
* - just one flag may be pressent.
*
*/
#define A_OUT "a.out"
#define SYMBOL_FILE "symbol.out" /* contains symbol table */
#define LINE_LENGTH 24
#define WORTH_LESS 1 /* lines contain no symbol */
#define LAST_LINE 2 /* end of file reached */
struct exec header; /* header info of a.out file */
int x_flag; /* flags to ast */
int X_flag;
int o_flag;
char *s_file, *o_file; /* names of files used by ast */
FILE *s_fd, *o_fd; /* file descriptors of those files */
int nr_symbols; /* number of symbols added */
char buffer[LINE_LENGTH]; /* contains line of symbol file */
char io_buf[BUFSIZ]; /* for buffered output on stderr */
unsigned int get_value(); /* forward definition */
main(argc, argv)
int argc;
char **argv;
{
extern FILE *fopen();
argv++;
if (*argv != NULL && **argv == '-') {
*argv += 1;
if (**argv == 'x') x_flag = 1;
else if (**argv == 'X') X_flag = 1;
else {
fprintf(stderr, "illegal flag: -%c\n", **argv);
Exit(-1);
}
argv++;
}
if (*argv != NULL) {
o_file = *argv;
argv++;
}
if (*argv != NULL) {
s_file = *argv;
argv++;
}
if (*argv != NULL) {
fprintf(stderr, "Usage: ast [-{x,X}] [file] [symbolfile]\n");
Exit(-1);
}
if (o_file == NULL) o_file = A_OUT;
o_fd = fopen(o_file, "a");
if (o_fd == NULL) {
fprintf(stderr, "can't open %s\n", o_file);
Exit(-1);
}
if (s_file == NULL) s_file = SYMBOL_FILE;
s_fd = fopen(s_file, "r");
if (s_fd == NULL) {
fprintf(stderr, "can't open %s\n", s_file);
Exit(-1);
}
setbuf(s_fd, io_buf);
ast(s_fd, o_fd);
Exit(0);
}
Exit(val)
int val;
{
_cleanup();
exit(val);
}
ast(s_fd, o_fd)
FILE *s_fd, *o_fd;
{
struct nlist symbol;
int line_type;
do_header();
for(;;) {
read_line(s_fd, buffer);
line_type = transform_line(buffer, &symbol);
if (line_type == WORTH_LESS) continue;
if (line_type == LAST_LINE) break;
save_line(o_fd, &symbol);
}
redo_header(o_fd);
}
read_line(fd, buffer)
FILE *fd;
char *buffer;
{
char ch;
char *buf1;
buf1 = buffer;
*buffer = '\n';
ch = fgetc(fd);
while (ch != '\n' && ch != EOF) {
*buffer = ch;
buffer++;
ch = fgetc(fd);
}
if (ch == EOF)
*buffer = '\0';
else *buffer = '\n';
buffer[1] = '\0';
}
transform_line(buffer, symbol)
char *buffer;
struct nlist *symbol;
{
switch(*buffer) {
case 'a': /* absolute symbol */
symbol->n_sclass = N_ABS;
break;
case 'A':
symbol->n_sclass = N_ABS | C_EXT;
break;
case 'u': /* undefined symbol */
symbol->n_sclass = N_UNDF;
break;
case 'U':
symbol->n_sclass = N_UNDF | C_EXT;
break;
case 't': /* text symbol */
symbol->n_sclass = N_TEXT;
break;
case 'T':
symbol->n_sclass = N_TEXT | C_EXT;
break;
case 'd':
symbol->n_sclass = N_DATA;
case 'D': /* data symbol */
symbol->n_sclass = N_DATA | C_EXT;
break;
case 'b':
symbol->n_sclass = N_BSS;
case 'B': /* bss symbol */
symbol->n_sclass = N_BSS | C_EXT;
break;
case '\0': /* reached end of file */
return(LAST_LINE);
default: /* one of first two lines */
return(WORTH_LESS);
}
if (buffer[1] != ' ') {
fprintf(stderr, "illegal file format\n");
Exit(-1);
}
symbol->n_value = get_value(buffer + 2);
if (buffer[6] != ' ') {
fprintf(stderr, "illegal file format\n");
Exit(-1);
}
get_name(buffer + 7, symbol->n_name);
return(0); /* yeah, found a symbol */
}
save_line(fd, symbol)
FILE *fd;
struct nlist *symbol;
{
if (!(symbol->n_sclass & C_EXT)) { /* local symbol */
if (x_flag) return;
if (X_flag && symbol->n_name[0] == 'I') return;
}
if (fwrite(symbol, sizeof(struct nlist), 1, fd) != 1) {
fprintf(stderr, "can't write %s\n", o_file);
Exit(-1);
}
nr_symbols++;
}
unsigned get_value(string)
char *string;
{
unsigned value;
int shift, bits;
value = 0;
for (shift = 0; shift < 16; shift += 4) {
bits = get_bits(*string);
value = (value << 4) | bits;
string++;
}
return(value);
}
get_bits(ch)
char ch;
{
if (ch >= '0' && ch <= '9')
return (ch - '0');
if (ch >= 'A' && ch <= 'F')
return (ch - 'A' + 10);
if (ch >= 'a' && ch <= 'f')
return (ch - 'a' + 10);
fprintf(stderr, "illegal file format\n");
Exit(-1);
}
get_name(str1, str2)
register char *str1, *str2;
{
int count;
for (count = 0; count < 8; count++) {
if (*str1 == '\n') break;
*str2++ = *str1++;
}
while (count < 8) {
*str2++ = '\0';
count++;
}
}
do_header()
{
int fd;
fd = open(o_file, 0);
if (read(fd, &header, sizeof(struct exec)) != sizeof(struct exec)) {
fprintf(stderr, "%s: no executable file\n", o_file);
Exit(-1);
}
if (BADMAG(header)) {
fprintf(stderr, "%s: bad header\n", o_file);
Exit(-1);
}
if (header.a_syms != 0L) {
fprintf(stderr, "%s: symbol table is installed\n", o_file);
Exit(-1);
}
fseek(o_fd, A_SYMPOS(header), 0);
nr_symbols = 0;
close(fd);
}
redo_header(fd)
FILE *fd;
{
header.a_syms = nr_symbols * sizeof(struct nlist);
fseek(fd, 0L, 0);
if (fwrite(&header, sizeof(header), 1, fd) != 1) {
fprintf(stderr, "%s: can't write\n", o_file);
Exit(-1);
}
}
SHAR_EOF
fi
if test -f 'nm.c'
then
echo shar: "will not over-write existing file 'nm.c'"
else
cat << \SHAR_EOF > 'nm.c'
/* nm - print name list. Author: Dick van Veen, veench@cs.vu.nl */
#include <a.out.h>
#include <stdio.h>
/*
* Read the name list in memory, sort it, and print it.
*
*/
/*
* nm [-gnopru] [file] ...
*
* flags:
* -g print only external symbols.
* -n sort numerically rather than alphabetically.
* -o prepend file name to each line rather than only once.
* -p don't sort, pint n symbol-table order.
* -r sort in reverse order.
* -u print only undefined symbols.
*
* - when no file name is present, a.out is assumed.
*
* NOTE: no archives are supported because assembly files don't
* have symbol tables.
*
*/
#define A_OUT "a.out"
int g_flag;
int n_flag;
int o_flag;
int p_flag;
int r_flag;
int u_flag;
char io_buf[BUFSIZ]; /* io buffer */
struct exec header; /* header of a.out file */
int stbl_elems; /* #elements in symbol table */
main(argc, argv)
int argc;
char **argv;
{
argv++;
while (*argv != 0 && **argv == '-') {
*argv += 1;
while (**argv != '\0') {
switch (**argv) {
case 'g':
g_flag = 1;
break;
case 'n':
n_flag = 1;
break;
case 'o':
o_flag = 1;
break;
case 'p':
p_flag = 1;
break;
case 'r':
r_flag = 1;
break;
case 'u':
u_flag = 1;
break;
default:
fprintf(stderr, "illegal flag: -%c\n", **argv);
Exit(-1);
}
*argv += 1;
}
argv++;
}
setbuf(stdin, io_buf);
if (*argv == 0) nm(A_OUT);
else while (*argv != 0) {
nm(*argv);
argv++;
}
Exit(0);
}
Exit(val)
int val;
{
_cleanup();
exit(val);
}
nm_sort(stbl1, stbl2)
struct nlist *stbl1, *stbl2;
{
int cmp;
if (n_flag) { /* sort numerically */
if ((stbl1->n_sclass & N_SECT) <
(stbl2->n_sclass & N_SECT)) cmp = -1;
else if ((stbl1->n_sclass & N_SECT) >
(stbl2->n_sclass & N_SECT)) cmp = 1;
else if (stbl1->n_value < stbl2->n_value)
cmp = -1;
else if (stbl1->n_value > stbl2->n_value)
cmp = 1;
else cmp = strncmp(stbl1->n_name, stbl2->n_name, 8);
} else {
cmp = strncmp(stbl1->n_name, stbl2->n_name, 8);
if (cmp == 0) {
if (stbl1->n_value < stbl2->n_value)
cmp = -1;
else if (stbl1->n_value > stbl2->n_value)
cmp = 1;
}
}
if (r_flag) cmp = -cmp; /* reverse sort */
return(cmp);
}
nm(file)
char *file;
{
struct nlist *stbl;
int fd;
fd = open(file, 0);
if (fd == -1) {
fprintf(stderr, "can't open %s\n", file);
return;
}
if (read_header(fd)) {
fprintf(stderr, "%s: no executable file\n", file);
return;
}
stbl = (struct nlist *) malloc((int) (header.a_syms & 0xFFFF));
if (stbl == NULL) {
fprintf(stderr, "%s: can't allocate symbol table\n", file);
return;
}
if (read(fd, stbl, (int) (header.a_syms & 0xFFFF))
!= (int) (header.a_syms & 0xFFFF)) {
fprintf(stderr, "%s: can't read symbol table\n", file);
return;
}
stbl_elems = (int) header.a_syms/sizeof(struct nlist);
if (!p_flag) qsort(stbl, stbl_elems, sizeof(struct nlist), nm_sort);
nm_print(file, stbl);
close(fd);
}
read_header(fd)
int fd;
{
if (read(fd, &header, sizeof(struct exec)) != sizeof(struct exec))
return(1);
if (BADMAG(header)) return(1);
lseek(fd, A_SYMPOS(header), 0);
return(0);
}
nm_print(file, stbl)
char *file;
register struct nlist *stbl;
{
struct nlist *last;
char name[9];
int n_sclass;
char type;
name[8] = '\0';
if (!o_flag) printf("%s:\n", file);
for (last = &stbl[stbl_elems]; stbl != last; stbl++) {
if (g_flag && !(stbl->n_sclass & C_EXT)) continue;
if (u_flag && stbl->n_sclass & N_SECT != N_UNDF) continue;
n_sclass = stbl->n_sclass & N_SECT;
if (n_sclass == N_ABS) type = 'a';
else if (n_sclass == N_TEXT) type = 't';
else if (n_sclass == N_DATA) type = 'd';
else if (n_sclass == N_BSS) type = 'b';
else type = 'u';
if (stbl->n_sclass & C_EXT) type += 'A' -'a';
strncpy(name, stbl->n_name, 8);
if (o_flag) printf("%s:%04X %c %s\n", file,
stbl->n_value, type, name);
else printf("%04X %c %s\n", stbl->n_value, type, name);
}
}
SHAR_EOF
fi
if test -f 'strip.c'
then
echo shar: "will not over-write existing file 'strip.c'"
else
cat << \SHAR_EOF > 'strip.c'
/* strip - remove symbols. Author: Dick van Veen, veench@cs.vu.nl */
#include <a.out.h>
#include <stdio.h>
#include <stat.h>
/*
* strip [file] ...
*
* - when no file is present, a.out is assumed.
*
*/
#define A_OUT "a.out"
#define NAME_LENGTH 128 /* max file path name */
char buffer[BUFSIZ]; /* used to copy executable */
char new_file[NAME_LENGTH]; /* contains name of temporary */
struct exec header;
main(argc, argv)
int argc;
char **argv;
{
argv++;
if (*argv == NULL) strip(A_OUT);
else while (*argv != NULL) {
strip(*argv);
argv++;
}
exit(0);
}
strip(file)
char *file;
{
int fd, new_fd;
struct stat buf;
fd = open(file, 0);
if (fd == -1) {
fprintf(stderr, "can't open %s\n", file);
close(fd);
return;
}
if (read_header(fd)) {
fprintf(stderr, "%s: not an executable file\n", file);
close(fd);
return;
}
if (header.a_syms == 0L) {
close(fd); /* no symbol table present */
return;
}
header.a_syms = 0L; /* remove table size */
fstat(fd, &buf);
new_fd = make_tmp(new_file, file);
if (new_fd == -1) {
fprintf(stderr, "can't create temporary file\n");
close(fd);
return;
}
if (write_header(new_fd)) {
fprintf(stderr, "%s: can't write temporary file\n");
unlink(new_file);
close(fd);
close(new_fd);
return;
}
if (copy_file(fd, new_fd, header.a_text + header.a_data)) {
fprintf(stderr, "can't copy %s\n", file);
unlink(new_file);
close(fd);
close(new_fd);
return;
}
close(fd);
close(new_fd);
if (unlink(file) == -1) {
fprintf(stderr, "can't unlink %s\n", file);
unlink(new_file);
return;
}
link(new_file, file);
unlink(new_file);
chmod(file, buf.st_mode);
}
read_header(fd)
int fd;
{
if (read(fd, &header, A_MINHDR) != A_MINHDR) return(1);
if (BADMAG(header)) return(1);
if (header.a_hdrlen > sizeof(struct exec)) return(1);
lseek(fd, 0L, 0); /* variable size header */
if (read(fd, &header, (int) header.a_hdrlen) != (int) header.a_hdrlen)
return(1);
return(0);
}
write_header(fd)
int fd;
{
lseek(fd, 0L, 0);
if(write(fd, &header, (int)header.a_hdrlen) != (int)header.a_hdrlen)
return(1);
return(0);
}
int make_tmp(new_name, name)
char *new_name, *name;
{
int len;
char *nameptr;
extern char *rindex();
len = strlen(name);
if (len + 1 > NAME_LENGTH) return(-1);
strcpy(new_name, name);
nameptr = rindex(new_name, '/');
if (nameptr == NULL) nameptr = new_name-1;
if (nameptr - new_name + 6 + 1 > NAME_LENGTH) return(-1);
strcpy(nameptr+1, "XXXXXX");
mktemp(new_name);
return(creat(new_name, 0777));
}
copy_file(fd1, fd2, size)
int fd1, fd2;
long size;
{
long count;
int length;
count = 0;
while (count < size) {
length = (int) (size - count);
if (length > sizeof(buffer)) length = sizeof(buffer);
length = read(fd1, buffer, length);
if (length == 0) break;
if (write(fd2, buffer, length) != length) return(1);
count += length;
}
if (count < size) return(1);
return(0);
}
SHAR_EOF
fi
exit 0
# End of shell archive