-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
What version of Go are you using (go version
)?
> go version go version go1.19.3 windows/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
> go env set GO111MODULE= set GOARCH=amd64 set GOBIN= set GOCACHE=C:\Users\kevpar\AppData\Local\go-build set GOENV=C:\Users\kevpar\AppData\Roaming\go\env set GOEXE=.exe set GOEXPERIMENT= set GOFLAGS= set GOHOSTARCH=amd64 set GOHOSTOS=windows set GOINSECURE= set GOMODCACHE=C:\Users\kevpar\go\pkg\mod set GONOPROXY= set GONOSUMDB= set GOOS=windows set GOPATH=C:\Users\kevpar\go set GOPRIVATE= set GOPROXY=https://proxy.golang.org,direct set GOROOT=C:\Program Files\Go set GOSUMDB=sum.golang.org set GOTMPDIR= set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64 set GOVCS= set GOVERSION=go1.19.3 set GCCGO=gccgo set GOAMD64=v1 set AR=ar set CC=gcc set CXX=g++ set CGO_ENABLED=1 set GOMOD=NUL set GOWORK= set CGO_CFLAGS=-g -O2 set CGO_CPPFLAGS= set CGO_CXXFLAGS=-g -O2 set CGO_FFLAGS=-g -O2 set CGO_LDFLAGS=-g -O2 set PKG_CONFIG=pkg-config set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=C:\Users\kevpar\AppData\Local\Temp\go-build397785229=/tmp/go-build -gno-record-gcc-switches
What did you do?
- Created a simple Go program.
- Compiled with
GOOS=windows
as hello.exe. - Tested running it under DOS (tried both DOSBox and FreeDOS).
What did you expect to see?
Program should print This program cannot be run in DOS mode.
and exit.
What did you see instead?
DOSBox crashed when the program was run. On FreeDOS the program appeared to do nothing.
Details
First, off, it likely goes without saying this is a very inconsequential bug. I imagine the number of users who will run a Go-built binary on a DOS system these days is quite small. :)
Windows PE binaries contain a old DOS-style (MZ) header at the front. This is generally used to point to a short amount of code that prints out a message saying the program cannot run under DOS. Go embeds a DOS header into binaries it builds, but this header appears to be invalid.
The structure of the header, per the Windows SDK, looks like this:
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
In a Go program, most of the values are correct, but the e_crlc
(number of relocations) is set to 4, and e_cparhdr
(size of header in 16-bit words) is set to 0. On other Windows programs I examined, these values were flipped. Having e_cparhdr
set to 0 appears to mean that execution will start at offset 0 in the file, rather than 0x40 where executable code actually appears. This results in the program executing invalid instructions.
Using a e_crlc
of 0 and e_cparhdr
of 4 should fix the issue. This change can be made here: https://github.com/golang/go/blob/master/src/cmd/link/internal/ld/pe.go#L155
As far as I can tell, this PE header has been incorrect as long as it has existed in Go. If anyone has context on whether this has ever worked, I'd be curious to know.