code wiki / _hdl_build / nx_pe_dll_emit_gate.nx
nx_pe_dll_emit_gate.nx source
↩ module page · 148 lines · 8463 B
1// nx_pe_dll_emit_gate.nx -- WINDOWS-NATIVE CRUX: emit a sovereign PE *DLL with a named export* that a FOREIGN Windows
2// process can LoadLibrary + GetProcAddress + call. This is the mechanism a Steam game needs: it loads d3d11.dll and
3// calls its exports, so our lib must be a loadable DLL a foreign binary can bind. Here we prove the loadability path
4// end to end with a minimal export (nishi_add(a,b)=a+b). No DllMain (AddressOfEntryPoint=0 -> loader binds directly).
5// Based at 0x180000000 so it never collides with the harness .exe (default 0x140000000); code is position-independent.
6// 100% NishiLang PE bytes; a C harness (benchmark oracle, like WARP/fxc) is the external verifier. license_tier: ORIGINAL expect_exit: 0
7import "nx_syscalls.nx"
8
9func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
10func pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 }
11func px2(v: i64) -> i64 { let d: *u8="0123456789abcdef" as *u8; let b: *u8=sys_mmap(4) as *u8; b[0]=d[(v>>4)&15]; b[1]=d[v&15]; sys_write(1,b,2); return 0 }
12
13func w8(buf: *u8, off: i64, v: i64) -> i64 { buf[off] = (v & 255) as u8; return 0 }
14func w16(buf: *u8, off: i64, v: i64) -> i64 { buf[off]=(v&255) as u8; buf[off+1]=((v>>8)&255) as u8; return 0 }
15func w32(buf: *u8, off: i64, v: i64) -> i64 { buf[off]=(v&255) as u8; buf[off+1]=((v>>8)&255) as u8; buf[off+2]=((v>>16)&255) as u8; buf[off+3]=((v>>24)&255) as u8; return 0 }
16func 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 }
17
18func main() -> i64 {
19 hw("=== nx_pe_dll_emit_gate -- emit a sovereign PE DLL with a named export (Windows-native loadability crux) ===\n" as *u8)
20 var fails: i64 = 0
21 let N: i64 = 0x600
22 let b: *u8 = sys_mmap(N) // zeroed
23
24 // ---- DOS header ----
25 w16(b, 0, 0x5A4D) // 'MZ'
26 w32(b, 0x3C, 0x80) // e_lfanew -> PE sig at 0x80
27
28 // ---- PE signature ----
29 w32(b, 0x80, 0x00004550) // 'PE\0\0'
30
31 // ---- COFF file header @ 0x84 ----
32 w16(b, 0x84 + 0, 0x8664) // Machine AMD64
33 w16(b, 0x84 + 2, 2) // NumberOfSections (.text + .edata)
34 w32(b, 0x84 + 4, 0) // TimeDateStamp
35 w32(b, 0x84 + 8, 0) // PointerToSymbolTable
36 w32(b, 0x84 + 12, 0) // NumberOfSymbols
37 w16(b, 0x84 + 16, 0xF0) // SizeOfOptionalHeader
38 w16(b, 0x84 + 18, 0x2022) // Characteristics: EXECUTABLE(0x02)|LARGE_ADDR(0x20)|DLL(0x2000)
39
40 // ---- Optional header (PE32+) @ 0x98 ----
41 let O: i64 = 0x98
42 w16(b, O + 0, 0x020B) // Magic PE32+
43 w8(b, O + 2, 1) // MajorLinkerVersion
44 w32(b, O + 4, 0x200) // SizeOfCode
45 w32(b, O + 8, 0x200) // SizeOfInitializedData
46 w32(b, O + 12, 0) // SizeOfUninitializedData
47 w32(b, O + 16, 0) // AddressOfEntryPoint = 0 -> no DllMain, loader binds directly
48 w32(b, O + 20, 0x1000) // BaseOfCode
49 w32(b, O + 24, 0x80000000) // ImageBase lo = 0x180000000 (out of the way of a default-based .exe)
50 w32(b, O + 28, 0x1) // ImageBase hi
51 w32(b, O + 32, 0x1000) // SectionAlignment
52 w32(b, O + 36, 0x200) // FileAlignment
53 w16(b, O + 40, 6) // MajorOSVersion
54 w16(b, O + 48, 6) // MajorSubsystemVersion
55 w32(b, O + 56, 0x3000) // SizeOfImage (headers + .text + .edata pages)
56 w32(b, O + 60, 0x200) // SizeOfHeaders
57 w16(b, O + 68, 3) // Subsystem CONSOLE
58 w16(b, O + 70, 0) // DllCharacteristics = 0 (no ASLR; code is base-independent, no reloc needed)
59 w32(b, O + 72, 0x100000) // SizeOfStackReserve lo
60 w32(b, O + 80, 0x1000) // SizeOfStackCommit lo
61 w32(b, O + 88, 0x100000) // SizeOfHeapReserve lo
62 w32(b, O + 96, 0x1000) // SizeOfHeapCommit lo
63 w32(b, O + 108, 16) // NumberOfRvaAndSizes
64 // DataDirectory[0] = Export table (RVA 0x2000, size 0x49)
65 w32(b, O + 112 + 0, 0x2000)
66 w32(b, O + 112 + 4, 0x49)
67 // [1] Import = 0 (no imports needed)
68
69 // ---- Section table @ 0x188 ----
70 let S: i64 = 0x188
71 // .text
72 wstr(b, S + 0, ".text\x00" as *u8)
73 w32(b, S + 8, 0x10) // VirtualSize
74 w32(b, S + 12, 0x1000) // VirtualAddress
75 w32(b, S + 16, 0x200) // SizeOfRawData
76 w32(b, S + 20, 0x200) // PointerToRawData
77 w32(b, S + 36, 0x60000020) // CODE|EXEC|READ
78 // .edata
79 wstr(b, S + 40 + 0, ".edata\x00" as *u8)
80 w32(b, S + 40 + 8, 0x49) // VirtualSize
81 w32(b, S + 40 + 12, 0x2000) // VirtualAddress
82 w32(b, S + 40 + 16, 0x200) // SizeOfRawData
83 w32(b, S + 40 + 20, 0x400) // PointerToRawData
84 w32(b, S + 40 + 36, 0x40000040) // INITIALIZED_DATA|READ
85
86 // ---- .text @ file 0x200 (RVA 0x1000): int nishi_add(int a,int b){return a+b;} ----
87 // MS x64: a=ecx, b=edx, ret in eax. mov eax,ecx ; add eax,edx ; ret
88 w8(b, 0x200, 0x89); w8(b, 0x201, 0xC8) // mov eax, ecx
89 w8(b, 0x202, 0x01); w8(b, 0x203, 0xD0) // add eax, edx
90 w8(b, 0x204, 0xC3) // ret
91
92 // ---- .edata @ file 0x400 (RVA 0x2000): IMAGE_EXPORT_DIRECTORY + tables + strings ----
93 let E: i64 = 0x400
94 // Export Directory (40 bytes)
95 w32(b, E + 0, 0) // Characteristics
96 w32(b, E + 4, 0) // TimeDateStamp
97 w16(b, E + 8, 0) // MajorVersion
98 w16(b, E + 10, 0) // MinorVersion
99 w32(b, E + 12, 0x203C) // Name RVA -> "nishigpu.dll"
100 w32(b, E + 16, 1) // Base (ordinal base)
101 w32(b, E + 20, 1) // NumberOfFunctions
102 w32(b, E + 24, 1) // NumberOfNames
103 w32(b, E + 28, 0x2028) // AddressOfFunctions RVA
104 w32(b, E + 32, 0x202C) // AddressOfNames RVA
105 w32(b, E + 36, 0x2030) // AddressOfNameOrdinals RVA
106 // AddressOfFunctions[0] @ 0x2028 -> function RVA 0x1000
107 w32(b, E + 0x28, 0x1000)
108 // AddressOfNames[0] @ 0x202C -> funcname RVA 0x2032
109 w32(b, E + 0x2C, 0x2032)
110 // AddressOfNameOrdinals[0] @ 0x2030 -> 0 (index into function table)
111 w16(b, E + 0x30, 0)
112 // funcname "nishi_add\0" @ 0x2032
113 wstr(b, E + 0x32, "nishi_add\x00" as *u8)
114 // dllname "nishigpu.dll\0" @ 0x203C
115 wstr(b, E + 0x3C, "nishigpu.dll\x00" as *u8)
116
117 // ---- write the DLL ----
118 let fd: i64 = sys_openat_wr("knowledge/nishigpu.dll\x00" as *u8, 0x1a4)
119 if fd < 0 { hw("FAIL cannot open output\n" as *u8); sys_exit(1); return 1 }
120 let wrote: i64 = sys_write(fd, b, N)
121 sys_close(fd)
122
123 // ---- self-checks ----
124 hw(" wrote "); pn(wrote); hw(" bytes to knowledge/nishigpu.dll\n" as *u8)
125 hw(" MZ="); px2(b[0] as i64); px2(b[1] as i64)
126 hw(" PEsig="); px2(b[0x80] as i64); px2(b[0x81] as i64); px2(b[0x82] as i64); px2(b[0x83] as i64)
127 hw(" Char="); px2(b[0x84+19] as i64); px2(b[0x84+18] as i64)
128 hw(" export.name.rva=0x"); px2((0x203C>>8)&255); px2(0x203C&255); hw("\n" as *u8)
129
130 var t1: i64 = 0
131 if (b[0] as i64) == 0x4D { if (b[1] as i64) == 0x5A { t1 = 1 } }
132 if t1 == 1 { hw("T1 PASS MZ signature\n" as *u8) } else { fails=fails+1; hw("T1 FAIL\n" as *u8) }
133 var t2: i64 = 0
134 if (b[0x84+18] as i64) == 0x22 { if (b[0x84+19] as i64) == 0x20 { t2 = 1 } } // 0x2022 DLL bit set
135 if t2 == 1 { hw("T2 PASS IMAGE_FILE_DLL characteristic (0x2022)\n" as *u8) } else { fails=fails+1; hw("T2 FAIL char\n" as *u8) }
136 var t3: i64 = 0
137 if wrote == N { t3 = 1 }
138 if t3 == 1 { hw("T3 PASS full image written (0x600 bytes)\n" as *u8) } else { fails=fails+1; hw("T3 FAIL wrote="); pn(wrote); hw("\n" as *u8) }
139 // T4 export directory Name RVA + funcname bytes present
140 var t4: i64 = 0
141 if (b[0x400 + 0x32] as i64) == 110 { if (b[0x400 + 0x33] as i64) == 105 { t4 = 1 } } // 'n','i' of nishi_add
142 if t4 == 1 { hw("T4 PASS export name 'nishi_add' present in .edata\n" as *u8) } else { fails=fails+1; hw("T4 FAIL export name\n" as *u8) }
143
144 if fails == 0 { hw("NX-PE-DLL verdict=GREEN -- sovereign PE DLL with named export emitted; C harness must now load+call it\n" as *u8); sys_exit(0); return 0 }
145 hw("NX-PE-DLL RED fails="); pn(fails); hw("\n" as *u8)
146 sys_exit(1)
147 return 1
148}