nx_pe_dll_com_device.nx source
↩ module page · 187 lines · 11611 B
1// nx_pe_dll_com_device.nx -- THE CONVERGENCE RUNG: emit a sovereign DLL exporting a D3D11CreateDevice-shaped factory
2// that returns a COM object whose vtable methods run REAL nx_cc-compiled rasterizer code. Fuses the two proven halves:
3// (COM object + vtable dispatch) x (compiled render fn) = a foreign Windows process does
4// dev = nishi_d3d_create_device(mem); dev->lpVtbl->Render(dev, buf, w, h) -> compiled rasterizer -> pixels.
5// Method BODIES (nx_dev_render/nx_dev_get_info) = sovereign nx_cc-compiled NishiLang. COM PLUMBING (per-method MS-x64->
6// SysV ABI thunks + a factory that builds the vtable in the CALLER-PROVIDED buffer via RIP-relative LEA) = generated by
7// THIS sovereign emitter (toolchain ABI glue, like a C++ compiler's vtables+thiscall adapters -- NO C++ ships).
8// Position-independent (no .reloc). Input /tmp/nishi_export_lib.s Output knowledge/nishi_com_device.dll
9// license_tier: ORIGINAL expect_exit: 0
10import "nx_syscalls.nx"
11import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
12import "nxasm_x86.nx"
13import "nx_pe_writer.nx"
14
15func cdv_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
16// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
17// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
18// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
19// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
20func cdv_putn(v: i64) -> i64 { nxi_out(v); return 0 }
21func cdv_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 }
22
23func cdv_assemble(src: *u8, n: i64, out: *u8, out_cap: i64, p0: *i64, l0: *u8, p1: *i64, l1: *u8) -> i64 {
24 let lab_off: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
25 let lab_len: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
26 let lab_addr: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
27 let lab_sec: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
28 let op0: *i64 = sys_mmap(72) as *i64 // 9 slots for SIB (matches nxasm_x86)
29 let op1: *i64 = sys_mmap(72) as *i64
30 let op2: *i64 = sys_mmap(72) as *i64 // API DRIFT FIX: axc_pass gained op2
31 let scratch: *u8 = sys_mmap(64)
32 let posbox: *i64 = sys_mmap(16) as *i64
33 let n_lab_box: *i64 = sys_mmap(16) as *i64
34 n_lab_box[0] = 0
35 let lh: *i64 = sys_mmap(ASM_LH_SIZE * 8) as *i64
36 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)
37 if text_size < 0 { return text_size }
38 let n_lab: i64 = n_lab_box[0]
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 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)
50 if total < 0 { return total }
51 if total > out_cap { return 0 - 200 }
52 p0[0] = a0
53 p1[0] = a1
54 return total
55}
56
57// emit the COM-device DLL. getinfo_off/render_off = code offsets of the compiled methods. Returns file size or -err.
58func cdv_emit(buf: *u8, code: *u8, code_len: i64, getinfo_off: i64, render_off: i64) -> i64 {
59 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT }
60 // glue layout in .text (relative offsets): [code][thunk_getinfo 21][thunk_render 30][factory 41]
61 let G: i64 = code_len // thunk_getinfo
62 let R: i64 = G + 21 // thunk_render
63 let F: i64 = R + 30 // factory (exported)
64 let text_used: i64 = F + 41
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
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, 0x56)
86
87 _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)
88 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 101, 100, 97, 116, 97, 0, 0, 0x56, edata_rva, 0x200, edata_file, PE_SECT_DATA_R)
89
90 // .text: compiled code
91 var i: i64 = 0
92 while i < code_len { buf[0x200 + i] = code[i]; i = i + 1 }
93
94 // thunk_getinfo @ 0x200+G (1-arg: self RCX->RDI; saves rdi/rsi):
95 // push rdi;push rsi;mov rdi,rcx;sub rsp,8;call nx_dev_get_info;add rsp,8;pop rsi;pop rdi;ret (21B)
96 let tg: i64 = 0x200 + G
97 _w8(buf,tg+0,0x57); _w8(buf,tg+1,0x56)
98 _w8(buf,tg+2,0x48); _w8(buf,tg+3,0x89); _w8(buf,tg+4,0xCF)
99 _w8(buf,tg+5,0x48); _w8(buf,tg+6,0x83); _w8(buf,tg+7,0xEC); _w8(buf,tg+8,0x08)
100 _w8(buf,tg+9,0xE8); _w32(buf, tg+10, getinfo_off - (G + 14))
101 _w8(buf,tg+14,0x48); _w8(buf,tg+15,0x83); _w8(buf,tg+16,0xC4); _w8(buf,tg+17,0x08)
102 _w8(buf,tg+18,0x5E); _w8(buf,tg+19,0x5F); _w8(buf,tg+20,0xC3)
103
104 // thunk_render @ 0x200+R (4-arg: RCX->RDI RDX->RSI R8->RDX R9->RCX; saves rdi/rsi):
105 // push rdi;push rsi;mov rdi,rcx;mov rsi,rdx;mov rdx,r8;mov rcx,r9;sub rsp,8;call nx_dev_render;add rsp,8;pop rsi;pop rdi;ret (30B)
106 let tr: i64 = 0x200 + R
107 _w8(buf,tr+0,0x57); _w8(buf,tr+1,0x56)
108 _w8(buf,tr+2,0x48); _w8(buf,tr+3,0x89); _w8(buf,tr+4,0xCF)
109 _w8(buf,tr+5,0x48); _w8(buf,tr+6,0x89); _w8(buf,tr+7,0xD6)
110 _w8(buf,tr+8,0x4C); _w8(buf,tr+9,0x89); _w8(buf,tr+10,0xC2)
111 _w8(buf,tr+11,0x4C); _w8(buf,tr+12,0x89); _w8(buf,tr+13,0xC9)
112 _w8(buf,tr+14,0x48); _w8(buf,tr+15,0x83); _w8(buf,tr+16,0xEC); _w8(buf,tr+17,0x08)
113 _w8(buf,tr+18,0xE8); _w32(buf, tr+19, render_off - (R + 23))
114 _w8(buf,tr+23,0x48); _w8(buf,tr+24,0x83); _w8(buf,tr+25,0xC4); _w8(buf,tr+26,0x08)
115 _w8(buf,tr+27,0x5E); _w8(buf,tr+28,0x5F); _w8(buf,tr+29,0xC3)
116
117 // factory @ 0x200+F (MS-ABI, mem in RCX). Builds object+vtable in the caller's buffer; returns mem. (41B)
118 // mov rax,rcx; lea rdx,[rip+d1]; mov [rcx+16],rdx; lea rdx,[rip+d2]; mov [rcx+24],rdx; lea rdx,[rcx+16]; mov [rcx],rdx;
119 // mov qword[rcx+8],0xC86432; ret (d1=-61 -> thunk_getinfo, d2=-51 -> thunk_render; both fixed by glue deltas)
120 let tf: i64 = 0x200 + F
121 _w8(buf,tf+0,0x48); _w8(buf,tf+1,0x89); _w8(buf,tf+2,0xC8) // mov rax,rcx
122 _w8(buf,tf+3,0x48); _w8(buf,tf+4,0x8D); _w8(buf,tf+5,0x15); _w32(buf, tf+6, G - (F + 10)) // lea rdx,[rip+d1]->getinfo thunk
123 _w8(buf,tf+10,0x48); _w8(buf,tf+11,0x89); _w8(buf,tf+12,0x51); _w8(buf,tf+13,0x10) // mov [rcx+16],rdx
124 _w8(buf,tf+14,0x48); _w8(buf,tf+15,0x8D); _w8(buf,tf+16,0x15); _w32(buf, tf+17, R - (F + 21))// lea rdx,[rip+d2]->render thunk
125 _w8(buf,tf+21,0x48); _w8(buf,tf+22,0x89); _w8(buf,tf+23,0x51); _w8(buf,tf+24,0x18) // mov [rcx+24],rdx
126 _w8(buf,tf+25,0x48); _w8(buf,tf+26,0x8D); _w8(buf,tf+27,0x51); _w8(buf,tf+28,0x10) // lea rdx,[rcx+16]
127 _w8(buf,tf+29,0x48); _w8(buf,tf+30,0x89); _w8(buf,tf+31,0x11) // mov [rcx],rdx
128 _w8(buf,tf+32,0x48); _w8(buf,tf+33,0xC7); _w8(buf,tf+34,0x41); _w8(buf,tf+35,0x08); _w32(buf, tf+36, 0xC86432) // mov qword[rcx+8],0xC86432
129 _w8(buf,tf+40,0xC3) // ret
130
131 // .edata @ edata_file (RVA edata_rva): 1 export "nishi_d3d_create_device" -> factory @ RVA 0x1000+F
132 let E: i64 = edata_file
133 _w32(buf, E + 12, edata_rva + 0x4A) // Name -> dllname
134 _w32(buf, E + 16, 1)
135 _w32(buf, E + 20, 1)
136 _w32(buf, E + 24, 1)
137 _w32(buf, E + 28, edata_rva + 0x28) // AddressOfFunctions
138 _w32(buf, E + 32, edata_rva + 0x2C) // AddressOfNames
139 _w32(buf, E + 36, edata_rva + 0x30) // AddressOfNameOrdinals
140 _w32(buf, E + 0x28, 0x1000 + F) // func[0] = factory
141 _w32(buf, E + 0x2C, edata_rva + 0x32) // name[0] RVA
142 _w16(buf, E + 0x30, 0) // ordinal[0]
143 cdv_wstr(buf, E + 0x32, "nishi_d3d_create_device\x00" as *u8)
144 cdv_wstr(buf, E + 0x4A, "nishicc.dll\x00" as *u8)
145 return file_size
146}
147
148func main() -> i64 {
149 cdv_puts("=== nx_pe_dll_com_device -- COM device (compiled methods behind an ID3D11-shaped vtable) ===\n" as *u8)
150 let lenbox: *i64 = sys_mmap(16) as *i64
151 let src: *u8 = sys_read_file("/tmp/nishi_export_lib.s" as *u8, lenbox)
152 if (src as i64) == 0 { cdv_puts("FAIL no /tmp/nishi_export_lib.s\n" as *u8); sys_exit(1); return 1 }
153 let n: i64 = lenbox[0]
154 if n <= 0 { cdv_puts("FAIL empty .s\n" as *u8); sys_exit(2); return 2 }
155
156 let code: *u8 = sys_mmap(1048576)
157 let gb: *i64 = sys_mmap(16) as *i64
158 let rb: *i64 = sys_mmap(16) as *i64
159 gb[0] = 0 - 1; rb[0] = 0 - 1
160 let code_len: i64 = cdv_assemble(src, n, code, 1048576, gb, "nx_dev_get_info" as *u8, rb, "nx_dev_render" as *u8)
161 if code_len < 0 { cdv_puts("FAIL assemble rc="); cdv_putn(code_len); cdv_puts("\n" as *u8); sys_exit(3); return 3 }
162 let getinfo_off: i64 = gb[0]
163 let render_off: i64 = rb[0]
164 if getinfo_off < 0 { cdv_puts("FAIL nx_dev_get_info not found\n" as *u8); sys_exit(4); return 4 }
165 if render_off < 0 { cdv_puts("FAIL nx_dev_render not found\n" as *u8); sys_exit(4); return 4 }
166 cdv_puts(" code_len="); cdv_putn(code_len); cdv_puts(" get_info@"); cdv_putn(getinfo_off); cdv_puts(" render@"); cdv_putn(render_off)
167 cdv_puts(" factory@"); cdv_putn(code_len + 51); cdv_puts("\n" as *u8)
168
169 let buf: *u8 = sys_mmap(0x10000)
170 let fsz: i64 = cdv_emit(buf, code, code_len, getinfo_off, render_off)
171 if fsz < 0 { cdv_puts("FAIL emit rc="); cdv_putn(fsz); cdv_puts("\n" as *u8); sys_exit(5); return 5 }
172
173 let fd: i64 = sys_openat_wr("knowledge/nishi_com_device.dll\x00" as *u8, 0x1a4)
174 if fd < 0 { cdv_puts("FAIL open output\n" as *u8); sys_exit(6); return 6 }
175 let wrote: i64 = sys_write(fd, buf, fsz)
176 sys_close(fd)
177 cdv_puts(" wrote "); cdv_putn(wrote); cdv_puts(" bytes -> knowledge/nishi_com_device.dll\n" as *u8)
178
179 var fails: i64 = 0
180 if (buf[0] as i64) != 0x4D { fails=fails+1 }
181 if (buf[0x84+18] as i64) != 0x22 { fails=fails+1 }
182 if wrote != fsz { fails=fails+1 }
183 if fails == 0 { cdv_puts("NX-PE-COM-DEVICE GREEN -- compiled methods behind a COM vtable; harness must create device + dispatch\n" as *u8); sys_exit(0); return 0 }
184 cdv_puts("NX-PE-COM-DEVICE RED fails="); cdv_putn(fails); cdv_puts("\n" as *u8)
185 sys_exit(1)
186 return 1
187}