2018-07-16 05:59:02 +00:00
// Helper for debugging and testing.
// Based on https://github.com/kragen/stoneknifeforth/blob/702d2ebe1b/386.c
2017-12-31 06:53:08 +00:00
: ( before " End Main " )
assert ( argc > 1 ) ;
2018-06-28 04:52:09 +00:00
if ( is_equal ( argv [ 1 ] , " run " ) ) {
2018-07-17 05:18:18 +00:00
START_TRACING_UNTIL_END_OF_SCOPE ;
2018-06-28 04:52:09 +00:00
assert ( argc > 2 ) ;
reset ( ) ;
2018-07-03 22:37:45 +00:00
cerr < < std : : hex ;
2018-07-09 05:57:50 +00:00
initialize_mem ( ) ;
2018-07-10 05:43:40 +00:00
Mem_offset = CODE_START ;
2018-06-28 04:52:09 +00:00
load_elf ( argv [ 2 ] ) ;
2018-06-28 22:54:52 +00:00
while ( EIP < End_of_program ) // weak final-gasp termination check
2018-06-28 04:52:09 +00:00
run_one_instruction ( ) ;
2018-07-26 17:59:33 +00:00
dbg < < " executed past end of the world: " < < EIP < < " vs " < < End_of_program < < end ( ) ;
2018-06-28 04:52:09 +00:00
}
2017-12-31 06:53:08 +00:00
: ( code )
void load_elf ( const string & filename ) {
int fd = open ( filename . c_str ( ) , O_RDONLY ) ;
2018-06-28 23:34:47 +00:00
if ( fd < 0 ) raise < < filename . c_str ( ) < < " : open " < < perr ( ) < < ' \n ' < < die ( ) ;
2017-12-31 06:53:08 +00:00
off_t size = lseek ( fd , 0 , SEEK_END ) ;
lseek ( fd , 0 , SEEK_SET ) ;
uint8_t * elf_contents = static_cast < uint8_t * > ( malloc ( size ) ) ;
2018-06-28 23:34:47 +00:00
if ( elf_contents = = NULL ) raise < < " malloc( " < < size < < ' ) ' < < perr ( ) < < ' \n ' < < die ( ) ;
2017-12-31 06:53:08 +00:00
ssize_t read_size = read ( fd , elf_contents , size ) ;
2018-06-28 23:34:47 +00:00
if ( size ! = read_size ) raise < < " read → " < < size < < " (!= " < < read_size < < ' ) ' < < perr ( ) < < ' \n ' < < die ( ) ;
2017-12-31 06:53:08 +00:00
load_elf_contents ( elf_contents , size ) ;
free ( elf_contents ) ;
}
2018-06-28 22:21:53 +00:00
void load_elf_contents ( uint8_t * elf_contents , size_t size ) {
2017-12-31 06:53:08 +00:00
uint8_t magic [ 5 ] = { 0 } ;
memcpy ( magic , elf_contents , 4 ) ;
2018-06-28 22:22:13 +00:00
if ( memcmp ( magic , " \177 ELF " , 4 ) ! = 0 )
2018-06-28 23:34:47 +00:00
raise < < " Invalid ELF file; starts with \" " < < magic < < ' " ' < < die ( ) ;
2018-06-28 22:22:13 +00:00
if ( elf_contents [ 4 ] ! = 1 )
2018-06-28 23:34:47 +00:00
raise < < " Only 32-bit ELF files (4-byte words; virtual addresses up to 4GB) supported. \n " < < die ( ) ;
2018-06-28 22:22:13 +00:00
if ( elf_contents [ 5 ] ! = 1 )
2018-06-28 23:34:47 +00:00
raise < < " Only little-endian ELF files supported. \n " < < die ( ) ;
2018-06-28 22:22:13 +00:00
// unused: remaining 10 bytes of e_ident
uint32_t e_machine_type = u32_in ( & elf_contents [ 16 ] ) ;
if ( e_machine_type ! = 0x00030002 )
2018-06-28 23:34:47 +00:00
raise < < " ELF type/machine 0x " < < HEXWORD < < e_machine_type < < " isn't i386 executable \n " < < die ( ) ;
2018-06-28 22:22:13 +00:00
// unused: e_version. We only support version 1, and later versions will be backwards compatible.
2017-12-31 06:53:08 +00:00
uint32_t e_entry = u32_in ( & elf_contents [ 24 ] ) ;
uint32_t e_phoff = u32_in ( & elf_contents [ 28 ] ) ;
2018-06-28 22:22:13 +00:00
// unused: e_shoff
// unused: e_flags
uint32_t e_ehsize = u16_in ( & elf_contents [ 40 ] ) ;
2018-06-28 23:34:47 +00:00
if ( e_ehsize < 52 ) raise < < " Invalid binary; ELF header too small \n " < < die ( ) ;
2018-06-28 22:22:13 +00:00
uint32_t e_phentsize = u16_in ( & elf_contents [ 42 ] ) ;
uint32_t e_phnum = u16_in ( & elf_contents [ 44 ] ) ;
2018-07-26 17:59:33 +00:00
dbg < < e_phnum < < " entries in the program header, each " < < e_phentsize < < " bytes long " < < end ( ) ;
2018-06-28 22:22:13 +00:00
// unused: e_shentsize
// unused: e_shnum
// unused: e_shstrndx
2017-12-31 06:53:08 +00:00
2018-06-28 22:22:13 +00:00
for ( size_t i = 0 ; i < e_phnum ; + + i )
2018-07-03 23:36:37 +00:00
load_segment_from_program_header ( elf_contents , size , e_phoff + i * e_phentsize , e_ehsize ) ;
2017-12-31 06:53:08 +00:00
2018-07-17 05:18:18 +00:00
// initialize code and stack
Reg [ ESP ] . u = AFTER_STACK ;
Reg [ EBP ] . u = 0 ;
2017-12-31 06:53:08 +00:00
EIP = e_entry ;
}
2018-07-03 23:36:37 +00:00
void load_segment_from_program_header ( uint8_t * elf_contents , size_t size , uint32_t offset , uint32_t e_ehsize ) {
2018-06-28 22:22:13 +00:00
uint32_t p_type = u32_in ( & elf_contents [ offset ] ) ;
2018-07-26 17:59:33 +00:00
dbg < < " program header at offset " < < offset < < " : type " < < p_type < < end ( ) ;
2018-06-28 22:22:13 +00:00
if ( p_type ! = 1 ) {
2018-07-26 17:59:33 +00:00
dbg < < " ignoring segment at offset " < < offset < < " of non PT_LOAD type " < < p_type < < " (see http://refspecs.linuxbase.org/elf/elf.pdf) " < < end ( ) ;
2018-06-28 22:22:13 +00:00
return ;
}
uint32_t p_offset = u32_in ( & elf_contents [ offset + 4 ] ) ;
uint32_t p_vaddr = u32_in ( & elf_contents [ offset + 8 ] ) ;
2018-06-28 23:34:47 +00:00
if ( e_ehsize > p_vaddr ) raise < < " Invalid binary; program header overlaps ELF header \n " < < die ( ) ;
2018-06-28 22:22:13 +00:00
// unused: p_paddr
uint32_t p_filesz = u32_in ( & elf_contents [ offset + 16 ] ) ;
uint32_t p_memsz = u32_in ( & elf_contents [ offset + 20 ] ) ;
if ( p_filesz ! = p_memsz )
2018-06-28 23:34:47 +00:00
raise < < " Can't handle segments where p_filesz != p_memsz (see http://refspecs.linuxbase.org/elf/elf.pdf) \n " < < die ( ) ;
2018-06-28 22:22:13 +00:00
if ( p_offset + p_filesz > size )
2018-06-28 23:34:47 +00:00
raise < < " Invalid binary; segment at offset " < < offset < < " is too large: wants to end at " < < p_offset + p_filesz < < " but the file ends at " < < size < < ' \n ' < < die ( ) ;
2018-07-03 23:36:37 +00:00
if ( Mem . size ( ) < p_vaddr + p_memsz )
Mem . resize ( p_vaddr + p_memsz ) ;
2018-06-28 22:22:13 +00:00
if ( size > p_memsz ) size = p_memsz ;
2018-07-26 17:59:33 +00:00
dbg < < " blitting file offsets ( " < < p_offset < < " , " < < ( p_offset + p_filesz ) < < " ) to addresses ( " < < p_vaddr < < " , " < < ( p_vaddr + p_memsz ) < < ' ) ' < < end ( ) ;
2018-06-28 22:22:13 +00:00
for ( size_t i = 0 ; i < p_filesz ; + + i )
2018-07-09 05:33:15 +00:00
write_mem_u8 ( p_vaddr + i , elf_contents [ p_offset + i ] ) ;
2018-06-28 22:22:13 +00:00
if ( End_of_program < p_vaddr + p_memsz )
End_of_program = p_vaddr + p_memsz ;
}
2018-07-10 14:18:36 +00:00
: ( before " End Includes " )
// Very primitive/fixed/insecure ELF segments for now.
// code: 0x08048000 -> 0x08048fff
// data: 0x08049000 -> 0x08049fff
// heap: 0x0804a000 -> 0x0804afff
// stack: 0x0804bfff -> 0x0804b000 (downward)
const int CODE_START = 0x08048000 ;
const int SEGMENT_SIZE = 0x1000 ;
const int AFTER_STACK = 0x0804c000 ;
: ( code )
2018-07-09 05:57:50 +00:00
void initialize_mem ( ) {
2018-07-10 14:18:36 +00:00
Mem . resize ( AFTER_STACK - CODE_START ) ;
2018-07-09 05:57:50 +00:00
}
2017-12-31 06:53:08 +00:00
inline uint32_t u32_in ( uint8_t * p ) {
return p [ 0 ] | p [ 1 ] < < 8 | p [ 2 ] < < 16 | p [ 3 ] < < 24 ;
}
2018-06-28 22:22:13 +00:00
inline uint16_t u16_in ( uint8_t * p ) {
return p [ 0 ] | p [ 1 ] < < 8 ;
}
2018-06-28 23:34:47 +00:00
: ( before " End Types " )
struct perr { } ;
: ( code )
2018-07-26 03:47:41 +00:00
ostream & operator < < ( ostream & os , perr /*unused*/ ) {
2017-12-31 06:53:08 +00:00
if ( errno )
2018-06-29 00:25:41 +00:00
os < < " : " < < strerror ( errno ) ;
2018-06-28 23:34:47 +00:00
return os ;
}
: ( before " End Types " )
struct die { } ;
: ( code )
2018-07-26 03:47:41 +00:00
ostream & operator < < ( ostream & /*unused*/ , die /*unused*/ ) {
2018-06-28 23:34:47 +00:00
if ( Trace_stream ) Trace_stream - > newline ( ) ;
exit ( 1 ) ;
2017-12-31 06:53:08 +00:00
}
2018-06-28 23:34:04 +00:00
: ( before " End Includes " )
2017-12-31 06:53:08 +00:00
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <stdarg.h>
2017-12-31 18:23:08 +00:00
# include <errno.h>