Merge pull request #29 from kernigh/pr-elf-symbol

ELF symbol table from aelflod
This commit is contained in:
David Given 2016-12-14 22:30:44 +01:00 committed by GitHub
commit decf0f5e9c
4 changed files with 464 additions and 69 deletions

View file

@ -1,25 +1,58 @@
.TH ASLOD 1 "$Revision$" .TH AELFLOD 1 "$Revision$"
.SH NAME .SH NAME
aelflod \- ACK ELF loader aelflod \- ACK ELF loader
.SH SYNOPSIS .SH SYNOPSIS
aelflod [-h] [-v] inputfile outputfile .B aelflod
[-a\fInumber\fP] [-b] [-h] [-l] [-m\fInumber\fP] [-v] inputfile outputfile
.SH DESCRIPTION .SH DESCRIPTION
.I aelflod .I aelflod
converts an absolute ack.out file into a simple binary memory converts an absolute ack.out file into a simple binary memory
dump wrapped up in an ELF executable. It is suitable for producing dump wrapped up in an ELF executable.
executables for operating systems such as Linux. It is suitable for producing executables for operating systems
such as Linux.
.PP
.I aelflod
accepts the following flags:
.TP
.BI \-a number
Set the ABI in the ELF header to \fInumber\fP.
The default value is \fI3\fP for Linux.
.TP
.B \-b
Write a big-endian ELF file.
.TP
.B \-h
Print a help message and exit.
.TP
.B \-l
Write a little-endian ELF file.
This is the default.
.TP
.BI \-m number
Set the machine type in the ELF header to \fInumber\fP.
The default value is \fI3\fP for Intel 386 (i386).
Other values are \fI4\fP for Motorola 68000 (m68k)
and \fI20\fP for PowerPC.
.TP
.B \-v
Be verbose.
.PP
The input file must contain exactly four segments: TEXT, ROM, The input file must contain exactly four segments: TEXT, ROM,
DATA and BSS, in that order, all occupying contiguous memory. DATA and BSS, in that order, all occupying contiguous memory.
The file must have all references resolved and be linked to a The file must have all references resolved and be linked to a
fixed address. The fixed address must be at least 0x54 bytes fixed address.
greater than a page boundary, in order to make room for the ELF The fixed address must be at least 0x54 bytes greater than a
header itself. page boundary, in order to make room for the ELF header itself.
.PP
aelflod will write out an ELF header followed by each segment, in .I aelflod
order, ensuring that enough padding is inserted between each segment will write out an ELF header followed by each segment, in order,
to keep the offsets correct. The created executable will contain just ensuring that enough padding is inserted between each segment
one rwx segment, and no sections. to keep the offsets correct.
The created executable will contain just one ELF segment mapped rwx.
.PP
If the input file has symbols, then
.I aelflod
will convert the symbol table to ELF.
The output file has ELF section headers if and only if it has symbols.
.SH "SEE ALSO" .SH "SEE ALSO"
ack.out(5) ack.out(5)

View file

@ -38,7 +38,11 @@ int elfmachine = 3; /* machine = EM_386 */
struct outhead outhead; struct outhead outhead;
struct outsect outsect[S_MAX]; struct outsect outsect[S_MAX];
struct outname* outname = NULL;
char* stringarea; char* stringarea;
uint32_t ack_off_char;
int nstab = 0; /* S_STB symbol count */
int nsym = 0; /* other symbol count */
char* outputfile = NULL; /* Name of output file, or NULL */ char* outputfile = NULL; /* Name of output file, or NULL */
char* program; /* Name of current program: argv[0] */ char* program; /* Name of current program: argv[0] */
@ -49,23 +53,30 @@ FILE* output; /* Output stream */
#define readf(a, b, c) fread((a), (b), (int)(c), input) #define readf(a, b, c) fread((a), (b), (int)(c), input)
#define writef(a, b, c) fwrite((a), (b), (int)(c), output) #define writef(a, b, c) fwrite((a), (b), (int)(c), output)
/* Header and program header table of an ELF object file. */ /* Contents of an ELF object file. */
#define ELF_HEADER_SIZE 0x34 #define ELF_HEADER_SIZE 0x34
#define PROGRAM_HEADER_SIZE 0x20 #define PROGRAM_HEADER_SIZE 0x20
#define PROGRAM_HEADER_COUNT 1 #define PROGRAM_HEADER_COUNT 1
unsigned long codeoffset; #define SECTION_HEADER_SIZE 0x28
#define STAB_SYMBOL_SIZE 12
#define ELF_SYMBOL_SIZE 16
uint32_t code_offset; /* ELF segment */
uint32_t stab_offset; /* Debugger symbol table */
uint32_t symtab_offset; /* ELF symbol table */
uint32_t strtab_offset; /* String table */
uint32_t shstrtab_offset; /* Section header string table */
uint32_t sh_offset; /* ELF section headers */
int sh_count = 0; /* Number of ELF sections */
int shstrtab_nr = 0; /* Section number of .shstrtab */
int shstrtab_size;
const char elf_le_ident_string[] = { const char elf_le_ident_string[] = {
0x7F, 'E', 'L', 'F' 0x7F, 'E', 'L', 'F'
}; };
/* Output file definitions and such */
#define HDR_LENGTH 32
char hdr[HDR_LENGTH] ;
bool verbose = false; bool verbose = false;
/* Segment numbers understood by aelflod. */ /* Segment numbers understood by aelflod. */
@ -78,13 +89,33 @@ enum {
NUM_SEGMENTS NUM_SEGMENTS
}; };
#define N_EXT 040 /*
#define N_UNDEF 00 * ELF section numbers count up in the order that we write the ELF
#define N_ABS 01 * section headers. If we have no debugger symbols, we will skip
#define N_TEXT 02 * .stab and .stabstr, then subtract 2 from all later numbers.
#define N_DATA 03 */
#define N_BSS 04 enum {
#define N_FN 037 N_UNDEF = 0,
N_TEXT,
N_RODATA,
N_DATA,
N_BSS,
N_STAB,
N_STABSTR,
N_SYMTAB,
N_STRTAB,
N_SHSTRTAB,
NUM_ELF_SECTIONS,
};
const char shstrtab[] =
"\0.text\0.rodata\0.data\0.bss\0.stab\0.stabstr\0"
".symtab\0.strtab\0.shstrtab";
/* Compiler appends one more "\0". */
const int sh_name[] = {
/* Index of each name in shstrtab: */
0, 1, 7, 15, 21, 26, 32,
41, 49, 57,
};
/* Produce an error message and exit. */ /* Produce an error message and exit. */
@ -121,6 +152,80 @@ int follows(struct outsect* pa, struct outsect* pb)
return (pa->os_base >= align(pb->os_base+pb->os_size, pa->os_lign)); return (pa->os_base >= align(pb->os_base+pb->os_size, pa->os_lign));
} }
/* Convert a symbol's name index from ack.out to ELF. */
uint32_t cvname(struct outname* n)
{
if (n->on_foff) {
/* ack.out: offset from beginning of file
* ELF: index in string table
* the + 1 because we prepend a '\0' */
return n->on_foff - ack_off_char + 1;
} else
return 0; /* no name */
}
/* Convert a symbol's type and binding from ack.out to ELF. */
int cvinfo(struct outname* n)
{
int bind, type;
switch (n->on_type & S_ETC) {
case S_SCT:
type = 3; /* STT_SECTION */
break;
case S_FIL:
case S_MOD:
type = 4; /* STT_FILE */
break;
default:
switch (n->on_type & S_TYP) {
case S_MIN + TEXT:
type = 2; /* STT_FUNC */
break;
case S_MIN + ROM:
case S_MIN + DATA:
case S_MIN + BSS:
case S_MIN + NUM_SEGMENTS:
type = 1; /* STT_OBJECT */
break;
default:
type = 0; /* STT_NOTYPE */
break;
}
break;
}
if (n->on_type & S_EXT)
bind = 1; /* STB_GLOBAL */
else
bind = 0; /* STB_LOCAL */
return (bind << 4) | type;
}
/* Convert a symbol's section index from ack.out to ELF. */
int cvsect(struct outname* n)
{
switch (n->on_type & S_TYP) {
case S_ABS:
return 0xfff1; /* SHN_ABS */
case S_MIN + TEXT:
return N_TEXT;
case S_MIN + ROM:
return N_RODATA;
case S_MIN + DATA:
return N_DATA;
case S_MIN + BSS:
case S_MIN + NUM_SEGMENTS:
return N_BSS;
default:
return N_UNDEF;
}
}
/* Writes a byte. */ /* Writes a byte. */
void emit8(unsigned char value) void emit8(unsigned char value)
@ -220,6 +325,188 @@ void emitphdr(unsigned long address, unsigned long filesize,
fileoffset += filesize; fileoffset += filesize;
} }
/* The next few functions write parts of the symbol table. */
void emit_stab(void)
{
struct outname* n;
int i;
for (i = 0; i < outhead.oh_nname; i++) {
n = &outname[i];
if (n->on_type & S_STB) {
emit32(cvname(n)); /* name index */
emit8(n->on_type >> 8); /* type */
emit8(cvsect(n)); /* section */
emit16(n->on_desc); /* desc */
emit32(n->on_valu); /* value */
}
}
}
void emit_symtab(void)
{
struct outname* n;
int i;
for (i = 0; i < outhead.oh_nname; i++) {
n = &outname[i];
if (!(n->on_type & S_STB)) {
emit32(cvname(n)); /* name index */
emit32(n->on_valu); /* value */
emit32(0); /* size = unknown */
emit8(cvinfo(n)); /* info */
emit8(0); /* other */
emit16(cvsect(n)); /* section */
}
}
}
void emit_strtab(void)
{
/* We prepend a '\0' because ELF uses offset 0 for symbols
* without a name. */
emit8('\0');
writef(stringarea, outhead.oh_nchar, 1);
}
void emit_shstrtab(void)
{
if (nstab) {
writef(shstrtab, sizeof(shstrtab), 1);
} else {
/* Skip .stab and .stabstr */
int i = sh_name[N_SYMTAB];
writef(shstrtab, sh_name[N_STAB], 1);
writef(shstrtab + i, sizeof(shstrtab) - i, 1);
}
}
/* Writes out an ELF section header. */
void emit_sh(int i)
{
uint32_t name, type, flags, addr, offset, size, addralign,
link, entsize;
/* If no debugger symbols, skip .stab and .stabstr */
if (nstab == 0 && (i == N_STAB || i == N_STABSTR))
return;
name = sh_name[i];
if (nstab == 0 && i >= N_STAB)
name -= (sh_name[N_SYMTAB] - sh_name[N_STAB]);
switch (i) {
case N_TEXT:
case N_RODATA:
case N_DATA:
case N_STAB:
type = 1; /* SHT_PROGBITS */
break;
case N_BSS:
type = 8; /* SHT_NOBITS */
break;
case N_SYMTAB:
type = 2; /* SHT_SYMTAB */
break;
case N_STABSTR:
case N_STRTAB:
type = 3; /* SHT_STRTAB */
break;
default:
type = 0; /* SHT_NULL */
break;
}
switch (i) {
case N_TEXT:
flags = 4|2; /* SHF_EXECINSTR|SHF_ALLOC */
addr = outsect[TEXT].os_base;
offset = code_offset;
size = outsect[TEXT].os_size;
addralign = outsect[TEXT].os_lign;
break;
case N_RODATA:
flags = 2; /* SHF_ALLOC */
addr = outsect[ROM].os_base;
offset = code_offset + outsect[TEXT].os_size;
size = outsect[ROM].os_size;
addralign = outsect[ROM].os_lign;
break;
case N_DATA:
flags = 2|1; /* SHF_ALLOC|SHF_WRITE */
addr = outsect[DATA].os_base;
offset = code_offset + outsect[TEXT].os_size +
outsect[ROM].os_size;
size = outsect[DATA].os_size;
addralign = outsect[DATA].os_lign;
break;
case N_BSS:
flags = 2|1; /* SHF_ALLOC|SHF_WRITE */
addr = outsect[BSS].os_base;
offset = code_offset + outsect[TEXT].os_size +
outsect[ROM].os_size + outsect[DATA].os_size;
size = outsect[BSS].os_size;
addralign = outsect[BSS].os_lign;
break;
default:
flags = addr = offset = size = addralign = 0;
break;
}
entsize = 0;
switch (i) {
case N_STAB:
offset = stab_offset;
size = STAB_SYMBOL_SIZE * nstab;
entsize = STAB_SYMBOL_SIZE;
break;
case N_SYMTAB:
offset = symtab_offset;
size = ELF_SYMBOL_SIZE * nsym;
entsize = ELF_SYMBOL_SIZE;
break;
case N_STABSTR:
case N_STRTAB:
/* .stabstr, .strtab share the string area */
offset = strtab_offset;
/* the + 1 because we prepend a '\0' */
size = 1 + outhead.oh_nchar;
break;
case N_SHSTRTAB:
offset = shstrtab_offset;
size = shstrtab_size;
break;
}
/* Link .stab to .stabstr and .symtab to .strtab */
switch (i) {
case N_STAB:
link = N_STABSTR;
break;
case N_SYMTAB:
link = N_STRTAB;
if (nstab == 0)
link -= 2;
break;
default:
link = 0;
break;
}
emit32(name);
emit32(type);
emit32(flags);
emit32(addr);
emit32(offset);
emit32(size);
emit32(link);
emit32(0); /* info */
emit32(addralign);
emit32(entsize);
}
/* Macros from modules/src/object/obj.h */ /* Macros from modules/src/object/obj.h */
#define Xchar(ch) ((ch) & 0377) #define Xchar(ch) ((ch) & 0377)
#define uget2(c) (Xchar((c)[0]) | ((unsigned) Xchar((c)[1]) << 8)) #define uget2(c) (Xchar((c)[0]) | ((unsigned) Xchar((c)[1]) << 8))
@ -264,6 +551,57 @@ int rsect(FILE* f, struct outsect* sect)
return 1 ; return 1 ;
} }
/*
* Read the ack.out symbol table and string area. Count symbols.
* Seek back to the current file position.
*/
int rnames(FILE* f)
{
long told;
int i;
/* If no symbols, then do nothing successfully. */
if (outhead.oh_nname == 0)
return 1;
/* Seek to the symbol table. */
told = ftell(f);
if (told == -1)
return 0;
ack_off_char = OFF_CHAR(outhead); /* for cvname() */
if (fseek(f, OFF_NAME(outhead), SEEK_SET))
return 0;
/* Using calloc(a, b) to check if a * b would overflow. */
outname = calloc(outhead.oh_nname, sizeof(outname[0]));
if (outname == NULL)
fatal("out of memory.");
for (i = 0; i < outhead.oh_nname; i++) {
char buf[SZ_NAME], *c;
if (fread(buf, SZ_NAME, 1, f) != 1)
return 0;
c = buf;
outname[i].on_foff = get4(c); c += 4;
outname[i].on_type = uget2(c); c += 2;
outname[i].on_desc = uget2(c); c += 2;
outname[i].on_valu = get4(c);
if (outname[i].on_type & S_STB)
nstab++;
else
nsym++;
}
stringarea = malloc(outhead.oh_nchar);
if (stringarea == NULL)
fatal("out of memory.");
if (fread(stringarea, outhead.oh_nchar, 1, f) != 1)
return 0;
if (fseek(f, told, SEEK_SET))
return 0;
return 1;
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
/* General housecleaning and setup. */ /* General housecleaning and setup. */
@ -287,7 +625,7 @@ int main(int argc, char* argv[])
break; break;
case 'h': case 'h':
fprintf(stderr, "%s: Syntax: aelflod [-a<number>] [-b] [-h] [-l]\n\t[-m<number>] <inputfile> <outputfile>\n", fprintf(stderr, "%s: Syntax: aelflod [-a<number>] [-b] [-h] [-l]\n\t[-m<number>] [-v] <inputfile> <outputfile>\n",
program); program);
exit(0); exit(0);
@ -360,6 +698,11 @@ int main(int argc, char* argv[])
} }
} }
/* Read the symbol table, then seek back to the section data. */
if (!rnames(input))
fatal("failed to read symbol table.");
/* A few checks */ /* A few checks */
if (outsect[BSS].os_flen != 0) if (outsect[BSS].os_flen != 0)
@ -387,8 +730,8 @@ int main(int argc, char* argv[])
/* Ensure the base address doesn't overlap the file header. */ /* Ensure the base address doesn't overlap the file header. */
codeoffset = outsect[TEXT].os_base & 0x1FFF; code_offset = outsect[TEXT].os_base & 0x1FFF;
if (codeoffset < (ELF_HEADER_SIZE + PROGRAM_HEADER_SIZE*PROGRAM_HEADER_COUNT)) if (code_offset < (ELF_HEADER_SIZE + PROGRAM_HEADER_SIZE*PROGRAM_HEADER_COUNT))
fatal("base address too small --- overlaps ELF header"); fatal("base address too small --- overlaps ELF header");
/* Rationalise the memory sizes. */ /* Rationalise the memory sizes. */
@ -398,6 +741,30 @@ int main(int argc, char* argv[])
outsect[DATA].os_size = outsect[BSS ].os_base - outsect[DATA].os_base; outsect[DATA].os_size = outsect[BSS ].os_base - outsect[DATA].os_base;
outsect[BSS ].os_size = align(outsect[BSS].os_size, outsect[BSS].os_lign); outsect[BSS ].os_size = align(outsect[BSS].os_size, outsect[BSS].os_lign);
stab_offset = code_offset + outsect[TEXT].os_size +
outsect[ROM].os_size + outsect[DATA].os_size;
/* If we have symbols, then calculate some offsets. */
if (outhead.oh_nname) {
sh_count = NUM_ELF_SECTIONS;
shstrtab_nr = N_SHSTRTAB;
shstrtab_size = sizeof(shstrtab);
if (nstab == 0) {
/* Skip .stab and .stabstr */
sh_count -= 2;
shstrtab_nr -= 2;
shstrtab_size -=
(sh_name[N_SYMTAB] - sh_name[N_STAB]);
}
symtab_offset = stab_offset + STAB_SYMBOL_SIZE * nstab;
strtab_offset = symtab_offset + ELF_SYMBOL_SIZE * nsym;
/* the + 1 because we prepend a '\0' */
shstrtab_offset = strtab_offset + 1 + outhead.oh_nchar;
sh_offset = shstrtab_offset + shstrtab_size;
}
/* Write out the ELF file header. */ /* Write out the ELF file header. */
writef(elf_le_ident_string, 4, 1); writef(elf_le_ident_string, 4, 1);
@ -414,33 +781,29 @@ int main(int argc, char* argv[])
emit32(1); /* ELF version again */ emit32(1); /* ELF version again */
emit32(outsect[TEXT].os_base); /* entry point */ emit32(outsect[TEXT].os_base); /* entry point */
emit32(ELF_HEADER_SIZE); /* program header offset */ emit32(ELF_HEADER_SIZE); /* program header offset */
emit32(0); /* section header offset */ emit32(sh_offset); /* section header offset */
emit32(0); /* flags */ emit32(0); /* flags */
emit16(ELF_HEADER_SIZE); /* elf header size */ emit16(ELF_HEADER_SIZE); /* elf header size */
emit16(PROGRAM_HEADER_SIZE); /* program header entry size */ emit16(PROGRAM_HEADER_SIZE); /* program header entry size */
emit16(1); /* number of program header entries */ emit16(1); /* number of program header entries */
emit16(0x28); /* section header entry size */ emit16(SECTION_HEADER_SIZE); /* section header entry size */
emit16(0); /* number of section header entries */ emit16(sh_count); /* number of section header entries */
emit16(0); /* section header string table index = SHN_UNDEF */ emit16(shstrtab_nr); /* section header string table index */
/* Write out a single rwx section for the entire program. */ /* Write out a single rwx section for the entire program. */
{ {
unsigned long filelength = codeoffset + uint32_t filelength = stab_offset;
outsect[TEXT].os_size + uint32_t memlength = filelength + outsect[BSS].os_size;
outsect[ROM].os_size +
outsect[DATA].os_size;
unsigned long memlength = filelength + emitphdr(outsect[TEXT].os_base & ~0x1FFF,
outsect[BSS].os_size; filelength, memlength, 0, 4|2|1);
emitphdr(outsect[TEXT].os_base & ~0x1FFF, filelength, memlength,
0, 4|2|1);
} }
/* Write padding until the code start. */ /* Write padding until the code start. */
fseek(output, codeoffset, SEEK_SET); if (fseek(output, code_offset, SEEK_SET))
fatal("output seek error");
/* Write out the actual data. */ /* Write out the actual data. */
@ -448,6 +811,19 @@ int main(int argc, char* argv[])
emits(&outsect[ROM]); emits(&outsect[ROM]);
emits(&outsect[DATA]); emits(&outsect[DATA]);
/* Write out the symbol table and section headers. */
if (outhead.oh_nname) {
int i;
if (nstab)
emit_stab();
emit_symtab();
emit_strtab();
emit_shstrtab();
for (i = 0; i < NUM_ELF_SECTIONS; i++)
emit_sh(i);
}
if (ferror(output)) if (ferror(output))
fatal("output write error"); fatal("output write error");
if (outputfile) if (outputfile)
@ -459,7 +835,7 @@ int main(int argc, char* argv[])
{ {
uint32_t ss = 0; uint32_t ss = 0;
printf(" address length\n"); printf(" address length\n");
printf(" ehdr : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base & ~0x1FFF, codeoffset); printf(" ehdr : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base & ~0x1FFF, code_offset);
printf(" text : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base, outsect[TEXT].os_size); printf(" text : %08"PRIx32" %08"PRIx32"\n", outsect[TEXT].os_base, outsect[TEXT].os_size);
printf(" rom : %08"PRIx32" %08"PRIx32"\n", outsect[ROM].os_base, outsect[ROM].os_size); printf(" rom : %08"PRIx32" %08"PRIx32"\n", outsect[ROM].os_base, outsect[ROM].os_size);
printf(" data : %08"PRIx32" %08"PRIx32"\n", outsect[DATA].os_base, outsect[DATA].os_size); printf(" data : %08"PRIx32" %08"PRIx32"\n", outsect[DATA].os_base, outsect[DATA].os_size);

View file

@ -2,19 +2,19 @@
.SH NAME .SH NAME
aslod \- ACK simple loader aslod \- ACK simple loader
.SH SYNOPSIS .SH SYNOPSIS
aslod [-h] [-v] inputfile outputfile .B aslod
[-h] [-v] inputfile outputfile
.SH DESCRIPTION .SH DESCRIPTION
.I aslod .I aslod
converts an absolute ack.out file into a simple binary memory converts an absolute ack.out file into a simple binary memory dump.
dump. It is suitable for producing RAM images, executables for It is suitable for producing RAM images, executables for
simple operating systems such as CP/M, DOS, etc. simple operating systems such as CP/M, DOS, etc.
.PP
The input file must contain exactly four segments: TEXT, ROM, The input file must contain exactly four segments: TEXT, ROM,
DATA and BSS, in that order, all occupying contiguous memory. DATA and BSS, in that order, all occupying contiguous memory.
The file must have all references resolved and be linked to a The file must have all references resolved and be linked to a
fixed address. aslod will dump the segments, in order, such fixed address. aslod will dump the segments, in order, such
that the first byte of TEXT is at offset 0 in the file that the first byte of TEXT is at offset 0 in the file
(regardless of where it is in memory). (regardless of where it is in memory).
.SH "SEE ALSO" .SH "SEE ALSO"
ack.out(5) ack.out(5)

View file

@ -45,12 +45,6 @@ FILE* output; /* Output stream */
#define readf(a, b, c) fread((a), (b), (int)(c), input) #define readf(a, b, c) fread((a), (b), (int)(c), input)
#define writef(a, b, c) fwrite((a), (b), (int)(c), output) #define writef(a, b, c) fwrite((a), (b), (int)(c), output)
/* Output file definitions and such */
#define HDR_LENGTH 32
char hdr[HDR_LENGTH] ;
bool verbose = false; bool verbose = false;
/* Segment numbers understood by aslod. */ /* Segment numbers understood by aslod. */
@ -63,14 +57,6 @@ enum {
NUM_SEGMENTS NUM_SEGMENTS
}; };
#define N_EXT 040
#define N_UNDEF 00
#define N_ABS 01
#define N_TEXT 02
#define N_DATA 03
#define N_BSS 04
#define N_FN 037
/* Produce an error message and exit. */ /* Produce an error message and exit. */
void fatal(const char* s, ...) void fatal(const char* s, ...)