code wiki / (root) / nx_pe_compile_win_dll.nx

nx_pe_compile_win_dll.nx source

↩ module page · 169 lines · 10456 B

1// nx_pe_compile_win_dll.nx -- compiler->DLL: wrap REAL nx_cc-compiled NishiLang in a PE DLL with named exports, incl. an 2// MS-x64->SysV ABI thunk (up to 4 int args) so exported functions that take arguments work. Assembles the .s nx_cc emitted 3// (reuse nxasm_x86 axc_pass, NO shared edit), resolves export offsets, appends the thunk, emits a 2-export DLL. Exports: 4// nishi_answer (direct no-arg) + nishi_render(buf,w,h) (via thunk) -- a real rasterizer a foreign Windows process calls to 5// get a rendered frame in a caller-provided buffer = "a game calls our sovereign d3d-lib and gets pixels". Code is 6// position-independent (immediate/rel32) -> no .reloc; based 0x180000000. cl/WARP = oracles only; DLL = 100% NishiLang. 7// Input: /tmp/nishi_export_lib.s Output: knowledge/nishi_compiled.dll license_tier: ORIGINAL expect_exit: 0 8import "nx_syscalls.nx" 9import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 10import "nxasm_x86.nx" 11import "nx_pe_writer.nx" 12 13func pcwd_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 14// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 15// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 16// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 17// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 18func pcwd_putn(v: i64) -> i64 { nxi_out(v); return 0 } 19func pcwd_wstr(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ buf[off+i]=s[i]; i=i+1 } buf[off+i]=0 as u8; return i+1 } 20 21func pcwd_assemble_exports(src: *u8, n: i64, out: *u8, out_cap: i64, p0: *i64, l0: *u8, p1: *i64, l1: *u8) -> i64 { 22 let lab_off: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 23 let lab_len: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 24 let lab_addr: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 25 let lab_sec: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 26 let op0: *i64 = sys_mmap(72) as *i64 // 9 slots: +OP_INDEX(7)/OP_SCALE(8) for SIB (matches nxasm_x86) 27 let op1: *i64 = sys_mmap(72) as *i64 28 let op2: *i64 = sys_mmap(72) as *i64 // API DRIFT FIX: axc_pass gained op2 between op1 and scratch 29 let scratch: *u8 = sys_mmap(64) 30 let posbox: *i64 = sys_mmap(16) as *i64 31 let n_lab_box: *i64 = sys_mmap(16) as *i64 32 n_lab_box[0] = 0 33 let lh: *i64 = sys_mmap(ASM_LH_SIZE * 8) as *i64 34 35 let text_size: i64 = axc_pass(src, n, out, 0, 0, lab_off, lab_len, lab_addr, lab_sec, n_lab_box, lh, op0, op1, op2, scratch, posbox) 36 if text_size < 0 { return text_size } 37 let n_lab: i64 = n_lab_box[0] 38 39 var a0: i64 = 0 - 1 40 var a1: i64 = 0 - 1 41 var k: i64 = 0 42 while k < n_lab { 43 if lab_sec[k] == 1 { lab_addr[k] = lab_addr[k] + text_size } 44 if axc_tok_is(src, lab_off[k], lab_len[k], l0) == 1 { a0 = lab_addr[k] } 45 if axc_tok_is(src, lab_off[k], lab_len[k], l1) == 1 { a1 = lab_addr[k] } 46 k = k + 1 47 } 48 axc_lh_build(src, lab_off, lab_len, n_lab, lh) 49 50 let total: i64 = axc_pass(src, n, out, text_size, 1, lab_off, lab_len, lab_addr, lab_sec, n_lab_box, lh, op0, op1, op2, scratch, posbox) 51 if total < 0 { return total } 52 if total > out_cap { return 0 - 200 } 53 p0[0] = a0 54 p1[0] = a1 55 return total 56} 57 58// wrap `code` in a PE DLL: nishi_answer direct @ answer_off, nishi_render via ABI thunk -> render_off. DYNAMIC layout: 59// .text spans ceil(text_used/page) virtual pages, .edata placed CONTIGUOUSLY right after (a virtual GAP = loader err 193). 60// Returns the total file size, or -err. 61func pcwd_emit_dll(buf: *u8, code: *u8, code_len: i64, answer_off: i64, render_off: i64) -> i64 { 62 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT } 63 let thunk_off: i64 = code_len 64 let text_used: i64 = code_len + 30 // 30-byte 4-arg MS-x64->SysV thunk (saves rdi/rsi) 65 let text_pages: i64 = (text_used + 4095) / 4096 66 let text_raw: i64 = text_pages * 4096 67 let edata_rva: i64 = 0x1000 + text_raw // contiguous after .text's virtual span (NO gap) 68 let edata_file: i64 = 0x200 + text_raw 69 let size_of_image: i64 = edata_rva + 0x1000 70 let file_size: i64 = edata_file + 0x200 71 72 _w16(buf, 0, 0x5A4D); _w32(buf, 0x3C, 0x80); _w32(buf, 0x80, 0x00004550) 73 _w16(buf, 0x84 + 0, PE_MACHINE_AMD64); _w16(buf, 0x84 + 2, 2); _w16(buf, 0x84 + 16, 0xF0); _w16(buf, 0x84 + 18, 0x2022) 74 let O: i64 = 0x98 75 _w16(buf, O + 0, PE_OH_MAGIC_PEPLUS); _w8(buf, O + 2, 1) 76 _w32(buf, O + 4, text_raw); _w32(buf, O + 8, 0x200) 77 _w32(buf, O + 16, 0); _w32(buf, O + 20, 0x1000) 78 _w64(buf, O + 24, 0x80000000, 0x1) // ImageBase 0x180000000 79 _w32(buf, O + 32, 0x1000); _w32(buf, O + 36, 0x200) 80 _w16(buf, O + 40, 6); _w16(buf, O + 48, 6) 81 _w32(buf, O + 56, size_of_image); _w32(buf, O + 60, 0x200) 82 _w16(buf, O + 68, PE_SUBSYSTEM_CONSOLE) 83 _w64(buf, O + 72, 0x100000, 0); _w64(buf, O + 80, 0x1000, 0); _w64(buf, O + 88, 0x100000, 0); _w64(buf, O + 96, 0x1000, 0) 84 _w32(buf, O + 108, 16) 85 _w32(buf, O + 112 + 0, edata_rva); _w32(buf, O + 112 + 4, 0x62) // DataDir[0] Export 86 87 // Sections (virtually contiguous) 88 _emit_section_header(buf, FOFF_SECT_TBL, 46, 116, 101, 120, 116, 0, 0, 0, text_used, 0x1000, text_raw, 0x200, PE_SECT_CODE_X_R) 89 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 101, 100, 97, 116, 97, 0, 0, 0x62, edata_rva, 0x200, edata_file, PE_SECT_DATA_R) 90 91 // .text: real nx_cc-compiled code 92 var i: i64 = 0 93 while i < code_len { buf[0x200 + i] = code[i]; i = i + 1 } 94 95 // 4-arg MS-x64 -> SysV thunk (preserves rdi/rsi; real call + realign). RCX->RDI RDX->RSI R8->RDX R9->RCX 96 // push rdi;push rsi; mov rdi,rcx; mov rsi,rdx; mov rdx,r8; mov rcx,r9; sub rsp,8; call fn; add rsp,8; pop rsi;pop rdi;ret 97 let t: i64 = 0x200 + thunk_off 98 _w8(buf,t+0,0x57); _w8(buf,t+1,0x56) 99 _w8(buf,t+2,0x48); _w8(buf,t+3,0x89); _w8(buf,t+4,0xCF) // mov rdi,rcx 100 _w8(buf,t+5,0x48); _w8(buf,t+6,0x89); _w8(buf,t+7,0xD6) // mov rsi,rdx 101 _w8(buf,t+8,0x4C); _w8(buf,t+9,0x89); _w8(buf,t+10,0xC2) // mov rdx,r8 102 _w8(buf,t+11,0x4C); _w8(buf,t+12,0x89); _w8(buf,t+13,0xC9) // mov rcx,r9 103 _w8(buf,t+14,0x48); _w8(buf,t+15,0x83); _w8(buf,t+16,0xEC); _w8(buf,t+17,0x08) // sub rsp,8 104 _w8(buf,t+18,0xE8) // call rel32 105 let jrel: i64 = render_off - (thunk_off + 23) 106 _w32(buf, t+19, jrel) 107 _w8(buf,t+23,0x48); _w8(buf,t+24,0x83); _w8(buf,t+25,0xC4); _w8(buf,t+26,0x08) // add rsp,8 108 _w8(buf,t+27,0x5E); _w8(buf,t+28,0x5F); _w8(buf,t+29,0xC3) // pop rsi;pop rdi;ret 109 110 // .edata @ file edata_file (RVA edata_rva): 2 exports, names SORTED ("nishi_answer" < "nishi_render") 111 let E: i64 = edata_file 112 _w32(buf, E + 12, edata_rva + 0x56) // Name RVA -> "nishicc.dll" 113 _w32(buf, E + 16, 1) // Base 114 _w32(buf, E + 20, 2) // NumberOfFunctions 115 _w32(buf, E + 24, 2) // NumberOfNames 116 _w32(buf, E + 28, edata_rva + 0x28) // AddressOfFunctions 117 _w32(buf, E + 32, edata_rva + 0x30) // AddressOfNames 118 _w32(buf, E + 36, edata_rva + 0x38) // AddressOfNameOrdinals 119 _w32(buf, E + 0x28, 0x1000 + answer_off) // func[0] = nishi_answer (direct) 120 _w32(buf, E + 0x2C, 0x1000 + thunk_off) // func[1] = nishi_render thunk 121 _w32(buf, E + 0x30, edata_rva + 0x3C) // name[0] = "nishi_answer" 122 _w32(buf, E + 0x34, edata_rva + 0x49) // name[1] = "nishi_render" 123 _w16(buf, E + 0x38, 0) // "nishi_answer" -> func[0] 124 _w16(buf, E + 0x3A, 1) // "nishi_render" -> func[1] 125 pcwd_wstr(buf, E + 0x3C, "nishi_answer\x00" as *u8) 126 pcwd_wstr(buf, E + 0x49, "nishi_render\x00" as *u8) 127 pcwd_wstr(buf, E + 0x56, "nishicc.dll\x00" as *u8) 128 return file_size 129} 130 131func main() -> i64 { 132 pcwd_puts("=== nx_pe_compile_win_dll -- nx_cc code -> PE DLL: nishi_answer (direct) + nishi_render (thunk, real raster) ===\n" as *u8) 133 let lenbox: *i64 = sys_mmap(16) as *i64 134 let src: *u8 = sys_read_file("/tmp/nishi_export_lib.s" as *u8, lenbox) 135 if (src as i64) == 0 { pcwd_puts("FAIL no /tmp/nishi_export_lib.s (build nishi_export_lib --build-only first)\n" as *u8); sys_exit(1); return 1 } 136 let n: i64 = lenbox[0] 137 if n <= 0 { pcwd_puts("FAIL empty .s\n" as *u8); sys_exit(2); return 2 } 138 139 let code: *u8 = sys_mmap(1048576) 140 let ab: *i64 = sys_mmap(16) as *i64 141 let rb: *i64 = sys_mmap(16) as *i64 142 ab[0] = 0 - 1; rb[0] = 0 - 1 143 let code_len: i64 = pcwd_assemble_exports(src, n, code, 1048576, ab, "nishi_answer" as *u8, rb, "nishi_render" as *u8) 144 if code_len < 0 { pcwd_puts("FAIL assemble rc="); pcwd_putn(code_len); pcwd_puts("\n" as *u8); sys_exit(3); return 3 } 145 let answer_off: i64 = ab[0] 146 let render_off: i64 = rb[0] 147 if answer_off < 0 { pcwd_puts("FAIL nishi_answer not found\n" as *u8); sys_exit(4); return 4 } 148 if render_off < 0 { pcwd_puts("FAIL nishi_render not found\n" as *u8); sys_exit(4); return 4 } 149 pcwd_puts(" code_len="); pcwd_putn(code_len); pcwd_puts(" nishi_answer@"); pcwd_putn(answer_off); pcwd_puts(" nishi_render@"); pcwd_putn(render_off); pcwd_puts(" (thunk@"); pcwd_putn(code_len); pcwd_puts(")\n" as *u8) 150 151 let buf: *u8 = sys_mmap(0x10000) 152 let fsz: i64 = pcwd_emit_dll(buf, code, code_len, answer_off, render_off) 153 if fsz < 0 { pcwd_puts("FAIL emit rc="); pcwd_putn(fsz); pcwd_puts("\n" as *u8); sys_exit(5); return 5 } 154 155 let fd: i64 = sys_openat_wr("knowledge/nishi_compiled.dll\x00" as *u8, 0x1a4) 156 if fd < 0 { pcwd_puts("FAIL open output\n" as *u8); sys_exit(6); return 6 } 157 let wrote: i64 = sys_write(fd, buf, fsz) 158 sys_close(fd) 159 160 pcwd_puts(" wrote "); pcwd_putn(wrote); pcwd_puts(" bytes -> knowledge/nishi_compiled.dll file_size="); pcwd_putn(fsz); pcwd_puts("\n" as *u8) 161 var fails: i64 = 0 162 if (buf[0] as i64) != 0x4D { fails=fails+1 } 163 if (buf[0x84+18] as i64) != 0x22 { fails=fails+1 } 164 if wrote != fsz { fails=fails+1 } 165 if fails == 0 { pcwd_puts("NX-PE-COMPILE-DLL GREEN -- nx_cc render fn exported via ABI thunk; harness must render+verify a frame\n" as *u8); sys_exit(0); return 0 } 166 pcwd_puts("NX-PE-COMPILE-DLL RED fails="); pcwd_putn(fails); pcwd_puts("\n" as *u8) 167 sys_exit(1) 168 return 1 169}