# Freeze into ELF binary which requires libc (for dlsym and dlopen). # FIXME: also have r13 backup, because coroutines < sys .asm "::" via sys .asm .ops ":" via sys .asm .ops .|label "@" deff sys .asm .|peek =*:peek sys .asm .|poke =*:poke { :labelRecord [ } "[[" deff { ] :labelResolve } "]]" deff # hex decoding { # ==strNumber 0 -01 { 48 sub [ 0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 10 11 12 13 14 15 ] * -01 16 mul -01 add } each } "%" defq { 8 { _ 256 mod -01 256 div } rep -- } /uint64 deffd { _ 0 lt { 4294967296 add } rep 4294967295 band 4 { _ 256 mod -01 256 div } rep -- } /uint32 deffd { _ 0 lt { 65536 add } rep 65535 band 2 { _ 256 mod -01 256 div } rep -- } /uint16 deffd { _ 0 lt { 256 add } rep 255 band } /uint8 deffd [ /rdi /rsi /rdx /rcx /r8 /r9 ] _ =*:availableIntegerRegisters len ==:INTREGISTERS [ /xmm0 /xmm1 /xmm2 /xmm3 /xmm4 /xmm5 /xmm6 /xmm7 ] _ =*:availableFloatRegisters len ==:FLOATREGISTERS < { { 8 uw } /u64 deffd { 4 uw } /u32 deffd { 2 uw } /u16 deffd { 1 uw } /u8 deffd }" /defBitVariants deffd < # 0 -> number of bytes to combine into unsigned int # 1 -> base address # 0 <- first w bytes of string interpreted as unsigned int { ==w ==a [ a _ w add range |peek each ] 256 math .unbase } /uw deffst defBitVariants > /consume defvd < # 0 -> number of bytes to produce # 1 -> address to write at # 2 -> int to convert to bytes { ==w ==a ==i a _ w add range { 0 -01 poke }" each i 256 math .base _ dom { -101*0 a add poke }" each -- } /uw deffst defBitVariants > /produce defvd > _ .consume "=>" via _ .produce "<=" via /mem defvd { ==a 4096 str .alloc ==buf 0 ==i { i a add peek _ i buf len lt and } { i buf =[] i 1 add =i } loop -- i buf str .prefix } /peekString deffd { ::rawAddress 24 add } /rawContentAddress deffd 8 str .alloc ==:r15backup 8 str .alloc ==:rspbackup 8 str .alloc _ str .zero ==:dlopenAddress 8 str .alloc _ str .zero ==:dlsymAddress [ { sys .?freebsd } { "Elymas memory image" ==:prognameContent [ prognameContent rawContentAddress uint64 ] str .fromArray ==:prognamePointer 8 str .alloc _ str .zero ==:environContent [ environContent rawContentAddress uint64 ] str .fromArray ==:environPointer }' ] conds [ ] ==saveFromGC { ==f ==sym [ rspbackup rawContentAddress /rax :movqImmReg /rsp /rax :movqRegMem /r15 /rax :movqRegReg r15backup rawContentAddress /r15 :movqImmReg /r15 /r15 :movqMemReg 8 /r15 :subqImm8Reg /r15 :popqMem 8 /r15 :subqImm8Reg /rax /r15 :movqRegMem /r14 :pushqReg /r13 :pushqReg /r12 :pushqReg /rbx :pushqReg /rbp :pushqReg [ /rdi /rsi /rdx /rcx /r8 /r9 ] { :pushqReg } each [ /rdi /rsi /rdx /rcx /r8 /r9 ] { -- ::internalAllocateInteger /rax :movqImmReg /rax :callqReg /rax :pushqReg } each [ /rdi /rsi /rdx /rcx /r8 /r9 ] { -- /rdi :popqReg # integer on stack 5 8 mul /rsp /rax :movqMemDisp8Reg /rax 8 /rdi :movqRegMemDisp8 # load value /rdi 5 8 mul /rsp :movqRegMemDisp8 # replace register value by integer object } each f ::rawAddress /rax :movqImmReg /rax :pushqReg "*" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg /rdx :popqReg 63 /rdx :btrqImm8Reg [ 8 /rdx /rdx :movqMemDisp8Reg ] len :jcRel8 8 /rdx /rdx :movqMemDisp8Reg /rbp :popqReg /rbx :popqReg /r12 :popqReg /r13 :popqReg /r14 :popqReg /r15 /rax :movqMemReg 8 /r15 :addqImm8Reg /r15 :pushqMem 8 /r15 :addqImm8Reg /rax /r15 :movqRegReg /rdx /rax :movqRegReg :retn ] [ f ] ::createFunction _ ::rawCodeAddress < ==value 0 ==info > sym saveSymbol [ -01 ] saveFromGC -01 cat =saveFromGC } /defineFunction deffst < { ::internalAllocateFloat /rax :movqImmReg /rax :callqReg /xmm0 8 /rax :movqXmmMemDisp8 /rax :pushqReg } /wrapFloat deffd { /rax /rdx :movqRegReg 32 /rdx :shrqImm8Reg [ 63 /rax :btsqImm8Reg 0 :jmpRel8 ] len :jnzRel8 63 /rax :btsqImm8Reg [ /rax :pushqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 ] len :jmpRel8 /rax :pushqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 /rax :pushqReg } /wrapInteger deffd { ==type [ { type "" eq }' { } { type "s" eq }' { /rax :pushqReg /rcx /rcx :xorqRegReg /rax /rdi :movqRegReg /rax /rax :xorqRegReg /rcx :decqReg :repnz :scasb /rcx :negqReg 2 /rcx :subqImm8Reg /rcx :pushqReg /rcx /rdi :movqRegReg ::internalAllocateString /rax :movqImmReg /rax :callqReg /rcx :popqReg /rsi :popqReg /rax :pushqReg 24 /rax /rdi :leaqMemDisp8Reg :repnz :movsb } { type "i64" eq }' { wrapInteger } { type "i32" eq }' { /eax /rax :movsxlqRegReg # sign extend result wrapInteger } { type "i16" eq }' { /ax /rax :movsxwqRegReg # sign extend result wrapInteger } { type "i8" eq }' { /al /rax :movsxbqRegReg # sign extend result wrapInteger } { type "u64" eq }' { wrapInteger } { type "u32" eq }' { /eax /eax :movlRegReg # zero extend result 63 /rax :btsqImm8Reg /rax :pushqReg } { type "u16" eq }' { /ax /rax :movzxwqRegReg # zero extend result 63 /rax :btsqImm8Reg /rax :pushqReg } { type "u8" eq }' { /al /rax :movzxbqRegReg # zero extend result 63 /rax :btsqImm8Reg /rax :pushqReg } { type "p" eq }' { wrapInteger } { type "d" eq }' { wrapFloat } { type "f" eq }' { /xmm0 /xmm0 :cvtss2sdXmmXmm wrapFloat } { 1 }' { type dump "unknown return type specification" die } ] conds } =*returnWrapper > /helpers defvd # wrap a C function # 0 -> return value specification string # v: no return value # p: pointer return value # u8: uint8_t return value # u16: uint16_t return value # u32: uint32_t return value # u64: uint64_t return value # i8: int8_t return value # i16: int16_t return value # i32: int32_t return value # i64: int64_t return value # s: string return value (will be copied) # f: float return value # d: double return value # 1 -> argument specification string # "iis" == foo(int, int, char *) # p: pointer argument # c: callback argument (create suitable callbacks using wrapCallback) # i: integer argument (of any width) # f: float argument # d: double argument # s: string argument (pointer to string content passed, string is zero-terminated) # b: buffer argument (pointer to string content passed, no zero-termination, but no copy) # 2 -> address of function # the resulting function will take as many arguments as specified and return a single value { ==rets ==args ==func 0 ==nextIntegerRegister 0 ==nextFloatRegister [ 0 args len range { _ ==arg args * ==t { # returns the register where the value is available /rax ==reg nextIntegerRegister INTREGISTERS lt { nextIntegerRegister availableIntegerRegisters =reg } rep args len 1 sub arg sub 8 mul /rsp reg :movqMemDisp8Reg reg } /useIntegerStorage deffst { # increments storage index, possibly do final stores nextIntegerRegister INTREGISTERS ge { /rax args len 1 sub arg sub 8 mul /rbp :movqRegMemDisp8 } rep nextIntegerRegister 1 add =nextIntegerRegister } /nextIntegerStorage deffst { 0 nextIntegerRegister 6 min range { availableIntegerRegisters :pushqReg } each /rbp :pushqReg } /pushIntegerStorage deffst { /rbp :popqReg 0 nextIntegerRegister 6 min range reverse { availableIntegerRegisters :popqReg } each } /popIntegerStorage deffst { # returns the register where the value is available /not_enough_registers ==reg nextFloatRegister FLOATREGISTERS lt { nextFloatRegister availableFloatRegisters =reg } rep reg } /useFloatStorage deffst { nextFloatRegister FLOATREGISTERS ge { "FIXME: insufficient float registers available - parameters must go on stack" die } rep nextFloatRegister 1 add =nextFloatRegister } /nextFloatStorage deffst [ { t 0 "i" * eq t 0 "p" * eq or }' { [ useIntegerStorage ==reg 63 reg :btrqImm8Reg [ 8 reg reg :movqMemDisp8Reg ] len :jcRel8 8 reg reg :movqMemDisp8Reg nextIntegerStorage ] } { t 0 "c" * eq }' { [ useIntegerStorage ==reg 8 /r15 :subqImm8Reg reg /r15 :movqRegMem # keep the GC away 24 reg reg :movqMemDisp8Reg 16 reg :addqImm8Reg nextIntegerStorage ] } { t 0 "d" * eq }' { [[ useFloatStorage ==reg args len 1 sub arg sub 8 mul /rsp /rax :movqMemDisp8Reg 63 /rax :btrqImm8Reg /unboxedIntegerLoad :jcLbl8 7 /rax /cl :movbMemDisp8Reg %F0 /cl :andbImmReg /integerLoad :jzLbl8 %20 /cl :cmpbImmReg /floatLoad :jzLbl8 :ud2 @floatLoad 8 /rax reg :movqMemDisp8Xmm /loadDone :jmpLbl8 @integerLoad 8 /rax /rax :movqMemDisp8Reg @unboxedIntegerLoad /rax reg :cvtsi2sdqRegXmm @loadDone nextFloatStorage ]] } { t 0 "f" * eq }' { [[ useFloatStorage ==reg args len 1 sub arg sub 8 mul /rsp /rax :movqMemDisp8Reg 63 /rax :btrqImm8Reg /unboxedIntegerLoad :jcLbl8 7 /rax /cl :movbMemDisp8Reg %F0 /cl :andbImmReg /integerLoad :jzLbl8 %20 /cl :cmpbImmReg /floatLoad :jzLbl8 :ud2 @floatLoad 8 /rax reg :movqMemDisp8Xmm /loadDone :jmpLbl8 @integerLoad 8 /rax /rax :movqMemDisp8Reg @unboxedIntegerLoad /rax reg :cvtsi2sdqRegXmm @loadDone reg reg :cvtsd2ssXmmXmm nextFloatStorage ]] } { t 0 "b" * eq }' { [ useIntegerStorage ==reg 24 reg :addqImm8Reg nextIntegerStorage ] } { t 0 "s" * eq }' { [ useIntegerStorage ==reg { 8 /r15 :subqImm8Reg reg /r15 :movqRegMem # keep the GC away 0 24 1 /rax reg :andbImmMemIndexScaleDisp8 24 reg :addqImm8Reg } =*finalZeroSpaceAvailable { pushIntegerStorage reg :pushqReg 1 /rax /rdi :leaqMemDisp8Reg ::internalAllocateString /rax :movqImmReg /rax :callqReg 8 /r15 :subqImm8Reg /rax /r15 :movqRegMem # keep the GC away 24 /rax /rdi :leaqMemDisp8Reg /rax :popqReg /rdi :pushqReg 24 /rax /rsi :leaqMemDisp8Reg 16 /rax /rcx :movqMemDisp8Reg :reprcx :movsb 0 /rdi :andbImmMem reg :popqReg popIntegerStorage } =*finalZeroSpaceNotAvailable 16 reg /rax :movqMemDisp8Reg 7 /al :testbImmReg [ finalZeroSpaceNotAvailable 0 :jmpRel8 ] len :jnzRel8 finalZeroSpaceNotAvailable [ finalZeroSpaceAvailable ] len :jmpRel8 finalZeroSpaceAvailable nextIntegerStorage ] } { 1 }' { t dump "unknown argument semantics argument" die } ] conds } each ] ==argumentLoaders [ /rbx :popqReg r15backup rawContentAddress /rax :movqImmReg /r15 /rax :movqRegMem args len INTREGISTERS ge { # FIXME: this logic is broken for non-integer args /rsp /rbp :movqRegReg INTREGISTERS 8 mul /rbp :addqImm8Reg %F0 /rbp :andqImm8Reg } rep argumentLoaders { _ len dearray } each args len INTREGISTERS min 8 mul /rsp :addqImm8Reg /rsp /rbp :movqRegReg %F0 /rsp :andqImm8Reg func /rax :movqImmReg /rax :callqReg /rbp /rsp :movqRegReg args len INTREGISTERS gt { args len INTREGISTERS sub 8 mul /rsp :addqImm8Reg } rep rets helpers .returnWrapper r15backup rawContentAddress /rax :movqImmReg /rax /r15 :movqMemReg /rbx :pushqReg :retn ] [ ] ::createFunction } /wrapFunction deffd { ==rets ==args ==func 0 ==nextIntegerRegister 0 ==nextFloatRegister [ /rbx :pushqReg /rbp :pushqReg /r12 :pushqReg /r13 :pushqReg /r14 :pushqReg /r15 :pushqReg r15backup rawContentAddress /rax :movqImmReg /rax /r15 :movqMemReg # FIXME: push arguments on stack { args "^(s|p|i64|i32|i16|i8|u64|u32|u16|u8|p|d|f)(.*)" regex } { ==type =args [ { type "f" eq } { /not_enough_registers ==reg nextFloatRegister FLOATREGISTERS lt { nextFloatRegister _ availableFloatRegisters =reg 1 add =nextFloatRegister } rep reg /xmm0 :movqXmmXmm type helpers .returnWrapper } { 1 } { /not_enough_registers ==reg nextIntegerRegister INTREGISTERS lt { nextIntegerRegister _ availableIntegerRegisters =reg 1 add =nextIntegerRegister } rep reg /rax :movqRegReg type helpers .returnWrapper } ] conds } loop args "" neq { "Invalid type spec for wrapCallback: " args cat die } rep func ::rawAddress /rax :movqImmReg /rax :pushqReg "*" | ::rawCodeAddress /rax :movqImmReg /rax :callqReg # FIXME: grab return values from stack r15backup rawContentAddress /rax :movqImmReg /r15 /rax :movqRegMem /r15 :popqReg /r14 :popqReg /r13 :popqReg /r12 :popqReg /rbp :popqReg /rbx :popqReg :retn ] [ func ::rawAddress ] ::createFunction } /wrapCallback deffd [ { sys .?freebsd } { [ ] ==allLibraries }' ] conds { "\0" cat } [ /rbx :popqReg r15backup rawContentAddress /rax :movqImmReg /r15 /rax :movqRegMem /rdi :popqReg 24 /rdi :addqImm8Reg # load filename 257 /rsi :movqImmReg # RTLD_LAZY | RTLD_GLOBAL /rsp /rbp :movqRegReg %F0 /rsp :andqImm8Reg dlopenAddress rawContentAddress /rax :movqImmReg /rax /rax :movqMemReg /rax :callqReg /rbp /rsp :movqRegReg /rax :pushqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 /rax :pushqReg /rbx :pushqReg :retn ] [ ] ::createFunction ; [ { sys .?freebsd } { { _ 0 neq { [ -101 ] allLibraries cat =allLibraries } rep } ; } ] conds /dlopen deffd { -01 "\0" cat -01 } [ /rbx :popqReg r15backup rawContentAddress /rax :movqImmReg /r15 /rax :movqRegMem /rdi :popqReg 63 /rdi :btrqImm8Reg [ 8 /rdi /rdi :movqMemDisp8Reg ] len :jcRel8 8 /rdi /rdi :movqMemDisp8Reg # load library handle /rsi :popqReg 24 /rsi :addqImm8Reg # load symbol name /rsp /rbp :movqRegReg %F0 /rsp :andqImm8Reg dlsymAddress rawContentAddress /rax :movqImmReg /rax /rax :movqMemReg /rax :callqReg /rbp /rsp :movqRegReg /rax :pushqReg ::internalAllocateInteger /rax :movqImmReg /rax :callqReg 8 /rax :popqMemDisp8 /rax :pushqReg /rbx :pushqReg :retn ] [ ] ::createFunction ; /dlsym deffd # [ # /rbx :popqReg # r15backup rawContentAddress /rax :movqImmReg # /r15 /rax :movqRegMem # /rdi :popqReg # 63 /rdi :btrqImm8Reg # # %100 /rsi :movqImmReg # RTLD_GLOBAL # dlopenAddress rawContentAddress /rax :movqImmReg # /rax /rax :movqMemReg # /rax :callqReg # # /rax :pushqReg # ::internalAllocateInteger /rax :movqImmReg # /rax :callqReg # 8 /rax :popqMemDisp8 # /rax :pushqReg # /rbx :pushqReg # :retn # ] [ ] ::createFunction /malloc deffd # 0 -> return specification string (see wrapFunction for details) # 1 -> argument specification string (see wrapFunction for details) # 2 -> name of function # the resulting function will take as many arguments as specified and return a single integer [ { sys .?linux } { { ==rets ==args ==name name 0 dlsym _ not { "Could not resolve function: " name cat die } rep args rets wrapFunction } }' { sys .?freebsd } { { ==rets ==args ==name 0 ==result allLibraries { result { -- } { name -01 dlsym =result } ? * } each result _ not { "Could not resolve function: " name cat die } rep args rets wrapFunction } }' ] conds /resolveFunction deffd { ==filename # ==f (left on the stack a while and executed from sys .asm .programStart) sys .asm .patchProgramStart ==frozenAllocationCount ==heapSize { ==align ==value align value align mod sub align mod } /alignUpto deff sys .file ==out filename out _ .creating _ .writeonly .open [ < ".null" ==?name 0 ==?nameOffset { =nameOffset } /setNameOffset deff 0 ==?dataOffset { =dataOffset } /setDataOffset deff 0 ==?type # reserved first section 0 ==?flags # none 0 ==?addr # not loaded 0 ==?link # no associated section 0 ==?entsize # no entries [ ] ==?data 0 ==?dataSize > < ".strtab" ==?name 0 ==?nameOffset { =nameOffset } /setNameOffset deff 0 ==?dataOffset { =dataOffset } /setDataOffset deff 3 ==?type # string table 0 ==?flags # none 0 ==?addr # not loaded 0 ==?link # no associated section 0 ==?entsize # no entries [ ] ==?data # to be filled later 0 ==?dataSize # to be filled later { _ =data len =dataSize } /setData deff > _ ==?stringTable < ".dynamic" ==?name 0 ==?nameOffset { =nameOffset } /setNameOffset deff 0 ==?dataOffset { =dataOffset } /setDataOffset deff 6 ==?type # dynamic linking table 0 ==?flags # none 0 ==?addr # not loaded 0 ==?link # no associated section # TODO: that's a lie, the string table is associated 0 ==?entsize # no entries # TODO: also a lie [ ] ==?data # to be filled later 0 ==?dataSize # to be filled later { _ =data len =dataSize } /setData deff > _ ==?dynamicTable < ".dynsym" ==?name 0 ==?nameOffset { =nameOffset } /setNameOffset deff 0 ==?dataOffset { =dataOffset } /setDataOffset deff 11 ==?type # dynamic linking table 0 ==?flags # none 0 ==?addr # not loaded 1 ==?link # string table is associated %18 ==?entsize [ ] ==?data # to be filled later 0 ==?dataSize # to be filled later { _ =data len =dataSize } /setData deff > _ ==?symbolTable < ".rela" ==?name 0 ==?nameOffset { =nameOffset } /setNameOffset deff 0 ==?dataOffset { =dataOffset } /setDataOffset deff 4 ==?type # relocation table 0 ==?flags # none 0 ==?addr # not loaded 3 ==?link # symbol table is associated (section index 3) %18 ==?entsize [ ] ==?data # to be filled later 0 ==?dataSize # to be filled later { _ =data len =dataSize } /setData deff > _ ==?relocationTable [ { sys .?freebsd } { < ".hash" ==?name 0 ==?nameOffset { =nameOffset } /setNameOffset deff 0 ==?dataOffset { =dataOffset } /setDataOffset deff 5 ==?type # symbol hash table 0 ==?flags # none 0 ==?addr # not loaded 3 ==?link # symbol table is associated (section index 3) 0 ==?entsize # no uniform fixed-size entries here [ ] ==?data # to be filled later 0 ==?dataSize # to be filled later { _ =data len =dataSize } /setData deff > _ ==?hashTable }' ] conds ] ==?metaSections [ 0 frozenAllocationCount range { ==i < ".-=#=-" ==?name 0 ==?nameOffset { =nameOffset } /setNameOffset deff 0 ==?dataOffset { =dataOffset } /setDataOffset deff 1 ==?type # program data 7 ==?flags # writable, allocated, executable i sys .asm .globalAllocBase ==?addr # address where this section will be loaded 0 ==?link # no associated section 0 ==?entsize # no entries i sys .asm .globalAllocBase ==?dataBase i sys .asm .globalAllocSize ==?dataSize > } each ] ==?allocSections 4096 ==:PAGESIZE allocSections len 4 add ==programHeaderCount metaSections len allocSections len add ==sectionHeaderCount [ { sys .?linux } { "/lib64/ld-linux-x86-64.so.2" ==:INTERPRETERNAME 0 ==interpreterStringOffset "libc.so.6" ==:LIBCNAME 0 ==libcStringOffset "libdl.so.2" ==:LIBDLNAME 0 ==libdlStringOffset "dlopen" ==:DLOPENNAME 0 ==dlopenStringOffset "dlsym" ==:DLSYMNAME 0 ==dlsymStringOffset }' { sys .?freebsd } { "/libexec/ld-elf.so.1" ==:INTERPRETERNAME 0 ==interpreterStringOffset "libc.so.7" ==:LIBCNAME 0 ==libcStringOffset "dlopen" ==:DLOPENNAME 0 ==dlopenStringOffset "dlsym" ==:DLSYMNAME 0 ==dlsymStringOffset "__progname" ==:PROGNAME 0 ==prognameStringOffset "environ" ==:ENVIRON 0 ==environStringOffset }' ] conds < 1 ==stringOffset { =*saveOffset ==string string { } each %00 stringOffset _ saveOffset string len add 1 add =stringOffset } /stringTableEntry deffst [ %00 # initial zero byte of string table ### section names [ metaSections allocSections ] { { ==s s .name s .|setNameOffset stringTableEntry } each } each INTERPRETERNAME { =interpreterStringOffset } stringTableEntry LIBCNAME { =libcStringOffset } stringTableEntry DLOPENNAME { =dlopenStringOffset } stringTableEntry DLSYMNAME { =dlsymStringOffset } stringTableEntry [ { sys .?linux } { LIBDLNAME { =libdlStringOffset } stringTableEntry }' { sys .?freebsd } { PROGNAME { =prognameStringOffset } stringTableEntry ENVIRON { =environStringOffset } stringTableEntry }' ] conds ] stringTable .setData > -- [ { sys .?freebsd } { [ 1 uint32 # number of "hash" buckets (ahem) 5 uint32 # number of symbols 1 uint32 # bucket starts at first real symbol 0 uint32 # reserved undefined symbol refers to itself 2 uint32 # first symbol refers to the second, 3 uint32 # ... which refers to the third, etc. 4 uint32 0 uint32 # ... which refers to the undefined symbol ] hashTable .setData }' ] conds < [ 0 uint64 0 uint64 0 uint64 # symbol table index 0 is reserved dlopenStringOffset uint32 %12 # globally visible function entry point %00 0 uint16 0 uint64 0 uint64 dlsymStringOffset uint32 %12 # globally visible function entry point %00 0 uint16 0 uint64 0 uint64 [ { sys .?freebsd } { prognameStringOffset uint32 %11 # globally visible object (char * presumably) %00 %F1 %FF # SHN_ABS prognamePointer rawContentAddress uint64 # value 8 uint64 # size environStringOffset uint32 %11 # globally visible object (char **) %00 %F1 %FF # SHN_ABS environPointer rawContentAddress uint64 # value 8 uint64 # size }' ] conds # FIXME: make a createSymbol function instead of having hash and symbol table data being dependent ] symbolTable .setData > -- < [ dlopenAddress rawContentAddress uint64 6 uint32 # R_X86_64_GLOB_DAT 1 uint32 0 uint64 dlsymAddress rawContentAddress uint64 6 uint32 # R_X86_64_GLOB_DAT 2 uint32 0 uint64 ] relocationTable .setData > -- { [ # TAG VALUE 1 uint64 libcStringOffset uint64 # require libc (DT_NEEDED) 5 uint64 %700000000000 stringTable .dataOffset add uint64 # string table address (DT_STRTAB) 6 uint64 %700000000000 symbolTable .dataOffset add uint64 # symbol table address (DT_SYMTAB) 7 uint64 %700000000000 relocationTable .dataOffset add uint64 # relocation table address (DT_RELA) 8 uint64 relocationTable .dataSize uint64 # relocation table size (DT_RELASZ) 9 uint64 24 uint64 # relocation entry size (DT_RELAENT) 10 uint64 stringTable .dataSize uint64 # string table size (DT_STRSZ) 11 uint64 24 uint64 # symbol entry size (DT_SYMENT) [ { sys .?linux } { 1 uint64 libdlStringOffset uint64 # require libdl (DT_NEEDED) }' { sys .?freebsd } { 4 uint64 %700000000000 hashTable .dataOffset add uint64 # hash table address (DT_HASH) }' ] conds # 0x0000000000000015 (DEBUG) 0x0 # 0x0000000000000003 (PLTGOT) 0x604c48 # 0x0000000000000002 (PLTRELSZ) 888 (bytes) # 0x0000000000000014 (PLTREL) RELA # 0x0000000000000017 (JMPREL) 0x400af8 24 uint64 0 uint64 # immediately apply all relocations (DT_BIND_NOW) 0 uint64 0 uint64 # end of table (DT_NULL) ] dynamicTable .setData } /fillDynamicTable deffst fillDynamicTable # some data not available yet, but size already needed < # %40 ==? section header size, %38 == program header size sectionHeaderCount %40 mul programHeaderCount %38 mul add %40 add ==dataOffset metaSections { ==s dataOffset s .setDataOffset dataOffset s .dataSize add =dataOffset } each dataOffset _ 4096 alignUpto add =dataOffset allocSections { ==s dataOffset s .setDataOffset dataOffset s .dataSize add _ PAGESIZE alignUpto add =dataOffset } each > -- fillDynamicTable [ ### elf header # unsigned char e_ident[16]; /* ELF identification */ %7F 0 1 2 "ELF" -30*20*10* # elf identifier %02 # elfclass64 %01 # elf version %01 # little endian encoding [ { sys .?linux } { %00 %00 } # Sys-V ABI { sys .?freebsd } { %09 %00 } # FreeBSD ABI ] conds %00 %00 %00 %00 %00 %00 %00 # padding # Elf64_Half e_type; /* Object file type */ %02 %00 # executable file # Elf64_Half e_machine; /* Machine type */ %3E %00 # whatever, /bin/ls has this # Elf64_Word e_version; /* Object file version */ %01 %00 %00 %00 # always 1 # Elf64_Addr e_entry; /* Entry point address */ sys .asm .|programStart sys .asm .rawCodeAddress uint64 # Elf64_Off e_phoff; /* Program header offset */ %40 uint64 # Elf64_Off e_shoff; /* Section header offset */ programHeaderCount %38 mul %40 add uint64 # Elf64_Word e_flags; /* Processor-specific flags */ %00 %00 %00 %00 # taken from from /bin/ls # Elf64_Half e_ehsize; /* ELF header size */ %40 %00 # Elf64_Half e_phentsize; /* Size of program header entry */ %38 %00 # Elf64_Half e_phnum; /* Number of program header entries */ programHeaderCount uint16 # Elf64_Half e_shentsize; /* Size of section header entry */ %40 %00 # Elf64_Half e_shnum; /* Number of section header entries */ sectionHeaderCount uint16 # Elf64_Half e_shstrndx; /* Section name string table index */ %01 %00 # section header name table index in section headers table ### program header describing program header table *sigh* 6 uint32 # program header table (PT_PHDR) 7 uint32 # read | write | execute # Elf64_Off p_offset; /* Offset in file */ %40 uint64 # Elf64_Addr p_vaddr; /* Virtual address in memory */ %700000000000 %40 add uint64 # Elf64_Addr p_paddr; /* Reserved */ %00 %00 %00 %00 %00 %00 %00 %00 # Elf64_Xword p_filesz; /* Size of segment in file */ programHeaderCount %38 mul uint64 # Elf64_Xword p_memsz; /* Size of segment in memory */ programHeaderCount %38 mul uint64 # Elf64_Xword p_align; /* Alignment of segment */ %08 %00 %00 %00 %00 %00 %00 %00 # alignment ### program header requesting interpreter 3 uint32 # interpreter requested (PT_INTERP) 7 uint32 # read | write | execute # Elf64_Off p_offset; /* Offset in file */ stringTable .dataOffset interpreterStringOffset add uint64 # Elf64_Addr p_vaddr; /* Virtual address in memory */ %700000000000 stringTable .dataOffset add interpreterStringOffset add uint64 # Elf64_Addr p_paddr; /* Reserved */ %00 %00 %00 %00 %00 %00 %00 %00 # Elf64_Xword p_filesz; /* Size of segment in file */ INTERPRETERNAME len 1 add uint64 # Elf64_Xword p_memsz; /* Size of segment in memory */ INTERPRETERNAME len 1 add uint64 # Elf64_Xword p_align; /* Alignment of segment */ %01 %00 %00 %00 %00 %00 %00 %00 # alignment ### program header loading all meta sections # Elf64_Word p_type; /* Type of segment */ 1 uint32 # loadable segment # Elf64_Word p_flags; /* Segment attributes */ 7 uint32 # read | write | execute # Elf64_Off p_offset; /* Offset in file */ 0 uint64 # Elf64_Addr p_vaddr; /* Virtual address in memory */ %700000000000 uint64 # Elf64_Addr p_paddr; /* Reserved */ %00 %00 %00 %00 %00 %00 %00 %00 # Elf64_Xword p_filesz; /* Size of segment in file */ [ metaSections { _ .dataOffset -01 .dataSize add } each ] |max fold uint64 # Elf64_Xword p_memsz; /* Size of segment in memory */ [ metaSections { _ .dataOffset -01 .dataSize add } each ] |max fold uint64 # Elf64_Xword p_align; /* Alignment of segment */ %00 %10 %00 %00 %00 %00 %00 %00 # alignment allocSections { ==s ### program header # Elf64_Word p_type; /* Type of segment */ %01 %00 %00 %00 # loadable segment # Elf64_Word p_flags; /* Segment attributes */ %07 %00 %00 %00 # read | write | execute # Elf64_Off p_offset; /* Offset in file */ s .dataOffset uint64 # Elf64_Addr p_vaddr; /* Virtual address in memory */ s .dataBase uint64 # Elf64_Addr p_paddr; /* Reserved */ %00 %00 %00 %00 %00 %00 %00 %00 # Elf64_Xword p_filesz; /* Size of segment in file */ s .dataSize uint64 # Elf64_Xword p_memsz; /* Size of segment in memory */ s .dataSize uint64 # Elf64_Xword p_align; /* Alignment of segment */ %00 %10 %00 %00 %00 %00 %00 %00 # alignment } each ### program header describing dynamic table 2 uint32 # dynamic table 7 uint32 # read | write | execute # Elf64_Off p_offset; /* Offset in file */ dynamicTable .dataOffset uint64 # Elf64_Addr p_vaddr; /* Virtual address in memory */ %700000000000 dynamicTable .dataOffset add uint64 # Elf64_Addr p_paddr; /* Reserved */ %00 %00 %00 %00 %00 %00 %00 %00 # Elf64_Xword p_filesz; /* Size of segment in file */ dynamicTable .dataSize uint64 # Elf64_Xword p_memsz; /* Size of segment in memory */ dynamicTable .dataSize uint64 # Elf64_Xword p_align; /* Alignment of segment */ %00 %10 %00 %00 %00 %00 %00 %00 # alignment [ metaSections allocSections ] { { ==s ### section header # Elf64_Word sh_name; /* Section name */ s .nameOffset uint32 # Elf64_Word sh_type; /* Section type */ s .type uint32 # Elf64_Xword sh_flags; /* Section attributes */ s .flags uint64 # Elf64_Addr sh_addr; /* Virtual address in memory */ s .addr uint64 # Elf64_Off sh_offset; /* Offset in file */ s .dataOffset uint64 # Elf64_Xword sh_size; /* Size of section */ s .dataSize uint64 # Elf64_Word sh_link; /* Link to other section */ s .link uint32 # Elf64_Word sh_info; /* Miscellaneous information */ 0 uint32 # Elf64_Xword sh_addralign; /* Address alignment boundary */ 1 uint64 # Elf64_Xword sh_entsize; /* Size of entries, if section has table */ s .entsize uint64 } each } each ] ==?fileHeaders 0 ==fileOffset [ fileHeaders metaSections { .data } each ] { ==data fileOffset data len add =fileOffset data str .fromArray out .writeall } each [ { sys .?linux } { 1 ==:WRITE }' { sys .?freebsd } { 4 ==:WRITE }' ] conds # FIXME: get the constant from somewhere else [ ] _ =fileHeaders =metaSections < > =stringTable allocSections { ==section section .dataOffset fileOffset sub str .alloc out .writeall section .dataOffset section .dataSize add =fileOffset 2000000000 ==:BLOCKSIZE 0 section .dataSize BLOCKSIZE div 1 add range { ==block section .dataBase block BLOCKSIZE mul add ==dataBase block section .dataSize BLOCKSIZE div eq section .dataSize block BLOCKSIZE mul sub BLOCKSIZE ? ==dataSize out .fd dataBase dataSize 0 0 0 WRITE sys .asm .syscall -- dataSize neq { "write failed" die } rep } each } each out .close ==f sys .asm .patchProgramStart -- heapSize neq { "freezing allocated new memory chunks, retrying..." dump f filename sys .so .freeze } rep } /freeze deffd > /so sys .defv sys .so .|freeze /freeze sys .deff # vim: syn=elymas