diff --git a/Makefile b/Makefile index 5de97014..c67d5e66 100644 --- a/Makefile +++ b/Makefile @@ -480,7 +480,7 @@ endif $(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go @$(MD5SUM) test.hex GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo - GOOS=windows GOARCH=amd64 $(TINYGO) build -o test.exe ./testdata/cgo + GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo ifneq ($(OS),Windows_NT) # TODO: this does not yet work on Windows. Somehow, unused functions are # not garbage collected. diff --git a/builder/sizes.go b/builder/sizes.go index 7297f42f..584549ec 100644 --- a/builder/sizes.go +++ b/builder/sizes.go @@ -4,6 +4,7 @@ import ( "bytes" "debug/dwarf" "debug/elf" + "debug/pe" "encoding/binary" "fmt" "io" @@ -356,6 +357,72 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz }) } } + } else if file, err := pe.NewFile(f); err == nil { + // Read DWARF information. The error is intentionally ignored. + data, _ := file.DWARF() + if data != nil { + addresses, err = readProgramSizeFromDWARF(data, 0) + if err != nil { + // However, _do_ report an error here. Something must have gone + // wrong while trying to parse DWARF data. + return nil, err + } + } + + // Read COFF sections. + optionalHeader := file.OptionalHeader.(*pe.OptionalHeader64) + for _, section := range file.Sections { + // For more information: + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_section_header + const ( + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 + ) + if section.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + // Debug sections, etc. + continue + } + address := uint64(section.VirtualAddress) + optionalHeader.ImageBase + if section.Characteristics&IMAGE_SCN_CNT_CODE != 0 { + // .text + sections = append(sections, memorySection{ + Address: address, + Size: uint64(section.VirtualSize), + Type: memoryCode, + }) + } else if section.Characteristics&IMAGE_SCN_CNT_INITIALIZED_DATA != 0 { + if section.Characteristics&IMAGE_SCN_MEM_WRITE != 0 { + // .data + sections = append(sections, memorySection{ + Address: address, + Size: uint64(section.Size), + Type: memoryData, + }) + if section.Size < section.VirtualSize { + // Equivalent of a .bss section. + // Note: because of how the PE/COFF format is + // structured, not all zero-initialized data is marked + // as such. A portion may be at the end of the .data + // section and is thus marked as initialized data. + sections = append(sections, memorySection{ + Address: address + uint64(section.Size), + Size: uint64(section.VirtualSize) - uint64(section.Size), + Type: memoryBSS, + }) + } + } else if section.Characteristics&IMAGE_SCN_MEM_READ != 0 { + // .rdata, .buildid, .pdata + sections = append(sections, memorySection{ + Address: address, + Size: uint64(section.VirtualSize), + Type: memoryROData, + }) + } + } + } } else if file, err := wasm.Parse(f); err == nil { // File is in WebAssembly format.