mirror of https://github.com/tinygo-org/tinygo.git
Browse Source
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.pull/2857/head
Ayke van Laethem
2 years ago
committed by
Ron Evans
4 changed files with 103 additions and 2 deletions
@ -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{}))) |
|||
} |
|||
} |
|||
|
Loading…
Reference in new issue