8l: Windows 386 PE that prints via kernel32 (-H 10)#7
Open
rafael2knokia wants to merge 2 commits into
Open
Conversation
3 tasks
Wire the PE writer into 6l so it can produce 64-bit Windows executables
that actually do I/O, the amd64 counterpart to the 386 support on 8l.
src/cmd/ld/pe.{c,h}: add the PE32+ path.
- New IMAGE_OPTIONAL_HEADER64 (ImageBase and the stack/heap sizes widen
to 8 bytes, BaseOfData dropped; written verbatim like the 32-bit
header). peinit() already set pe64 for thechar=='6'; asmbpe() now
emits Magic 0x20b, MajorSubsystem/OS 6.0 (64-bit Windows predates
4.0), LARGE_ADDRESS_AWARE, and fills oh64 from oh.
- The import table now imports GetStdHandle, WriteFile and ExitProcess
from kernel32. peimports() (run from dope(), before reloc()) lays out
the .idata section and defines an __imp_<name> linker symbol at each
import's IAT slot, so a program can call through the thunk with
"MOVQ __imp_WriteFile(SB), AX; CALL AX". IAT entries and the null
terminator are 8 bytes wide under pe64.
- add_import_table() now only writes the bytes (layout done earlier),
and writes them at the section's PointerToRawData rather than the
current EOF (with non-empty .data the file position is not
page-aligned, which left the image "truncated").
- dope() skips zero-size .data/.bss: an empty section does not advance
the virtual-address cursor, so the next section would collide on
VirtualAddress and the loader rejects the image.
src/cmd/6l: compile pe.c (symlink + mkfile), add "-H 10" / goos=windows
handling in obj.c (peinit + case 10, defaulting INITTEXT=PEBASE+0x1000
like 8l), call dope() before reloc() so the __imp_* symbols resolve, and
dispatch asmbpe() from asmb(). 6l has no PE symbol-table path yet, so
obj.c forces -s for -H 10.
tests/s/mini: hello_windows_amd64.s prints "Hello, World!" via
GetStdHandle + WriteFile + ExitProcess (Win64 ABI: args in CX/DX/R8/R9,
32-byte shadow space, 16-byte aligned stack), built with 6l -H 10.
Verified on Linux with wine: `6l -s -H 10` produces a
"PE32+ executable (console) x86-64" (Magic 0x20b, ImageBase 0x400000)
that prints "Hello, World!" and exits 0.
rafael2knokia
force-pushed
the
windows-smoke
branch
from
July 11, 2026 00:25
20b684b to
89731a0
Compare
Builds on the shared PE import machinery added for amd64: hook 8l up so a 386 PE can call into kernel32 and print, the counterpart to the 6l amd64 support. src/cmd/8l/obj.c: call dope() before reloc() (not after) so the __imp_<name> import symbols defined while laying out .idata resolve during relocation; force -s for -H 10 (asmbpe() would otherwise emit a .symdat section after .idata and perturb the layout; matches 6l). tests/s/mini: hello_windows_386.s now prints "Hello, World!" via GetStdHandle + WriteFile + ExitProcess instead of just returning. It uses the Win32 __stdcall convention (args on the stack right-to-left, callee pops them). The argument area is reserved with SUBL and filled with MOVL rather than PUSHL: 8l statically balances PUSH/POP within a function and cannot see that the callee pops, so PUSHL trips "unbalanced PUSH/POP". The import thunk is called indirectly (MOVL __imp_WriteFile(SB), AX; CALL AX) because a direct "CALL sym(SB)" is rejected by patch() for non-text targets. mkfile: build hello_windows_386.exe with 8l_ -s -H 10 and enable it in WINDOWSBINS alongside amd64. Verified on Linux with wine: `8l_ -s -H 10` produces a "PE32 executable (console) Intel 80386" that prints "Hello, World!" and exits 0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
8l_ -H 10) now prints to the console instead of just returning.8l's pass order and makeshello_windows_386.scallGetStdHandle+WriteFile+ExitProcessusing the shared PE import machinery.Details
src/cmd/8l/obj.c: calldope()beforereloc()so the__imp_*import symbols resolve; force-sfor-H 10(avoids a.symdatsection that would perturb the import layout; matches6l).hello_windows_386.s: prints via the Win32__stdcallconvention. The arg area is reserved withSUBL/MOVLrather thanPUSHLbecause8lstatically balances PUSH/POP and can't see the callee pop. Thunks are called indirectly (MOVL __imp_WriteFile(SB), AX; CALL AX).mkfile: build with8l_ -s -H 10, enabled inWINDOWSBINSalongside amd64.Stacking (please read)
This PR is stacked on top of #8 and, because GitHub cross-fork PRs can't use a fork branch as base, its diff also contains #8's commit (
6l: PE32+ output for Windows amd64). The 386 printing reuses the shared kernel32 import machinery that #8 introduces insrc/cmd/ld/pe.c. Please review/merge #8 first; then this PR reduces to just the8lcommit. (Happy to squash both into one PR if you prefer.)Test plan
8l_ -s -H 10builds aPE32 executable (console) Intel 80386.wine: printsHello, World!, exits 0.mk allintests/s/minipasses; plan9 386 (-H 2) path unaffected.