code wiki / (root) / nx_pe_compile_win_call.nx

nx_pe_compile_win_call.nx

buildroot/runtime/nx_pe_compile_win_call.nx

11807 B214 linesdepth 4pulls 6 transitivereach 0 importersview sourcekind tooltopic pe
docsdependenciesstructsconstsfunctions

about

nx_pe_compile_win_call.nx -- W3b-2: nxc2-COMPILED code issues a kernel32 call ITSELF. W3b-1 (nx_pe_compile_win_io) proved a compiled-organ PE does native kernel32 I/O, but the call was in the hand-laid ENTRY STUB. This rung closes the gap W3b-1 deferred: the nxc2-COMPILED `main` calls a NishiLang shim `nx_win_exit(code)` and THAT call reaches kernel32 ExitProcess via a keystone-synthesized SysV->MS-x64 thunk -- with NO compiler or assembler edits. THE LINKER TRICK (why this is clean, not byte-surgery): the shared assembler axc_label_resolve LOUD-FAILS (sys_exit 102) on any undefined symbol -- so a kernel32 import cannot be left as an unresolved extern. Instead `nx_win_exit` is a REAL bodied NishiLang function: the compiler emits `call nx_win_exit` (verified: direct call, not DCE'd, not tail-called) and the assembler resolves the label normally. The keystone then acts as the LINKER: 1. resolve nx_win_exit's offset in the assembled .text (alongside main's), 2. APPEND a 13-byte MS-x64 thunk after the compiled code, 3. OVERWRITE the shim's first 5 bytes with `jmp rel32` -> the thunk. No size-matching (the rest of the shim body just becomes unreached), no NOP-pad needed, no undefined symbols. The thunk (at a keystone-known RVA) can form the RIP-relative IAT call disp, which the compiler could not (it does not know the PE's IAT layout -- binding is the linker's job). shim entry (overwritten): jmp <thunk> ; E9 rel32 thunk (appended to .text): mov ecx, edi ; SysV arg0 (rdi) -> MS-x64 arg0 (ecx) sub rsp, 0x28 ; shadow + align call [rip+ExitProcess] ; ExitProcess(code) -- never returns int3 NO-FALSE-GREEN: source is `{ let r = nx_win_exit(7); return r + 92 }`. r+92 = 99. If the redirect did NOT happen, nx_win_exit would return 7, r=7, main returns 99, entry stub exits 99. Observing exit 7 (NOT 99) proves the compiled call reached ExitProcess(7) and terminated the process BEFORE `return 99`. Arg-vary (nx_win_exit(13) -> exit 13) proves the arg flows from compiled code through the SysV->MS-x64 thunk. Tamper (corrupt ExitProcess import name) -> 0xC0000139 = REAL OS binding. PIPELINE (build WSL sovereign, run native): src.nx -> ./_offc/nx_compile_x86_native.elf <src> > /tmp/nxwin.s -> ./_offc/nx_sov_build_run.elf nx_pe_compile_win_call (reads /tmp/nxwin.s) -> _offc/nx_win_compiled_call.exe -> run native on Windows 11. HONEST SCOPE: one kernel32 import (ExitProcess), one no-result thunk, fixed shim name. Generalizes to WriteFile/read/etc. by widening the thunk's ABI marshaling (rdx/r8/r9 + stack) and a shim->import table -> then an I/O organ's own sys_write maps here. Replicates the keystone assemble (reuses axc_pass; no shared-assembler edit). lineage_id: substrate_pe_compile_win_call_v1

dependencies 3 imports · 0 importers

nx_syscalls.nx nxasm_x86.nx nx_pe_writer.nx nx_pe_compile_win_call.nx

imports: nx_syscalls.nxnxasm_x86.nxnx_pe_writer.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main sys_mmap sys_read_file sys_openat_rd sys_lseek sys_mmap ↻ sys_read sys_close nxc_assemble sys_mmap ↻ axc_pass axc_is_space axc_is_ident axc_apply_section axc_tok_is axc_cstr_len axc_is_space ↻ axc_is_ident ↻ axc_tok_is ↻ axc_emit_bytes axc_is_digit axc_parse_int axc_is_digit ↻ axc_parse_operand axc_is_space ↻ axc_is_ident ↻ axc_reg_num axc_tok_is ↻ axc_parse_int ↻ axc_is_digit ↻ axc_tok_is ↻ axc_emit axc_tok_is ↻ axc_alu x86_alu_imm x86_rex_w x86_modrm x86_put_u32le x86_alu_rr x86_rex_w ↻

structs

none

consts

46const NXC_CODE_CAP: i64 = 1048576
47const NXC_FILE_SIZE: i64 = 0x600 // headers + .text + .idata (keystone single-import layout)
48const NXC_STUB_LEN: i64 = 18 // keystone entry stub: sub rsp,0x28;call main;mov ecx,eax;call ExitProcess;int3
49const NXC_THUNK_LEN: i64 = 13 // mov ecx,edi;sub rsp,0x28;call [rip+ExitProcess];int3
50const NXC_TEXT_CAP: i64 = 0x200 // one .text file chunk (stub + compiled code + thunk)

functions

54func nxc_assemble(src: *u8, n: i64, out: *u8, out_cap: i64, p_main: *i64, p_winexit: *i64) -> i64
94func nxc_emit_pe(buf: *u8, code: *u8, code_len: i64, main_off: i64, winexit_off: i64) -> i64
185func main() -> i64