# IDEA # this file will have 2 parts # 1. Data definitions and "building blocks" which would work # with any writer interface # 2. Simplified thing, which strictly works by generating executable files. # but might be nice if we could use the same API to generate executables in memory... use Mem undefined use Slice (Slice) use Alloc (Allocator) use Array use Iter map # ELF64 structures for x86-64 Linux # Elf64_Half=U16 Elf64_Word=U32 Elf64_Addr/Off/Xword=U64 Elf64Ehdr e-ident Array 16 U8 e-type U16 e-machine U16 e-version U32 e-entry U64 e-phoff U64 e-shoff U64 e-flags U32 e-ehsize U16 e-phentsize U16 e-phnum U16 e-shentsize U16 e-shnum U16 e-shstrndx U16 Elf64Phdr p-type U32 p-flags U32 p-offset U64 p-vaddr U64 p-paddr U64 p-filesz U64 p-memsz U64 p-align U64 # e_ident bytes (indices 0-7) fn elf-mag0() -> U8: 127 # 0x7f fn elf-mag1() -> U8: 69 # 'E' fn elf-mag2() -> U8: 76 # 'L' fn elf-mag3() -> U8: 70 # 'F' fn elf-class64() -> U8: 2 fn elfdata2lsb() -> U8: 1 fn ev-current() -> U8: 1 fn elfosabi-none() -> U8: 0 # e_type fn et-exec() -> U16: 2 # e_machine fn em-x86-64() -> U16: 62 # e_version fn ev-current-u32() -> U32: 1 # e_shstrndx special value fn shn-undef() -> U16: 0 # p_type fn pt-load() -> U32: 1 # p_flags fn pf-x() -> U32: 1 fn pf-w() -> U32: 2 fn pf-r() -> U32: 4 fn elf-header() ehdr-size = (Mem.get-typesize() as Mem.TypeSize Elf64Ehdr).size phdr-size = (Mem.get-typesize() as Mem.TypeSize Elf64Phdr).size return Elf64Ehdr { e-ident: Mem.cast-on-zeroed([elf-mag0(), elf-mag1(), elf-mag2(), elf-mag3(), elf-class64(), elfdata2lsb(), ev-current(), elfosabi-none()]), e-type: et-exec(), e-machine: em-x86-64(), e-version: ev-current-u32(), e-entry: Convert.from-hex('400000') + ehdr-size Mem.size-u64() + phdr-size Mem.size-u64(), e-phoff: ehdr-size Mem.size-u64(), e-shoff: 0, e-flags: 0, e-ehsize: ehdr-size Mem.size-u16(), e-phentsize: phdr-size Mem.size-u16(), e-phnum: 1, e-shentsize: 0, e-shnum: 0, e-shstrndx: shn-undef() } fn program-header(): Elf64Phdr { p-type: pt-load(), p-flags: Bits.u32-or(pf-r(), pf-x()), p-offset: 0, p-vaddr: Convert.from-hex('400000'), p-paddr: Convert.from-hex('400000'), p-filesz: 0, p-memsz: 0, p-align: Convert.from-hex('200000') } # Cool API use ByteBuf ByteBuf Elf al bytes ByteBuf al finished Bool fn filesz-off() header = undefined() as Elf64Phdr return Mem.ptr-to-int(&header.p-filesz) - Mem.ptr-to-int(&header) fn memsz-off() header = undefined() as Elf64Phdr return Mem.ptr-to-int(&header.p-memsz) - Mem.ptr-to-int(&header) # temp here fn as-mem(x Ptr a) -> Slice U8 return Slice { ptr: x Mem.cast-ptr(), count: x Mem.ptr-size-of() } fn mk(al Allocator) -> Elf Allocator bytes =& ByteBuf.mk(al) bytes ByteBuf.write-bytes(as-mem(&elf-header())) bytes ByteBuf.write-bytes(as-mem(&program-header())) return { bytes: bytes&, finished: False } fn finalize(elf Ptr (Elf Allocator)) bytes = &elf&.bytes initial-off = (Mem.get-typesize() as Mem.TypeSize Elf64Ehdr).size filesz = bytes&.buf.count Mem.size-u64() bytes ByteBuf.patch(initial-off + filesz-off(), filesz as U64) bytes ByteBuf.patch(initial-off + memsz-off(), filesz as U64) fn write-to-file (elf Ptr (Elf Allocator), filename) elf finalize() filename File.write-bytes(elf&.bytes) Cnile.chmod(filename, Convert.from-octal([7, 5, 5]) Mem.i32-u32()) fn bytebuf (elf Ptr (Elf Allocator)) -> Ptr (ByteBuf Allocator): &elf&.bytes # TEMPORARY, for demonstration purposes. fn write-hello-world(elf Ptr (Elf Allocator)) ByteBuf.write-bytes(&elf&.bytes, ['48', 'c7', 'c0', '01', '00', '00', '00'] map(Convert.from-hex)) # mov rax, 1 (SYS_write) ByteBuf.write-bytes(&elf&.bytes, ['48', 'c7', 'c7', '01', '00', '00', '00'] map(Convert.from-hex)) # mov rdi, 1 (stdout) ByteBuf.write-bytes(&elf&.bytes, ['48', '8d', '35', '15', '00', '00', '00'] map(Convert.from-hex)) # lea rsi, [rip+21] ByteBuf.write-bytes(&elf&.bytes, ['48', 'c7', 'c2', '0c', '00', '00', '00'] map(Convert.from-hex)) # mov rdx, 12 ByteBuf.write-bytes(&elf&.bytes, ['0f', '05'] map(Convert.from-hex)) # syscall ByteBuf.write-bytes(&elf&.bytes, ['48', 'c7', 'c0', '3c', '00', '00', '00'] map(Convert.from-hex)) # mov rax, 60 (SYS_exit) ByteBuf.write-bytes(&elf&.bytes, ['48', '31', 'ff'] map(Convert.from-hex)) # xor rdi, rdi ByteBuf.write-bytes(&elf&.bytes, ['0f', '05'] map(Convert.from-hex)) # syscall ByteBuf.write-bytes(&elf&.bytes, ['48', '65', '6c', '6c', '6f', '20', '57', '6f', '72', '6c', '64', '0a'] map(Convert.from-hex)) # "Hello World\n"