From 8e557fe36939e0a6f8d002a78b29d56ca00417ea Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Sun, 10 May 2026 01:39:40 +0100 Subject: [PATCH] contrib: Add bootstrap-go-toolchain.sh Ever since w64devkit began forcing bigobj by default, it has been incompatible with cgo. I patched go to support bigobj, but unfortunately upstream the patch ran in to bureaucracy/difficulties and I ran out of patience to get it through. So that means it's not really an option to use upstream go binaries, it must build from source. Bootstrapping go is not difficult, but it has grown more tedious as more required stages have been added to the bootstrap chain. So, to spare myself some tedium, I have created bootstrap-go-toolchain.sh. It builds the latest version of go, from source, using only w64devkit's gcc compiler. It also adds my bigobj patches to the toolchain, so cgo works properly. --- contrib/gobuild/bootstrap-go-toolchain.sh | 151 +++++++ contrib/gobuild/go-bigobj.patch | 480 ++++++++++++++++++++++ 2 files changed, 631 insertions(+) create mode 100644 contrib/gobuild/bootstrap-go-toolchain.sh create mode 100644 contrib/gobuild/go-bigobj.patch diff --git a/contrib/gobuild/bootstrap-go-toolchain.sh b/contrib/gobuild/bootstrap-go-toolchain.sh new file mode 100644 index 0000000..0dda09e --- /dev/null +++ b/contrib/gobuild/bootstrap-go-toolchain.sh @@ -0,0 +1,151 @@ +#!/bin/sh +# Build Go from source on Windows using w64devkit. +# Bootstraps from Go 1.4 through intermediate versions up to the latest. +# +# Usage: ./bootstrap-go-toolchain.sh [INSTALL_DIR] +# INSTALL_DIR Where to install the final Go toolchain (default: ./go) +# +# Prerequisites: w64devkit in PATH (provides gcc, wget, tar, patch, sh) +# +# The final version is patched with go-bigobj.patch located next to +# this script before building. +# Tarballs are verified against the SHA-256 values embedded below. +# +# Bootstrap chain (per https://go.dev/doc/install/source): +# Go 1.4 -> built with C compiler (gcc from w64devkit) +# Go 1.17 -> built with Go 1.4 +# Go 1.20 -> built with Go 1.17 +# Go 1.22 -> built with Go 1.20 +# Go 1.24 -> built with Go 1.22 +# Go 1.26 -> built with Go 1.24 +# +# Go 1.N requires Go 1.M to build, where M = N-2 rounded down to even. + +set -e + +# The Windows bootstrap breaks if GOBIN is present in the environment. +unset GOBIN + +# --- Version configuration (update as needed) --- +BOOTSTRAP_VERSIONS="1.17.13 1.20.14 1.22.12 1.24.6" +FINAL_VERSION=1.26.3 + +# --- Paths --- +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +BUILD="$SCRIPT_DIR/build" +DOWNLOADS="$SCRIPT_DIR/downloads" +PATCH_FILE="$SCRIPT_DIR/go-bigobj.patch" +INSTALL="${1:-$SCRIPT_DIR/go}" + +# --- URLs --- +GO14_URL="https://github.com/golang/go/archive/refs/heads/release-branch.go1.4.tar.gz" +GO_DL_URL="https://go.dev/dl" + +# --- SHA-256 sums --- +SHA256SUMS='10273543295cf4d97df5b1ff9b1db4089099b7cada99f14cfa1dfae9be789556 go1.4-branch.tar.gz +a1a48b23afb206f95e7bbaa9b898d965f90826f6f1d1fc0c1d784ada0cd300fd go1.17.13.src.tar.gz +1aef321a0e3e38b7e91d2d7eb64040666cabdcc77d383de3c9522d0d69b67f4e go1.20.14.src.tar.gz +012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71 go1.22.12.src.tar.gz +e1cb5582aab588668bc04c07de18688070f6b8c9b2aaf361f821e19bd47cfdbd go1.24.6.src.tar.gz +1c646875d0aa8799133184ed57cf79ff24bdefe8c8820470602a9d3d6d9192b8 go1.26.3.src.tar.gz' + +lookup_checksum_entry() { + name="$1" + printf '%s\n' "$SHA256SUMS" | awk -v name="$name" '$2 == name { print; found=1; exit } END { if (!found) exit 1 }' +} + +verify_download() { + name="$1" + entry="$(lookup_checksum_entry "$name")" || { + printf 'Missing embedded checksum for %s\n' "$name" >&2 + return 1 + } + + ( + cd "$DOWNLOADS" + printf '%s\n' "$entry" | sha256sum -c + ) +} + +download() { + dest="$1" + url="$2" + name="$(basename "$dest")" + + if [ -f "$dest" ]; then + if verify_download "$name" >/dev/null; then + printf 'Verified cached %s\n' "$name" + return + fi + + printf 'Checksum mismatch for %s, re-downloading\n' "$name" + rm -f "$dest" + fi + + printf "Downloading %s\n" "$name" + wget -O "$dest" "$url" + verify_download "$name" +} + +echo "Go bootstrap build" +echo " Install to: $INSTALL" +echo "" + +if [ ! -f "$PATCH_FILE" ]; then + printf "Missing required patch: %s\n" "$PATCH_FILE" >&2 + exit 1 +fi + +mkdir -p "$BUILD" "$DOWNLOADS" + +# Download all source tarballs +download "$DOWNLOADS/go1.4-branch.tar.gz" "$GO14_URL" +for v in $BOOTSTRAP_VERSIONS $FINAL_VERSION; do + download "$DOWNLOADS/go${v}.src.tar.gz" "$GO_DL_URL/go${v}.src.tar.gz" +done + +# Build Go 1.4 from C source +echo "" +echo "===== Building Go 1.4 (C bootstrap) =====" +rm -rf "$BUILD/go1.4" +mkdir -p "$BUILD/go1.4" +tar xf "$DOWNLOADS/go1.4-branch.tar.gz" -C "$BUILD/go1.4" --strip-components=1 +cd "$BUILD/go1.4/src" +CGO_ENABLED=0 cmd /c make.bat + +PREV="$BUILD/go1.4" + +# Build each intermediate version +for v in $BOOTSTRAP_VERSIONS; do + echo "" + echo "===== Building Go $v =====" + rm -rf "$BUILD/go${v}" + mkdir -p "$BUILD/go${v}" + tar xf "$DOWNLOADS/go${v}.src.tar.gz" -C "$BUILD/go${v}" --strip-components=1 + cd "$BUILD/go${v}/src" + CGO_ENABLED=0 GOROOT_BOOTSTRAP="$PREV" cmd /c make.bat + PREV="$BUILD/go${v}" +done + +# Build final version with patches +echo "" +echo "===== Building Go $FINAL_VERSION =====" +rm -rf "$BUILD/go${FINAL_VERSION}" +mkdir -p "$BUILD/go${FINAL_VERSION}" +tar xf "$DOWNLOADS/go${FINAL_VERSION}.src.tar.gz" -C "$BUILD/go${FINAL_VERSION}" --strip-components=1 + +printf "Applying %s\n" "$(basename "$PATCH_FILE")" +(cd "$BUILD/go${FINAL_VERSION}" && patch -p1 < "$PATCH_FILE") + +cd "$BUILD/go${FINAL_VERSION}/src" +CGO_ENABLED=1 GOROOT_BOOTSTRAP="$PREV" cmd /c make.bat + +# Install +echo "" +echo "===== Installing =====" +rm -rf "$INSTALL" +cp -r "$BUILD/go${FINAL_VERSION}" "$INSTALL" + +echo "" +echo "Go $FINAL_VERSION installed to $INSTALL" +echo "Add $INSTALL/bin to your PATH." diff --git a/contrib/gobuild/go-bigobj.patch b/contrib/gobuild/go-bigobj.patch new file mode 100644 index 0000000..13eda9b --- /dev/null +++ b/contrib/gobuild/go-bigobj.patch @@ -0,0 +1,480 @@ +From a3c23cfacd9ab6d02d98a0426af7c52a965e4683 Mon Sep 17 00:00:00 2001 +From: Peter0x44 +Date: Fri, 26 Sep 2025 17:59:23 +0100 +Subject: [PATCH] debug/pe: add support for bigobj COFF format + +This adds support for parsing bigobj COFF files, which use a different +header format with a 32-bit section count, and symbols with a 32 bit +section number. + +This fixes linking bigobj COFF with cgo. + +Fixes #24341 +--- + src/debug/pe/file.go | 176 +++++++++++++++++++++++++++++++++++++---- + src/debug/pe/pe.go | 31 ++++++++ + src/debug/pe/string.go | 13 ++- + src/debug/pe/symbol.go | 100 ++++++++++++++++------- + 4 files changed, 273 insertions(+), 47 deletions(-) + +diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go +index ed63a11cb6e98c..cbd49f7923bec4 100644 +--- a/src/debug/pe/file.go ++++ b/src/debug/pe/file.go +@@ -29,7 +29,11 @@ import ( + + // A File represents an open PE file. + type File struct { +- FileHeader ++ // FileHeader is populated for regular COFF files ++ FileHeader *FileHeader ++ // BigObjHeader is populated for bigobj COFF files ++ BigObjHeader *BigObjHeader ++ + OptionalHeader any // of type *OptionalHeader32 or *OptionalHeader64 + Sections []*Section + Symbols []*Symbol // COFF symbols with auxiliary symbol records removed +@@ -39,6 +43,69 @@ type File struct { + closer io.Closer + } + ++// IsBigObj reports whether the file is a bigobj COFF file. ++func (f *File) IsBigObj() bool { ++ return f.BigObjHeader != nil ++} ++ ++// GetMachine returns the machine type from the appropriate header. ++func (f *File) GetMachine() uint16 { ++ if f.BigObjHeader != nil { ++ return f.BigObjHeader.Machine ++ } ++ return f.FileHeader.Machine ++} ++ ++// GetNumberOfSections returns the number of sections from the appropriate header. ++func (f *File) GetNumberOfSections() uint32 { ++ if f.BigObjHeader != nil { ++ return f.BigObjHeader.NumberOfSections ++ } ++ return uint32(f.FileHeader.NumberOfSections) ++} ++ ++// GetTimeDateStamp returns the timestamp from the appropriate header. ++func (f *File) GetTimeDateStamp() uint32 { ++ if f.BigObjHeader != nil { ++ return f.BigObjHeader.TimeDateStamp ++ } ++ return f.FileHeader.TimeDateStamp ++} ++ ++// GetPointerToSymbolTable returns the symbol table pointer from the appropriate header. ++func (f *File) GetPointerToSymbolTable() uint32 { ++ if f.BigObjHeader != nil { ++ return f.BigObjHeader.PointerToSymbolTable ++ } ++ return f.FileHeader.PointerToSymbolTable ++} ++ ++// GetNumberOfSymbols returns the number of symbols from the appropriate header. ++func (f *File) GetNumberOfSymbols() uint32 { ++ if f.BigObjHeader != nil { ++ return f.BigObjHeader.NumberOfSymbols ++ } ++ return f.FileHeader.NumberOfSymbols ++} ++ ++// GetSizeOfOptionalHeader returns the optional header size from the appropriate header. ++// BigObj files don't have optional headers, so this returns 0 for them. ++func (f *File) GetSizeOfOptionalHeader() uint16 { ++ if f.BigObjHeader != nil { ++ return 0 ++ } ++ return f.FileHeader.SizeOfOptionalHeader ++} ++ ++// GetCharacteristics returns the characteristics from the appropriate header. ++// BigObj files don't have characteristics, so this returns 0 for them. ++func (f *File) GetCharacteristics() uint16 { ++ if f.BigObjHeader != nil { ++ return 0 ++ } ++ return f.FileHeader.Characteristics ++} ++ + // Open opens the named file using [os.Open] and prepares it for use as a PE binary. + func Open(name string) (*File, error) { + f, err := os.Open(name) +@@ -68,6 +135,69 @@ func (f *File) Close() error { + + // TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance) + ++// isBigObjFormat detects if the reader contains a bigobj COFF file by checking ++// the signature and GUID. The reader position should be at the start of the COFF header. ++func isBigObjFormat(r io.ReadSeeker) (bool, error) { ++ currentPos, err := r.Seek(0, io.SeekCurrent) ++ if err != nil { ++ return false, err ++ } ++ defer r.Seek(currentPos, io.SeekStart) ++ ++ // Read the first part of what could be a BigObjHeader ++ var sig struct { ++ Sig1 uint16 ++ Sig2 uint16 ++ Version uint16 ++ Machine uint16 ++ TimeDateStamp uint32 ++ ClassID [16]uint8 ++ } ++ ++ err = binary.Read(r, binary.LittleEndian, &sig) ++ if err != nil { ++ return false, err ++ } ++ ++ if sig.Sig1 != BigObjSig1 || sig.Sig2 != BigObjSig2 { ++ return false, nil ++ } ++ ++ if sig.ClassID != BigObjClassID { ++ return false, nil ++ } ++ ++ return true, nil ++} ++ ++// readCOFFHeader reads the appropriate COFF header type ("regular" or bigobj). ++// The unused header type will be nil ++func readCOFFHeader(sr *io.SectionReader, base int64) (*FileHeader, *BigObjHeader, error) { ++ _, err := sr.Seek(base, io.SeekStart) ++ if err != nil { ++ return nil, nil, err ++ } ++ ++ isBigObj, err := isBigObjFormat(sr) ++ if err != nil { ++ return nil, nil, err ++ } ++ ++ if isBigObj { ++ bigObjHeader := new(BigObjHeader) ++ if err := binary.Read(sr, binary.LittleEndian, bigObjHeader); err != nil { ++ return nil, nil, err ++ } ++ return nil, bigObjHeader, nil ++ } else { ++ fileHeader := new(FileHeader) ++ if err := binary.Read(sr, binary.LittleEndian, fileHeader); err != nil { ++ return nil, nil, err ++ } ++ return fileHeader, nil, nil ++ } ++} ++ + // NewFile creates a new [File] for accessing a PE binary in an underlying reader. + func NewFile(r io.ReaderAt) (*File, error) { + f := new(File) +@@ -89,11 +219,24 @@ func NewFile(r io.ReaderAt) (*File, error) { + } else { + base = int64(0) + } +- sr.Seek(base, io.SeekStart) +- if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil { ++ // Read appropriate header type - unused header will be nil ++ fileHeader, bigObjHeader, err := readCOFFHeader(sr, base) ++ if err != nil { + return nil, err + } +- switch f.FileHeader.Machine { ++ f.FileHeader = fileHeader ++ f.BigObjHeader = bigObjHeader ++ ++ // Calculate header size based on actual type ++ var headerSize int ++ if f.BigObjHeader != nil { ++ headerSize = binary.Size(*f.BigObjHeader) ++ } else { ++ headerSize = binary.Size(*f.FileHeader) ++ } ++ ++ // Validate machine type ++ switch f.GetMachine() { + case IMAGE_FILE_MACHINE_AMD64, + IMAGE_FILE_MACHINE_ARM64, + IMAGE_FILE_MACHINE_ARMNT, +@@ -104,19 +247,17 @@ func NewFile(r io.ReaderAt) (*File, error) { + IMAGE_FILE_MACHINE_UNKNOWN: + // ok + default: +- return nil, fmt.Errorf("unrecognized PE machine: %#x", f.FileHeader.Machine) ++ return nil, fmt.Errorf("unrecognized PE machine: %#x", f.GetMachine()) + } + +- var err error +- + // Read string table. +- f.StringTable, err = readStringTable(&f.FileHeader, sr) ++ f.StringTable, err = readStringTableFromFile(f, sr) + if err != nil { + return nil, err + } + + // Read symbol table. +- f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr) ++ f.COFFSymbols, err = readCOFFSymbols(f, sr) + if err != nil { + return nil, err + } +@@ -126,20 +267,23 @@ func NewFile(r io.ReaderAt) (*File, error) { + } + + // Seek past file header. +- _, err = sr.Seek(base+int64(binary.Size(f.FileHeader)), io.SeekStart) ++ _, err = sr.Seek(base+int64(headerSize), io.SeekStart) + if err != nil { + return nil, err + } + +- // Read optional header. +- f.OptionalHeader, err = readOptionalHeader(sr, f.FileHeader.SizeOfOptionalHeader) +- if err != nil { +- return nil, err ++ // Read optional header (only for regular COFF files). ++ if !f.IsBigObj() { ++ f.OptionalHeader, err = readOptionalHeader(sr, f.GetSizeOfOptionalHeader()) ++ if err != nil { ++ return nil, err ++ } + } + + // Process sections. +- f.Sections = make([]*Section, f.FileHeader.NumberOfSections) +- for i := 0; i < int(f.FileHeader.NumberOfSections); i++ { ++ numSections := f.GetNumberOfSections() ++ f.Sections = make([]*Section, numSections) ++ for i := uint32(0); i < numSections; i++ { + sh := new(SectionHeader32) + if err := binary.Read(sr, binary.LittleEndian, sh); err != nil { + return nil, err +diff --git a/src/debug/pe/pe.go b/src/debug/pe/pe.go +index 51001bd2b3b6be..987685febea4b0 100644 +--- a/src/debug/pe/pe.go ++++ b/src/debug/pe/pe.go +@@ -14,6 +14,37 @@ type FileHeader struct { + Characteristics uint16 + } + ++// BigObjHeader represents the ANON_OBJECT_HEADER_BIGOBJ structure ++// used in bigobj COFF format. This format allows for more than 65535 sections. ++type BigObjHeader struct { ++ Sig1 uint16 // Must be 0x0 ++ Sig2 uint16 // Must be 0xFFFF ++ Version uint16 // Currently 2 ++ Machine uint16 ++ TimeDateStamp uint32 ++ ClassID [16]uint8 // GUID that identifies this as bigobj format ++ SizeOfData uint32 ++ Flags uint32 ++ MetaDataSize uint32 ++ MetaDataOffset uint32 ++ NumberOfSections uint32 // 32-bit field (vs 16-bit in regular COFF) ++ PointerToSymbolTable uint32 ++ NumberOfSymbols uint32 ++} ++ ++// BigObj signature constants ++const ( ++ BigObjSig1 = 0x0 ++ BigObjSig2 = 0xFFFF ++ BigObjVersion = 2 ++) ++ ++// The GUID that identifies a file as bigobj format ++var BigObjClassID = [16]uint8{ ++ 0xC7, 0xA1, 0xBA, 0xD1, 0xEE, 0xBA, 0xA9, 0x4B, ++ 0xAF, 0x20, 0xFA, 0xF6, 0x6A, 0xA4, 0xDC, 0xB8, ++} ++ + type DataDirectory struct { + VirtualAddress uint32 + Size uint32 +diff --git a/src/debug/pe/string.go b/src/debug/pe/string.go +index 6cd08aed7152e7..bcdf40e663144f 100644 +--- a/src/debug/pe/string.go ++++ b/src/debug/pe/string.go +@@ -25,12 +25,19 @@ func cstring(b []byte) string { + // StringTable is a COFF string table. + type StringTable []byte + +-func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) { ++// readStringTableFromFile reads string table using the File struct ++func readStringTableFromFile(f *File, r io.ReadSeeker) (StringTable, error) { + // COFF string table is located right after COFF symbol table. +- if fh.PointerToSymbolTable <= 0 { ++ if f.GetPointerToSymbolTable() <= 0 { + return nil, nil + } +- offset := fh.PointerToSymbolTable + COFFSymbolSize*fh.NumberOfSymbols ++ ++ var symbolSize uint32 = COFFSymbolSize ++ if f.IsBigObj() { ++ symbolSize = BigObjSymbolSize ++ } ++ ++ offset := f.GetPointerToSymbolTable() + symbolSize*f.GetNumberOfSymbols() + _, err := r.Seek(int64(offset), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to string table: %v", err) +diff --git a/src/debug/pe/symbol.go b/src/debug/pe/symbol.go +index 80acebe9f1f40b..614a6556662c54 100644 +--- a/src/debug/pe/symbol.go ++++ b/src/debug/pe/symbol.go +@@ -14,23 +14,66 @@ import ( + ) + + const COFFSymbolSize = 18 ++const BigObjSymbolSize = 20 + + // COFFSymbol represents single COFF symbol table record. ++// NOTE: This is actually the format of a bigobj COFF symbol. ++// The only difference between a bigobj symbol and regular symbol ++// is that the SectionNumber is 32-bits. + type COFFSymbol struct { + Name [8]uint8 + Value uint32 +- SectionNumber int16 ++ SectionNumber int32 // bigobj format (this field is 32 bits, rather than 16) + Type uint16 + StorageClass uint8 + NumberOfAuxSymbols uint8 + } + ++// rawCOFFSymbol represents a COFF symbol as stored in a regular COFF file ++type rawCOFFSymbol struct { ++ Name [8]uint8 ++ Value uint32 ++ SectionNumber int16 // 16-bit in regular COFF ++ Type uint16 ++ StorageClass uint8 ++ NumberOfAuxSymbols uint8 ++} ++ ++// readBigObjSymbol reads a single symbol from bigobj COFF format (20 bytes) ++func readBigObjSymbol(r io.ReadSeeker) (COFFSymbol, error) { ++ var sym COFFSymbol ++ err := binary.Read(r, binary.LittleEndian, &sym) ++ return sym, err ++} ++ ++// readRegularSymbol reads a single symbol from regular COFF format (18 bytes) and converts to COFFSymbol ++func readRegularSymbol(r io.ReadSeeker) (COFFSymbol, error) { ++ var rawSym rawCOFFSymbol ++ err := binary.Read(r, binary.LittleEndian, &rawSym) ++ if err != nil { ++ return COFFSymbol{}, err ++ } ++ ++ sym := COFFSymbol{ ++ Name: rawSym.Name, ++ Value: rawSym.Value, ++ SectionNumber: int32(rawSym.SectionNumber), // Extend to 32 bits ++ Type: rawSym.Type, ++ StorageClass: rawSym.StorageClass, ++ NumberOfAuxSymbols: rawSym.NumberOfAuxSymbols, ++ } ++ return sym, nil ++} ++ + // readCOFFSymbols reads in the symbol table for a PE file, returning +-// a slice of COFFSymbol objects. The PE format includes both primary +-// symbols (whose fields are described by COFFSymbol above) and +-// auxiliary symbols; all symbols are 18 bytes in size. The auxiliary +-// symbols for a given primary symbol are placed following it in the +-// array, e.g. ++// a slice of COFFSymbol objects containing both primary and auxiliary symbols. ++// In a regular COFF file, each symbol is 18 bytes, with a 16-bit SectionNumber field. ++// In a bigobj COFF file, each symbol is 20 bytes, with a 32-bit SectionNumber field. ++// ++// The COFF symbol table contains both primary symbols and auxiliary symbols. ++// Auxiliary symbols immediately follow their associated primary symbol in both ++// the binary data and the returned slice. ++// In the binary format, symbols are arranged like this: + // + // ... + // k+0: regular sym k +@@ -48,44 +91,45 @@ type COFFSymbol struct { + // + // At the moment this package only provides APIs for looking at + // aux symbols of format 5 (associated with section definition symbols). +-func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) { +- if fh.PointerToSymbolTable == 0 { ++func readCOFFSymbols(f *File, r io.ReadSeeker) ([]COFFSymbol, error) { ++ if f.GetPointerToSymbolTable() == 0 { + return nil, nil + } +- if fh.NumberOfSymbols <= 0 { ++ if f.GetNumberOfSymbols() <= 0 { + return nil, nil + } +- _, err := r.Seek(int64(fh.PointerToSymbolTable), io.SeekStart) ++ _, err := r.Seek(int64(f.GetPointerToSymbolTable()), io.SeekStart) + if err != nil { + return nil, fmt.Errorf("fail to seek to symbol table: %v", err) + } +- c := saferio.SliceCap[COFFSymbol](uint64(fh.NumberOfSymbols)) ++ c := saferio.SliceCap[COFFSymbol](uint64(f.GetNumberOfSymbols())) + if c < 0 { + return nil, errors.New("too many symbols; file may be corrupt") + } + syms := make([]COFFSymbol, 0, c) + naux := 0 +- for k := uint32(0); k < fh.NumberOfSymbols; k++ { ++ ++ isBigObj := f.IsBigObj() ++ ++ for k := uint32(0); k < f.GetNumberOfSymbols(); k++ { + var sym COFFSymbol ++ ++ if isBigObj { ++ sym, err = readBigObjSymbol(r) ++ } else { ++ sym, err = readRegularSymbol(r) ++ } ++ if err != nil { ++ return nil, fmt.Errorf("fail to read symbol table: %v", err) ++ } ++ + if naux == 0 { +- // Read a primary symbol. +- err = binary.Read(r, binary.LittleEndian, &sym) +- if err != nil { +- return nil, fmt.Errorf("fail to read symbol table: %v", err) +- } +- // Record how many auxiliary symbols it has. ++ // This is a primary symbol ++ // Record how many auxilliary symbols it has + naux = int(sym.NumberOfAuxSymbols) + } else { +- // Read an aux symbol. At the moment we assume all +- // aux symbols are format 5 (obviously this doesn't always +- // hold; more cases will be needed below if more aux formats +- // are supported in the future). ++ // This is an auxilliary symbol + naux-- +- aux := (*COFFSymbolAuxFormat5)(unsafe.Pointer(&sym)) +- err = binary.Read(r, binary.LittleEndian, aux) +- if err != nil { +- return nil, fmt.Errorf("fail to read symbol table: %v", err) +- } + } + syms = append(syms, sym) + } +@@ -151,7 +195,7 @@ func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) { + type Symbol struct { + Name string + Value uint32 +- SectionNumber int16 ++ SectionNumber int32 + Type uint16 + StorageClass uint8 + }