windows: scan globals conservatively

Scan globals conservatively by reading writable sections from the PE
header.

I'd like to get rid of needing to precisely scan globals eventually, and
this brings us one step closer. It also avoids a bug with ThinLTO on
Windows.
Этот коммит содержится в:
Ayke van Laethem 2022-05-22 22:56:49 +02:00 коммит произвёл Ron Evans
родитель b52310fed2
коммит 5404c81ffd
4 изменённых файлов: 103 добавлений и 2 удалений

Просмотреть файл

@ -1,5 +1,5 @@
//go:build gc.conservative && !baremetal && !tinygo.wasm
// +build gc.conservative,!baremetal,!tinygo.wasm
//go:build gc.conservative && !baremetal && !tinygo.wasm && !windows
// +build gc.conservative,!baremetal,!tinygo.wasm,!windows
package runtime

Просмотреть файл

@ -11,6 +11,8 @@ import (
"unsafe"
)
const gcAsserts = false // perform sanity checks
// Ever-incrementing pointer: no memory is freed.
var heapptr = heapStart
@ -76,3 +78,7 @@ func setHeapEnd(newHeapEnd uintptr) {
// enough.
heapEnd = newHeapEnd
}
func markRoots(start, end uintptr) {
// dummy, so that markGlobals will compile
}

Просмотреть файл

@ -11,6 +11,8 @@ import (
"unsafe"
)
const gcAsserts = false // perform sanity checks
func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer
func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer
@ -38,3 +40,7 @@ func initHeap() {
func setHeapEnd(newHeapEnd uintptr) {
// Nothing to do here, this function is never actually called.
}
func markRoots(start, end uintptr) {
// dummy, so that markGlobals will compile
}

Просмотреть файл

@ -1,3 +1,92 @@
package runtime
import "unsafe"
const GOOS = "windows"
//export GetModuleHandleExA
func _GetModuleHandleExA(dwFlags uint32, lpModuleName unsafe.Pointer, phModule **exeHeader) bool
// MS-DOS stub with PE header offset:
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#ms-dos-stub-image-only
type exeHeader struct {
signature uint16
_ [58]byte // skip DOS header
peHeader uint32 // at offset 0x3C
}
// COFF file header:
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#file-headers
type peHeader struct {
magic uint32
machine uint16
numberOfSections uint16
timeDateStamp uint32
pointerToSymbolTable uint32
numberOfSymbols uint32
sizeOfOptionalHeader uint16
characteristics uint16
}
// COFF section header:
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers
type peSection struct {
name [8]byte
virtualSize uint32
virtualAddress uint32
sizeOfRawData uint32
pointerToRawData uint32
pointerToRelocations uint32
pointerToLinenumbers uint32
numberOfRelocations uint16
numberOfLinenumbers uint16
characteristics uint32
}
var module *exeHeader
// Mark global variables.
// Unfortunately, the linker doesn't provide symbols for the start and end of
// the data/bss sections. Therefore these addresses need to be determined at
// runtime. This might seem complex and it kind of is, but it only compiles to
// around 160 bytes of amd64 instructions.
// Most of this function is based on the documentation in
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format.
func markGlobals() {
// Constants used in this function.
const (
// https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexa
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 0x00000002
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
IMAGE_SCN_MEM_WRITE = 0x80000000
)
if module == nil {
// Obtain a handle to the currently executing image. What we're getting
// here is really just __ImageBase, but it's probably better to obtain
// it using GetModuleHandle to account for ASLR etc.
result := _GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, nil, &module)
if gcAsserts && (!result || module.signature != 0x5A4D) { // 0x4D5A is "MZ"
runtimePanic("cannot get module handle")
}
}
// Find the PE header at offset 0x3C.
pe := (*peHeader)(unsafe.Pointer(uintptr(unsafe.Pointer(module)) + uintptr(module.peHeader)))
if gcAsserts && pe.magic != 0x00004550 { // 0x4550 is "PE"
runtimePanic("cannot find PE header")
}
// Iterate through sections.
section := (*peSection)(unsafe.Pointer(uintptr(unsafe.Pointer(pe)) + uintptr(pe.sizeOfOptionalHeader) + unsafe.Sizeof(peHeader{})))
for i := 0; i < int(pe.numberOfSections); i++ {
if section.characteristics&IMAGE_SCN_MEM_WRITE != 0 {
// Found a writable section. Scan the entire section for roots.
start := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress)
end := uintptr(unsafe.Pointer(module)) + uintptr(section.virtualAddress) + uintptr(section.virtualSize)
markRoots(start, end)
}
section = (*peSection)(unsafe.Pointer(uintptr(unsafe.Pointer(section)) + unsafe.Sizeof(peSection{})))
}
}