From d7f23b7a3e8e259ae7dd0bec9a94eec66df6257d Mon Sep 17 00:00:00 2001 From: kamran ul haq Date: Tue, 17 Feb 2026 20:14:48 +0500 Subject: [PATCH] Add -strings flag to extract Go strings from binaries (#77) * Add -strings flag and ExtractMetadata field - Add Strings []string to ExtractMetadata struct - Add -strings command-line flag for string extraction - Update main_impl and main_impl_tmpfile signatures to accept printStrings parameter - Add placeholder string extraction logic with TODO marker - Update printForHuman to display extracted strings section - Verified flag appears in help and outputs correctly in both JSON and human format Part of #45 * Implement Go string extraction algorithm - Create objfile/strings.go with core extraction logic - Implement FLOSS-based string internment table detection - Add string candidate scanning (pointer + length pairs) - Implement findLongestMonotonicRun() for pattern detection - Add UTF-8 validation and printability filtering - Minimum string length: 4 characters, 80% printable - Add helper methods to elfFile: getSections(), is64Bit(), isLittleEndian() - Update main.go to call file.ExtractStrings() instead of placeholder - Tested with testproject/testproject: extracts 512 strings successfully - Extracts real Go strings: type names, runtime symbols, error messages Based on FLOSS floss/language/go/extract.py algorithm Part of #45 * Update README with -strings flag documentation - Add -strings flag to available flags list - Add Strings field to example JSON output - Document purpose: extract embedded Go strings from binary Part of #45 * Add tests validated against FLOSS output Per maintainer request, added comprehensive test suite: - strings_floss_test.go: Validates GoReSym against FLOSS reference output * 99.2% match rate (648/653 strings match FLOSS) * Uses FLOSS output from testproject.exe as ground truth * Reference saved in testdata/floss_reference.txt - strings_test.go: Additional unit tests for: * ELF and PE binary string extraction * Monotonic run detection algorithm * String filtering (printability, minimum length) - pe.go: Added helper methods (getSections, is64Bit, isLittleEndian) to enable string extraction from PE binaries All tests pass. * Address PR #77 review comments - Convert getSections() to iterateSections() using callback pattern to avoid memory pressure - Add Strings field to GoReSym.proto for external parsers - Implement iterateSections() for Mach-O format (previously missing) Changes requested by @stevemk14ebr in review: 1. Memory optimization: Replace array-based section loading with generator pattern 2. Proto definition: Add 'repeated string strings = 13' field 3. Mach-O support: Add missing iterateSections() implementation * Fix test failures and expand string extraction testing per PR #77 feedback - Fixed 4 test compilation errors by adding missing printStrings parameter to main_impl() calls - Added comprehensive TestStringExtraction function with 7 test cases covering Linux/macOS/Windows binaries - Implemented isPrintable() helper for ASCII validation (range 32-126) * Align string extraction with FLOSS algorithm per PR #77 review Rewrite string extraction to match FLOSS (extract.py) logic: - Sort candidates by address (not length) to fix monotonic run detection - Add image VA range and max section size filtering for candidates - Use candidate (pointer, length) pairs for direct extraction from blob - Replace 80% printable threshold with 100% fully printable check - Fix PE section addresses to include ImageBase for correct VA comparison Results: 648 strings extracted from PE test binary, 100% match with FLOSS. * Run string extraction on test build files * Extend main test to cover all new versions * Fix string extraction for old Windows binaries and add length sanity check 1. Add .text to data sections for old Go Windows binaries (1.7-1.10) that store strings in the code section instead of .rdata 2. Add maxReasonableStringLength (64KB) to filter out garbage candidates with huge lengths that cause incorrect blob boundary detection 3. Update TestIsDataSection to reflect .text being a valid data section Fixes string extraction failures on: - Old Windows PE binaries (Go 1.7-1.10): 0 -> 256 strings - Modern Windows: 574 strings - macOS: 284 strings All tests pass including TestWeirdBins on 15 real binaries. * Fix test failures for Go 1.5/1.6 and Go 1.22 macOS; fix build script typo 1. main_test.go: Skip Strings check for Go 1.5/1.6 Pre-SSA C-based linker does not produce length-sorted string blobs, so findLongestMonotonicRun never reaches the minimum threshold of 10. Pattern mirrors the existing interface-parsing guard. 2. objfile/strings.go: Handle prevNull == -1 in findStringBlobRange Apple's linker on newer Go/macOS (1.22+) packs sections without leading padding, so no null bytes exist before the first string candidate. bytes.LastIndex returns -1 in that case; treat it as offset 0 instead of bailing out with nil. 3. objfile/macho.go: Add missing is64Bit/isLittleEndian for machoFile elfFile and peFile already implement these interface methods; machoFile was the only rawFile implementation without them. Detect CPU type (CpuAmd64/Arm64 = 64-bit) and byte order from the macho.File struct. 4. build_test_files.sh: Fix $ver typo -> $GO_VER on mkdir line The directory was never created before Docker ran, causing Go 1.5 builds to produce no output. Now all 12 binaries are built for every version including 1.5 and 1.6. --------- Co-authored-by: Stephen Eckels --- GoReSym.proto | 1 + README.md | 2 + build_test_files.sh | 2 +- main.go | 27 +- main_test.go | 115 ++- objfile/elf.go | 31 + objfile/macho.go | 39 + objfile/pe.go | 47 + objfile/strings.go | 512 ++++++++++ objfile/strings_floss_test.go | 91 ++ objfile/strings_test.go | 344 +++++++ testdata/floss_reference.txt | 1750 +++++++++++++++++++++++++++++++++ 12 files changed, 2951 insertions(+), 10 deletions(-) create mode 100644 objfile/strings.go create mode 100644 objfile/strings_floss_test.go create mode 100644 objfile/strings_test.go create mode 100644 testdata/floss_reference.txt diff --git a/GoReSym.proto b/GoReSym.proto index bf773ca..d1f96ea 100644 --- a/GoReSym.proto +++ b/GoReSym.proto @@ -74,4 +74,5 @@ message ExtractMetadata { repeated string files = 10 [json_name="Files"]; repeated FuncMetadata userFunctions = 11 [json_name="UserFunctions"]; repeated FuncMetadata stdFunctions = 12 [json_name="StdFunctions"]; + repeated string strings = 13 [json_name="Strings"]; } diff --git a/README.md b/README.md index ad1b793..ae464cd 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ In this example, we ask GoReSym to recover type names (`-t`), user package names }, "Types": [ ... ], "Files": [ ... ], + "Strings": [ ... ], "UserFunctions": [ ... ], "StdFunctions": [ ... ] } @@ -73,6 +74,7 @@ Here are all the available flags: * `-d` ("default", optional) flag will print standard Go packages in addition to user packages. * `-p` ("paths", optional) flag will print any file paths embedded in the `pclntab`. * `-t` ("types", optional) flag will print Go type names. +* `-strings` (optional) flag will extract embedded Go strings from the binary by analyzing the string internment table. * `-m ` ("manual", optional) flag will dump the `RTYPE` structure recursively at the given virtual address * `-v ` ("version", optional) flag will override automated version detection and use the provided version. This is needed for some stripped binaries. Type parsing will fail if the version is not accurate. * `-human` (optional) flag will print a flat text listing instead of JSON. Especially useful when printing structure and interface types. diff --git a/build_test_files.sh b/build_test_files.sh index 440894a..22bb680 100755 --- a/build_test_files.sh +++ b/build_test_files.sh @@ -6,7 +6,7 @@ for v in "${versions[@]}" do GO_TAG=$v GO_VER=$(echo "$GO_TAG" | tr -d '.') - mkdir -p $(pwd)/test/build/"$ver"/ + mkdir -p $(pwd)/test/build/"$GO_VER"/ rm -f Dockerfile.test cat <Dockerfile.test diff --git a/main.go b/main.go index 8b3db5c..e652c1f 100644 --- a/main.go +++ b/main.go @@ -64,9 +64,10 @@ type ExtractMetadata struct { Files []string UserFunctions []FuncMetadata StdFunctions []FuncMetadata + Strings []string } -func main_impl_tmpfile(fileBytes []byte, printStdPkgs bool, printFilePaths bool, printTypes bool, noPrintFunctions bool, manualTypeAddress int, versionOverride string) (metadata ExtractMetadata, err error) { +func main_impl_tmpfile(fileBytes []byte, printStdPkgs bool, printFilePaths bool, printTypes bool, noPrintFunctions bool, manualTypeAddress int, versionOverride string, printStrings bool) (metadata ExtractMetadata, err error) { tmpFile, err := os.CreateTemp(os.TempDir(), "goresym_tmp-") if err != nil { return ExtractMetadata{}, fmt.Errorf("failed to create temporary file: %s", err) @@ -81,10 +82,10 @@ func main_impl_tmpfile(fileBytes []byte, printStdPkgs bool, printFilePaths bool, return ExtractMetadata{}, fmt.Errorf("failed to close temporary file: %s", err) } - return main_impl(tmpFile.Name(), printStdPkgs, printFilePaths, printTypes, noPrintFunctions, manualTypeAddress, versionOverride) + return main_impl(tmpFile.Name(), printStdPkgs, printFilePaths, printTypes, noPrintFunctions, manualTypeAddress, versionOverride, printStrings) } -func main_impl(fileName string, printStdPkgs bool, printFilePaths bool, printTypes bool, noPrintFunctions bool, manualTypeAddress int, versionOverride string) (metadata ExtractMetadata, err error) { +func main_impl(fileName string, printStdPkgs bool, printFilePaths bool, printTypes bool, noPrintFunctions bool, manualTypeAddress int, versionOverride string, printStrings bool) (metadata ExtractMetadata, err error) { extractMetadata := ExtractMetadata{} file, err := objfile.Open(fileName) @@ -287,6 +288,14 @@ restartParseWithRealTextBase: } } + if printStrings { + strings, err := file.ExtractStrings() + if err == nil { + extractMetadata.Strings = strings + } + // If error, Strings will remain empty (nil slice) + } + if !noPrintFunctions { for _, elem := range finalTab.ParsedPclntab.Funcs { if isStdPackage(elem.PackageName()) { @@ -376,6 +385,15 @@ func printForHuman(metadata ExtractMetadata) { fmt.Println("") } + fmt.Println("\n-Strings-") + if len(metadata.Strings) > 0 { + for _, str := range metadata.Strings { + fmt.Println(str) + } + } else { + fmt.Println("") + } + fmt.Println("\n-User Functions-") if len(metadata.UserFunctions) > 0 { for i, fn := range metadata.UserFunctions { @@ -431,6 +449,7 @@ func main() { typeAddress := flag.Int("m", 0, "Manually parse the RTYPE at the provided virtual address, disables automated enumeration of moduledata typelinks itablinks") versionOverride := flag.String("v", "", "Override the automated version detection, ex: 1.17. If this is wrong, parsing may fail or produce nonsense") humanView := flag.Bool("human", false, "Human view, print information flat rather than json, some information is omitted for clarity") + printStrings := flag.Bool("strings", false, "Extract embedded Go strings from binary") flag.Parse() if *about { @@ -449,7 +468,7 @@ func main() { os.Exit(1) } - metadata, err := main_impl(flag.Arg(0), *printStdPkgs, *printFilePaths, *printTypes, *noPrintFunctions, *typeAddress, *versionOverride) + metadata, err := main_impl(flag.Arg(0), *printStdPkgs, *printFilePaths, *printTypes, *noPrintFunctions, *typeAddress, *versionOverride, *printStrings) if err != nil { fmt.Println(TextToJson("error", fmt.Sprintf("Failed to parse file: %s", err))) os.Exit(1) diff --git a/main_test.go b/main_test.go index cd0ade2..d397540 100644 --- a/main_test.go +++ b/main_test.go @@ -11,7 +11,7 @@ import ( _ "net/http/pprof" ) -var versions = []string{"117", "116", "115", "114", "113", "112", "111", "110", "19", "18", "17", "16", "15"} +var versions = []string{"122", "121", "120", "119", "118", "117", "116", "115", "114", "113", "112", "111", "110", "19", "18", "17", "16", "15"} var fileNames = []string{"testproject_lin", "testproject_lin_32", "testproject_lin_stripped", "testproject_lin_stripped_32", "testproject_mac", "testproject_mac_stripped", "testproject_win_32.exe", "testproject_win_stripped_32.exe", "testproject_win_stripped.exe", "testproject_win.exe"} func TestAllVersions(t *testing.T) { @@ -30,7 +30,7 @@ func TestAllVersions(t *testing.T) { } t.Run(versionPath, func(t *testing.T) { - data, err := main_impl(filePath, true, true, true, false, 0, "") + data, err := main_impl(filePath, true, true, true, false, 0, "", true) if err != nil { t.Errorf("Go %s failed on %s: %s", v, file, err) } @@ -107,6 +107,14 @@ func TestAllVersions(t *testing.T) { if data.Arch == "" { t.Errorf("Go %s Arch failed on %s: %s", v, file, err) } + + // String extraction requires Go 1.7+ (SSA-based linker). + // Old Go 1.5/1.6 used C linker which doesn't produce sorted string blobs. + if v != "15" && v != "16" { + if len(data.Strings) == 0 { + t.Errorf("Go %s Strings failed on %s: %s", v, file, err) + } + } }) } } @@ -119,7 +127,7 @@ func testSymbolRecovery(t *testing.T, workingDirectory string, binaryName string return } - data, err := main_impl(filePath, true, true, true, false, 0, "") + data, err := main_impl(filePath, true, true, true, false, 0, "", false) if err != nil { t.Errorf("GoReSym failed: %s", err) } @@ -214,7 +222,7 @@ func TestWeirdBins(t *testing.T) { return } - _, err := main_impl(filePath, true, true, true, false, 0, "") + _, err := main_impl(filePath, true, true, true, false, 0, "", false) if err == nil { t.Errorf("GoReSym found pclntab in a non-go binary, this is not possible.") } @@ -228,7 +236,7 @@ func TestWeirdBins(t *testing.T) { return } - _, err := main_impl(filePath, true, true, true, false, 0, "") + _, err := main_impl(filePath, true, true, true, false, 0, "", false) if err == nil { t.Errorf("GoReSym found pclntab in a non-go binary, this is not possible.") } @@ -245,3 +253,100 @@ func TestBig(t *testing.T) { testSymbolRecovery(t, workingDirectory, "kubectl_macho", 0x6C6CB20, 0x7F8CB20, 0x5CD9E40) }) } + +func isPrintable(s string) bool { + if len(s) == 0 { + return false + } + for _, r := range s { + if r < 32 || r > 126 { + return false + } + } + return true +} + +func TestStringExtraction(t *testing.T) { + workingDirectory, err := os.Getwd() + if err != nil { + t.Errorf("Failed to get working directory") + return + } + + // Test string extraction on multiple test binaries + testCases := []struct { + version string + filename string + minStrings int + description string + }{ + {"117", "testproject_lin", 100, "Go 1.17 Linux binary"}, + {"117", "testproject_lin_stripped", 100, "Go 1.17 Linux stripped binary"}, + {"117", "testproject_mac", 100, "Go 1.17 macOS binary"}, + {"117", "testproject_win.exe", 100, "Go 1.17 Windows binary"}, + {"116", "testproject_lin", 100, "Go 1.16 Linux binary"}, + {"115", "testproject_lin_32", 50, "Go 1.15 Linux 32-bit binary"}, + {"18", "testproject_mac_stripped", 100, "Go 1.8 macOS stripped binary"}, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("%s_%s", tc.version, tc.filename), func(t *testing.T) { + filePath := fmt.Sprintf("%s/test/build/%s/%s", workingDirectory, tc.version, tc.filename) + if _, err := os.Stat(filePath); errors.Is(err, os.ErrNotExist) { + t.Skipf("Test file %s doesn't exist", filePath) + return + } + + // Extract with strings enabled + data, err := main_impl(filePath, false, false, false, true, 0, "", true) + if err != nil { + t.Errorf("Failed to extract from %s: %v", tc.description, err) + return + } + + // Check that strings were extracted + if len(data.Strings) == 0 { + t.Errorf("No strings extracted from %s", tc.description) + return + } + + // Check minimum number of strings + if len(data.Strings) < tc.minStrings { + t.Errorf("Expected at least %d strings from %s, got %d", tc.minStrings, tc.description, len(data.Strings)) + } + + // Check that all extracted strings are printable + nonPrintableCount := 0 + for _, str := range data.Strings { + if !isPrintable(str) { + nonPrintableCount++ + } + } + + // Allow up to 5% non-printable strings (some edge cases may exist) + maxNonPrintable := len(data.Strings) / 20 + if nonPrintableCount > maxNonPrintable { + t.Errorf("Too many non-printable strings in %s: %d out of %d (max allowed: %d)", + tc.description, nonPrintableCount, len(data.Strings), maxNonPrintable) + } + + // Check for some expected common Go strings + expectedSubstrings := []string{"main", "go", "runtime"} + foundCount := 0 + for _, expected := range expectedSubstrings { + for _, str := range data.Strings { + if strings.Contains(str, expected) { + foundCount++ + break + } + } + } + + if foundCount == 0 { + t.Errorf("No common Go strings found in %s (expected at least one of: %v)", tc.description, expectedSubstrings) + } + + t.Logf("%s: extracted %d strings (%d printable)", tc.description, len(data.Strings), len(data.Strings)-nonPrintableCount) + }) + } +} diff --git a/objfile/elf.go b/objfile/elf.go index 47b4d3b..85df600 100644 --- a/objfile/elf.go +++ b/objfile/elf.go @@ -425,3 +425,34 @@ func (f *elfFile) loadAddress() (uint64, error) { func (f *elfFile) dwarf() (*dwarf.Data, error) { return f.elf.DWARF() } + +// iterateSections calls the provided function for each section. +// This avoids loading all section data into memory at once. +func (f *elfFile) iterateSections(fn func(Section) error) error { + for _, sec := range f.elf.Sections { + data, err := sec.Data() + if err != nil { + // Skip sections we can't read + continue + } + section := Section{ + Name: sec.Name, + Addr: sec.Addr, + Data: data, + } + if err := fn(section); err != nil { + return err + } + } + return nil +} + +// is64Bit returns true if this is a 64-bit ELF file +func (f *elfFile) is64Bit() bool { + return f.elf.Class == elf.ELFCLASS64 +} + +// isLittleEndian returns true if this is a little-endian ELF file +func (f *elfFile) isLittleEndian() bool { + return f.elf.Data == elf.ELFDATA2LSB +} diff --git a/objfile/macho.go b/objfile/macho.go index b236c8d..2a645b1 100644 --- a/objfile/macho.go +++ b/objfile/macho.go @@ -419,3 +419,42 @@ func (f *machoFile) loadAddress() (uint64, error) { func (f *machoFile) dwarf() (*dwarf.Data, error) { return f.macho.DWARF() } + +// iterateSections calls the provided function for each section. +// This avoids loading all section data into memory at once. +func (f *machoFile) iterateSections(fn func(Section) error) error { + for _, sec := range f.macho.Sections { + data, err := sec.Data() + if err != nil { + // Skip sections we can't read + continue + } + section := Section{ + Name: sec.Name, + Addr: sec.Addr, + Data: data, + } + if err := fn(section); err != nil { + return err + } + } + return nil +} + +// is64Bit returns true if this is a 64-bit Mach-O binary. +// Go's debug/macho package represents CPU type directly on the File struct. +func (f *machoFile) is64Bit() bool { + switch f.macho.Cpu { + case macho.CpuAmd64, macho.CpuArm64: + return true + default: + return false // CpuI386, CpuArm, CpuPpc = 32-bit + } +} + +// isLittleEndian returns true if this Mach-O binary is little-endian. +// Modern Macs (x86-64, ARM64) are little-endian. +// Old PowerPC Macs were big-endian. +func (f *machoFile) isLittleEndian() bool { + return f.macho.ByteOrder == binary.LittleEndian +} diff --git a/objfile/pe.go b/objfile/pe.go index b10c068..2f7dc7e 100644 --- a/objfile/pe.go +++ b/objfile/pe.go @@ -521,3 +521,50 @@ func (f *peFile) loadAddress() (uint64, error) { func (f *peFile) dwarf() (*dwarf.Data, error) { return f.pe.DWARF() } + +// iterateSections calls the provided function for each section. +// This avoids loading all section data into memory at once. +// Section addresses are full VAs (ImageBase + VirtualAddress) so that +// pointer values found in the binary data can be compared directly. +func (f *peFile) iterateSections(fn func(Section) error) error { + var imageBase uint64 + switch oh := f.pe.OptionalHeader.(type) { + case *pe.OptionalHeader32: + imageBase = uint64(oh.ImageBase) + case *pe.OptionalHeader64: + imageBase = oh.ImageBase + } + + for _, sec := range f.pe.Sections { + data, err := sec.Data() + if err != nil { + // Skip sections we can't read + continue + } + section := Section{ + Name: sec.Name, + Addr: imageBase + uint64(sec.VirtualAddress), + Data: data, + } + if err := fn(section); err != nil { + return err + } + } + return nil +} + +// is64Bit returns true if this is a 64-bit PE file +func (f *peFile) is64Bit() bool { + switch f.pe.OptionalHeader.(type) { + case *pe.OptionalHeader64: + return true + default: + return false + } +} + +// isLittleEndian returns true if this is a little-endian PE file +// PE files are always little-endian on x86/x64/ARM architectures +func (f *peFile) isLittleEndian() bool { + return true +} diff --git a/objfile/strings.go b/objfile/strings.go new file mode 100644 index 0000000..2591927 --- /dev/null +++ b/objfile/strings.go @@ -0,0 +1,512 @@ +/*Copyright (C) 2022 Mandiant, Inc. All Rights Reserved.*/ + +// String extraction for Go binaries +// Algorithm aligned with FLOSS (floss/language/go/extract.py) +// +// FLOSS reference: https://github.com/mandiant/flare-floss +// Specifically: floss/language/go/extract.py +// +// The Go compiler stores strings in a "string blob" within data sections, +// sorted by length (shortest to longest). Each string is referenced by a +// struct { pointer, length } pair elsewhere in the binary. +// +// This algorithm: +// 1. Scans data sections for candidate string structures (pointer + length pairs) +// 2. Sorts candidates by pointer address (where the string data lives) +// 3. Finds the longest monotonically increasing run of lengths, which +// identifies candidates pointing into the string blob +// 4. Locates the blob boundaries by searching for null terminator sequences +// 5. Walks the blob using sorted pointers to extract individual strings +// +// Stack strings are out of scope (acceptable difference from FLOSS). + +package objfile + +import ( + "bytes" + "encoding/binary" + "fmt" + "sort" + "unicode" + "unicode/utf8" +) + +const minStringLength = 4 +const maxReasonableStringLength = 65536 // 64KB - no real Go string should exceed this + +// StringCandidate represents a potential Go string structure found in the binary. +// Go strings are represented as a struct with a pointer and length: +// +// type string struct { +// str unsafe.Pointer +// len int +// } +type StringCandidate struct { + Pointer uint64 // VA where the actual string data lives + Length uint64 // Length of the string in bytes +} + +// ExtractStrings finds embedded Go strings in the binary by analyzing the +// string internment table. Returns deduplicated, validated strings. +func (f *File) ExtractStrings() ([]string, error) { + var allStrings []string + + for _, entry := range f.entries { + strings, err := entry.extractStrings() + if err != nil { + continue + } + allStrings = append(allStrings, strings...) + } + + return allStrings, nil +} + +// extractStrings performs string extraction for a single Entry. +// +// This is the main orchestration function, following the FLOSS algorithm: +// +// FLOSS: get_string_blob_strings() in extract.py:266 +func (e *Entry) extractStrings() ([]string, error) { + is64bit := e.is64Bit() + isLittleEndian := e.isLittleEndian() + + // --------------------------------------------------------------- + // Step 1a: Compute the binary's virtual address range + // --------------------------------------------------------------- + // FLOSS: low, high = get_image_range(pe) in utils.py:71 + // + // Candidates whose pointer falls outside the binary's address space + // are noise. We compute the range from ALL sections (not just data). + var imageMin, imageMax uint64 + var maxSectionSize uint64 + first := true + + err := e.iterateSections(func(section Section) error { + // Skip sections with no virtual address (e.g., ELF .shstrtab, + // .symtab, .strtab, debug sections, and the null section). + // These are metadata sections not mapped to virtual memory. + if section.Addr == 0 { + return nil + } + sectionEnd := section.Addr + uint64(len(section.Data)) + if first || section.Addr < imageMin { + imageMin = section.Addr + } + if first || sectionEnd > imageMax { + imageMax = sectionEnd + } + if uint64(len(section.Data)) > maxSectionSize { + maxSectionSize = uint64(len(section.Data)) + } + first = false + return nil + }) + if err != nil { + return nil, err + } + + // --------------------------------------------------------------- + // Step 1b: Collect string structure candidates from data sections + // --------------------------------------------------------------- + // We also keep section data for blob boundary detection later. + // Only data sections are kept (not code), so memory impact is limited. + var allCandidates []StringCandidate + var dataSections []Section + + err = e.iterateSections(func(section Section) error { + if !isDataSection(section.Name) { + return nil + } + dataSections = append(dataSections, section) + candidates := findStringCandidates(section.Data, section.Addr, is64bit, isLittleEndian, imageMin, imageMax, maxSectionSize) + allCandidates = append(allCandidates, candidates...) + return nil + }) + if err != nil { + return nil, err + } + + if len(allCandidates) == 0 { + return []string{}, nil + } + + // --------------------------------------------------------------- + // Step 2: Sort candidates by pointer address + // --------------------------------------------------------------- + // FLOSS: struct_strings.sort(key=lambda s: s.address) + // + // This is the KEY difference from the previous implementation which + // sorted by length. Sorting by address groups candidates that point + // into the same memory region (the string blob) together. + sort.Slice(allCandidates, func(i, j int) bool { + return allCandidates[i].Pointer < allCandidates[j].Pointer + }) + + // --------------------------------------------------------------- + // Step 3: Find longest monotonically increasing run of lengths + // --------------------------------------------------------------- + // FLOSS: find_longest_monotonically_increasing_run(lengths) + // + // Since Go stores string data sorted by length in the blob, candidates + // pointing into the blob will have monotonically increasing lengths + // when sorted by address. This run is typically hundreds/thousands of + // entries long, far longer than any random run. + runStart, runEnd := findLongestMonotonicRun(allCandidates) + if runStart == -1 || runEnd == -1 { + return []string{}, nil + } + + // --------------------------------------------------------------- + // Step 4: Find string blob boundaries + // --------------------------------------------------------------- + // FLOSS: find_string_blob_range() in extract.py:210 + // + // Pick the mid-point candidate (to avoid edge corruption), find the + // section containing its string data, then search for |00 00 00 00| + // null sequences before and after to delimit the blob. + blobStart, blobEnd, blobData := findStringBlobRange(allCandidates, runStart, runEnd, dataSections) + if blobData == nil { + return []string{}, nil + } + + // --------------------------------------------------------------- + // Step 5: Extract strings using candidate (pointer, length) pairs + // --------------------------------------------------------------- + // FLOSS walks consecutive pointer pairs, augmented with LEA xrefs + // (PE-specific, requires disassembly) for additional granularity. + // + // Since we operate cross-platform (ELF/PE/Mach-O), we instead use + // each candidate's own (pointer, length) to extract its exact string + // from the blob. This is more precise: each Go string struct already + // knows its exact length, so we don't need pointer-gap inference. + // + // The blob boundary still serves its purpose: only candidates whose + // pointer falls within the blob are considered (filtering noise). + seen := make(map[string]bool) + var result []string + + for _, c := range allCandidates { + // Only consider candidates pointing into the blob + if c.Pointer < blobStart || c.Pointer >= blobEnd { + continue + } + + offset := c.Pointer - blobStart + if offset+c.Length > uint64(len(blobData)) { + continue + } + + buf := blobData[offset : offset+c.Length] + + // FLOSS: sbuf.decode("utf-8") -- skip on UnicodeDecodeError + if !utf8.Valid(buf) { + continue + } + + s := string(buf) + + if len(s) < minStringLength { + continue + } + + // Maintainer requirement: 100% printable (not 80%) + if !isFullyPrintable(s) { + continue + } + + // Deduplicate: multiple struct string candidates may reference + // the same string (e.g., same string used in different packages) + if seen[s] { + continue + } + seen[s] = true + + result = append(result, s) + } + + return result, nil +} + +// iterateSections calls the provided function for each section in the binary. +// Uses a callback pattern to avoid memory pressure from loading all sections. +func (e *Entry) iterateSections(fn func(Section) error) error { + if sectioner, ok := e.raw.(interface { + iterateSections(func(Section) error) error + }); ok { + return sectioner.iterateSections(fn) + } + return fmt.Errorf("binary format does not support section enumeration") +} + +// Section represents a binary section with its virtual address and raw data. +type Section struct { + Name string + Addr uint64 + Data []byte +} + +// isDataSection returns true if the section name suggests it contains data +// (string structures or string blob data). +func isDataSection(name string) bool { + dataNames := []string{ + ".rodata", ".data", ".noptrdata", // ELF + "__rodata", "__data", "__noptrdata", // Mach-O + ".rdata", ".text", // PE (.text needed for old Go 1.7-1.10 Windows binaries) + } + for _, dataName := range dataNames { + if name == dataName || name == dataName+".__" { + return true + } + } + return false +} + +// findStringCandidates scans binary data for potential Go string structures. +// Each candidate is a (pointer, length) pair found at pointer-aligned offsets. +// +// Filtering (aligned with FLOSS utils.py:331): +// - Pointer must fall within the binary's VA range [imageMin, imageMax) +// - Length must be > 0 and <= maxSectionSize +// - Both pointer and length must be non-zero +// +// FLOSS equivalent: get_struct_string_candidates_with_pointer_size() in utils.py:331 +func findStringCandidates(data []byte, baseAddr uint64, is64bit bool, isLittleEndian bool, imageMin, imageMax, maxSectionSize uint64) []StringCandidate { + var candidates []StringCandidate + ptrSize := 4 + if is64bit { + ptrSize = 8 + } + + structSize := ptrSize * 2 + if len(data) < structSize { + return candidates + } + + for i := 0; i <= len(data)-structSize; i += ptrSize { + var ptr, length uint64 + + if is64bit { + if isLittleEndian { + ptr = binary.LittleEndian.Uint64(data[i : i+8]) + length = binary.LittleEndian.Uint64(data[i+8 : i+16]) + } else { + ptr = binary.BigEndian.Uint64(data[i : i+8]) + length = binary.BigEndian.Uint64(data[i+8 : i+16]) + } + } else { + if isLittleEndian { + ptr = uint64(binary.LittleEndian.Uint32(data[i : i+4])) + length = uint64(binary.LittleEndian.Uint32(data[i+4 : i+8])) + } else { + ptr = uint64(binary.BigEndian.Uint32(data[i : i+4])) + length = uint64(binary.BigEndian.Uint32(data[i+4 : i+8])) + } + } + + // FLOSS: skips address==0, length==0 + if ptr == 0 || length == 0 { + continue + } + + // FLOSS: if length > limit (max section size), skip + if length > maxSectionSize { + continue + } + + // Additional sanity check: reject unreasonably large strings + // Real Go strings rarely exceed 64KB; larger values are likely garbage + if length > maxReasonableStringLength { + continue + } + + // FLOSS: if not (low <= address < high), skip + // The pointer must point within the binary's virtual address space. + if ptr < imageMin || ptr >= imageMax { + continue + } + + candidates = append(candidates, StringCandidate{ + Pointer: ptr, + Length: length, + }) + } + + return candidates +} + +// findLongestMonotonicRun finds the longest consecutive subsequence where each +// candidate's Length is >= the previous candidate's Length. +// +// IMPORTANT: candidates must already be sorted by Pointer address before calling. +// The lengths form a monotonically increasing pattern for candidates pointing +// into the Go string blob (because Go stores strings sorted by length). +// +// FLOSS equivalent: find_longest_monotonically_increasing_run() in extract.py:143 +// +// Example with address-sorted candidates: +// +// Pointer: 0x4000 0x4005 0x400A 0x4011 0x5000 0x5002 +// Length: 5 5 7 4 2 1 +// ^^^^^^^^^^^^^^^^^^^ +// monotonic run (5, 5, 7) = the string blob candidates +// ^^^^^^^^^^^^^^^^^^^ +// non-blob (lengths decrease) +func findLongestMonotonicRun(candidates []StringCandidate) (start, end int) { + if len(candidates) == 0 { + return -1, -1 + } + + maxRunLength := 0 + maxRunEndIndex := 0 + + currentRunLength := 0 + var priorLength uint64 + + for i, c := range candidates { + if c.Length >= priorLength { + currentRunLength++ + } else { + currentRunLength = 1 + } + + if currentRunLength > maxRunLength { + maxRunLength = currentRunLength + maxRunEndIndex = i + } + + priorLength = c.Length + } + + maxRunStartIndex := maxRunEndIndex - maxRunLength + 1 + + // Real string tables have hundreds/thousands of entries + if maxRunLength < 10 { + return -1, -1 + } + + return maxRunStartIndex, maxRunEndIndex +} + +// findStringBlobRange locates the string blob boundaries in memory. +// +// Algorithm (from FLOSS extract.py:210): +// 1. Pick the mid-point candidate from the monotonic run (avoids edge junk) +// 2. Find the section containing that candidate's string data +// 3. Search forward for |00 00 00 00| to find blob end +// 4. Search backward for |00 00 00 00| to find blob start +// +// Returns the blob's VA range and raw data, or nil if not found. +func findStringBlobRange(candidates []StringCandidate, runStart, runEnd int, sections []Section) (blobStart, blobEnd uint64, blobData []byte) { + // Pick mid-point to avoid junk at the edges + // FLOSS: run_mid = (run_start + run_end) // 2 + runMid := (runStart + runEnd) / 2 + midCandidate := candidates[runMid] + + // Find the section containing this string's data + // FLOSS: section = pe.get_section_by_rva(instance_rva) + var section *Section + for i := range sections { + sectionEnd := sections[i].Addr + uint64(len(sections[i].Data)) + if midCandidate.Pointer >= sections[i].Addr && midCandidate.Pointer < sectionEnd { + section = §ions[i] + break + } + } + if section == nil { + return 0, 0, nil + } + + // Calculate offset within section + // FLOSS: instance_offset = instance_rva - section.VirtualAddress + instanceOffset := int(midCandidate.Pointer - section.Addr) + if instanceOffset < 0 || instanceOffset >= len(section.Data) { + return 0, 0, nil + } + + // Search for |00 00 00 00| boundaries + // FLOSS uses this larger needle because some binaries have embedded |00 00| + // See: https://github.com/Arker123/flare-floss/pull/3#issuecomment-1623354852 + nullNeedle := []byte{0x00, 0x00, 0x00, 0x00} + + // Search forward from the instance for the blob end + // FLOSS: next_null = section_data.find(b"\x00\x00\x00\x00", instance_offset) + nextNullRel := bytes.Index(section.Data[instanceOffset:], nullNeedle) + if nextNullRel == -1 { + return 0, 0, nil + } + nextNull := instanceOffset + nextNullRel + + // Search backward from the instance for the blob start + // FLOSS: prev_null = section_data.rfind(b"\x00\x00\x00\x00", 0, instance_offset) + prevNull := bytes.LastIndex(section.Data[:instanceOffset], nullNeedle) + if prevNull == -1 { + // String blob starts at the beginning of the section. + // This happens in newer Go/macOS (Go 1.22+) where Apple's linker + // packs sections without leading padding. + prevNull = 0 + } + + // Convert section-relative offsets to VAs + // FLOSS: blob_start, blob_end = (section_start + prev_null, section_start + next_null) + blobStart = section.Addr + uint64(prevNull) + blobEnd = section.Addr + uint64(nextNull) + blobData = section.Data[prevNull:nextNull] + + return blobStart, blobEnd, blobData +} + +// isFullyPrintable returns true if ALL characters in the string are printable. +// This replaces the previous isMostlyPrintable (80% threshold). +// +// Printable means: unicode.IsPrint(r) returns true, OR the character is a +// common whitespace character (tab, newline, carriage return). +// +// Maintainer requirement: "ensure all strings are fully printable" +func isFullyPrintable(s string) bool { + if len(s) == 0 { + return false + } + + for _, r := range s { + if !unicode.IsPrint(r) && r != '\t' && r != '\n' && r != '\r' { + return false + } + } + return true +} + +// deduplicateUint64 removes duplicate values from a sorted slice. +func deduplicateUint64(sorted []uint64) []uint64 { + if len(sorted) <= 1 { + return sorted + } + result := make([]uint64, 0, len(sorted)) + result = append(result, sorted[0]) + for i := 1; i < len(sorted); i++ { + if sorted[i] != sorted[i-1] { + result = append(result, sorted[i]) + } + } + return result +} + +// is64Bit determines if the binary is 64-bit +func (e *Entry) is64Bit() bool { + if bitChecker, ok := e.raw.(interface { + is64Bit() bool + }); ok { + return bitChecker.is64Bit() + } + return true +} + +// isLittleEndian determines if the binary is little-endian +func (e *Entry) isLittleEndian() bool { + if endianChecker, ok := e.raw.(interface { + isLittleEndian() bool + }); ok { + return endianChecker.isLittleEndian() + } + return true +} diff --git a/objfile/strings_floss_test.go b/objfile/strings_floss_test.go new file mode 100644 index 0000000..e28fe48 --- /dev/null +++ b/objfile/strings_floss_test.go @@ -0,0 +1,91 @@ +// Copyright (C) 2024 Mandiant, Inc. All Rights Reserved. +package objfile + +import ( + "bufio" + "os" + "path/filepath" + "strings" + "testing" +) + +// TestExtractStrings_CompareWithFLOSS validates our Go string extraction +// against FLOSS's output. The reference output was generated using: +// python -m floss.language.go.extract testproject/testproject.exe -n 4 +func TestExtractStrings_CompareWithFLOSS(t *testing.T) { + // Load FLOSS reference output + flossOutputPath := filepath.Join("..", "testdata", "floss_reference.txt") + file, err := os.Open(flossOutputPath) + if err != nil { + t.Skipf("FLOSS reference output not found: %v", err) + } + defer file.Close() + + // Parse FLOSS output (format: "0x12345: string content") + flossStrings := make(map[string]bool) + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "0x") { + parts := strings.SplitN(line, ": ", 2) + if len(parts) == 2 { + flossStrings[parts[1]] = true + } + } + } + + t.Logf("FLOSS reference contains %d strings", len(flossStrings)) + + // Extract strings using GoReSym + testBinary := filepath.Join("..", "testproject", "testproject.exe") + f, err := Open(testBinary) + if err != nil { + t.Fatalf("Failed to open test binary: %v", err) + } + defer f.Close() + + goresymStrings, err := f.ExtractStrings() + if err != nil { + t.Fatalf("ExtractStrings() failed: %v", err) + } + + // Convert to set + goresymSet := make(map[string]bool) + for _, s := range goresymStrings { + goresymSet[s] = true + } + + // Calculate overlap + inBoth := 0 + for s := range goresymSet { + if flossStrings[s] { + inBoth++ + } + } + + goresymCount := len(goresymSet) + matchRate := float64(inBoth) / float64(goresymCount) * 100 + + t.Logf("GoReSym extracted: %d strings", goresymCount) + t.Logf("Overlap with FLOSS: %d strings (%.1f%% match rate)", inBoth, matchRate) + + // Validate high match rate (>= 95%) + if matchRate < 95.0 { + t.Errorf("Match rate too low: %.1f%% (expected >= 95%%)", matchRate) + + // Show examples of mismatches + t.Log("Sample strings only in GoReSym:") + count := 0 + for s := range goresymSet { + if !flossStrings[s] && count < 5 { + t.Logf(" %q", s) + count++ + } + } + } + + // Ensure reasonable extraction + if goresymCount < 100 { + t.Errorf("GoReSym extracted too few strings: %d (expected >= 100)", goresymCount) + } +} diff --git a/objfile/strings_test.go b/objfile/strings_test.go new file mode 100644 index 0000000..18e2e05 --- /dev/null +++ b/objfile/strings_test.go @@ -0,0 +1,344 @@ +package objfile + +import ( + "path/filepath" + "testing" +) + +// TestExtractStrings_ELF tests string extraction from an ELF binary (Linux) +func TestExtractStrings_ELF(t *testing.T) { + testBinary := filepath.Join("..", "testproject", "testproject") + + file, err := Open(testBinary) + if err != nil { + t.Skipf("Could not open test binary: %v (run: cd testproject && go build)", err) + } + defer file.Close() + + strings, err := file.ExtractStrings() + if err != nil { + t.Fatalf("ExtractStrings() failed: %v", err) + } + + // Should extract a reasonable number of Go strings + if len(strings) < 100 { + t.Errorf("Expected at least 100 strings from ELF binary, got %d", len(strings)) + } + + // Verify common Go type/keyword strings are present + expectedStrings := []string{"func", "chan", "bool", "uint"} + assertStringsPresent(t, strings, expectedStrings) +} + +// TestExtractStrings_PE tests string extraction from a PE binary (Windows) +func TestExtractStrings_PE(t *testing.T) { + testBinary := filepath.Join("..", "testproject", "testproject.exe") + + file, err := Open(testBinary) + if err != nil { + t.Skipf("Could not open Windows test binary: %v (run: cd testproject && GOOS=windows GOARCH=amd64 go build -o testproject.exe)", err) + } + defer file.Close() + + strings, err := file.ExtractStrings() + if err != nil { + t.Fatalf("ExtractStrings() failed: %v", err) + } + + // Should extract a reasonable number of Go strings + if len(strings) < 100 { + t.Errorf("Expected at least 100 strings from PE binary, got %d", len(strings)) + } + + // Verify common Go type/keyword strings are present + expectedStrings := []string{"func", "chan", "bool", "uint"} + assertStringsPresent(t, strings, expectedStrings) +} + +// TestFindLongestMonotonicRun validates the core algorithm +// Note: function requires at least 10 entries for a valid run (practical filter) +func TestFindLongestMonotonicRun(t *testing.T) { + t.Run("finds monotonic run in realistic data", func(t *testing.T) { + // Create realistic test data: monotonically increasing lengths + candidates := make([]StringCandidate, 50) + for i := range candidates { + candidates[i] = StringCandidate{ + Pointer: uint64(0x1000 + i*10), + Length: uint64(i + 4), // lengths 4, 5, 6, ... 53 + } + } + + start, end := findLongestMonotonicRun(candidates) + + // Should find the entire sequence as one run + if start == -1 || end == -1 { + t.Error("Expected to find a valid run, got (-1, -1)") + } + + runLength := end - start + 1 + if runLength < 10 { + t.Errorf("Expected run length >= 10, got %d", runLength) + } + }) + + t.Run("too short returns -1", func(t *testing.T) { + candidates := []StringCandidate{ + {Length: 1}, {Length: 2}, {Length: 3}, // Only 3 entries + } + start, end := findLongestMonotonicRun(candidates) + if start != -1 || end != -1 { + t.Errorf("Expected (-1, -1) for short input, got (%d, %d)", start, end) + } + }) +} + +// TestIsFullyPrintable validates the printability filter. +// All characters must be printable (unicode.IsPrint) or common whitespace (\t, \n, \r). +func TestIsFullyPrintable(t *testing.T) { + tests := []struct { + input string + want bool + }{ + {"hello world", true}, + {"runtime.error", true}, + {"line1\nline2", true}, // newlines are allowed + {"col1\tcol2", true}, // tabs are allowed + {"windows\r\n", true}, // carriage return allowed + {"\x00\x01\x02\x03", false}, // all non-printable + {"abc\x00\x01", false}, // any non-printable fails (was 80% threshold) + {"mostly ok \x01", false}, // even one non-printable byte fails + {"", false}, // empty string + } + + for _, tt := range tests { + got := isFullyPrintable(tt.input) + if got != tt.want { + t.Errorf("isFullyPrintable(%q) = %v, want %v", tt.input, got, tt.want) + } + } +} + +// TestExtractStrings_MinLength validates minimum length filtering +func TestExtractStrings_MinLength(t *testing.T) { + testBinary := filepath.Join("..", "testproject", "testproject") + + file, err := Open(testBinary) + if err != nil { + t.Skip("Test binary not available") + } + defer file.Close() + + strings, err := file.ExtractStrings() + if err != nil { + t.Fatalf("ExtractStrings() failed: %v", err) + } + + // All strings must be >= 4 characters (MIN_STRING_LENGTH) + for _, s := range strings { + if len(s) < 4 { + t.Errorf("Found string shorter than 4 chars: %q (len=%d)", s, len(s)) + } + } +} + +// TestFindLongestMonotonicRun_MixedData tests that the algorithm correctly +// finds the blob candidates among noise. This simulates address-sorted +// candidates where only the middle portion points into the string blob. +func TestFindLongestMonotonicRun_MixedData(t *testing.T) { + // Simulate: 5 noise candidates, then 20 blob candidates, then 5 noise + var candidates []StringCandidate + + // Noise before blob (random lengths, not monotonic) + noise1 := []uint64{50, 3, 100, 7, 42} + for i, l := range noise1 { + candidates = append(candidates, StringCandidate{ + Pointer: uint64(0x1000 + i*8), + Length: l, + }) + } + + // String blob candidates: sorted by address, monotonically increasing lengths + // This is what Go's string internment table looks like + for i := 0; i < 20; i++ { + candidates = append(candidates, StringCandidate{ + Pointer: uint64(0x4000 + i*10), + Length: uint64(4 + i), // 4, 5, 6, ..., 23 + }) + } + + // Noise after blob + noise2 := []uint64{2, 80, 1, 60, 5} + for i, l := range noise2 { + candidates = append(candidates, StringCandidate{ + Pointer: uint64(0x8000 + i*8), + Length: l, + }) + } + + start, end := findLongestMonotonicRun(candidates) + + if start == -1 || end == -1 { + t.Fatal("Expected to find a valid run, got (-1, -1)") + } + + runLength := end - start + 1 + if runLength != 20 { + t.Errorf("Expected run length 20 (the blob candidates), got %d", runLength) + } + + // The run should start at index 5 (after the 5 noise entries) + if start != 5 { + t.Errorf("Expected run to start at index 5, got %d", start) + } +} + +// TestDeduplicateUint64 validates the deduplication helper +func TestDeduplicateUint64(t *testing.T) { + tests := []struct { + name string + input []uint64 + expect []uint64 + }{ + {"no duplicates", []uint64{1, 2, 3}, []uint64{1, 2, 3}}, + {"with duplicates", []uint64{1, 1, 2, 3, 3, 3, 4}, []uint64{1, 2, 3, 4}}, + {"all same", []uint64{5, 5, 5}, []uint64{5}}, + {"single element", []uint64{42}, []uint64{42}}, + {"empty", []uint64{}, []uint64{}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := deduplicateUint64(tt.input) + if len(got) != len(tt.expect) { + t.Errorf("deduplicateUint64(%v) returned %d elements, want %d", tt.input, len(got), len(tt.expect)) + return + } + for i := range got { + if got[i] != tt.expect[i] { + t.Errorf("deduplicateUint64(%v)[%d] = %d, want %d", tt.input, i, got[i], tt.expect[i]) + } + } + }) + } +} + +// TestIsDataSection validates section name matching across ELF/PE/Mach-O formats +func TestIsDataSection(t *testing.T) { + tests := []struct { + name string + want bool + }{ + // ELF sections + {".rodata", true}, + {".data", true}, + {".noptrdata", true}, + // Mach-O sections + {"__rodata", true}, + {"__data", true}, + {"__noptrdata", true}, + // PE sections + {".rdata", true}, + {".text", true}, // Included for old Go Windows binaries (1.7-1.10) that store strings in .text + // Suffixed variants (some formats append .__) + {".rodata.__", true}, + // Non-data sections + {".bss", false}, + {"__TEXT", false}, + {".got", false}, + } + + for _, tt := range tests { + got := isDataSection(tt.name) + if got != tt.want { + t.Errorf("isDataSection(%q) = %v, want %v", tt.name, got, tt.want) + } + } +} + +// TestFindStringCandidates validates candidate extraction from raw binary data +func TestFindStringCandidates(t *testing.T) { + t.Run("64-bit little-endian", func(t *testing.T) { + // Create a fake section with one string struct: + // struct: pointer=0x5000, length=5 + // Scanner steps at ptrSize (8-byte) alignment, so we use exactly + // one 16-byte struct to avoid overlapping false positives. + data := make([]byte, 16) + // pointer = 0x5000 (little-endian uint64) + data[0] = 0x00 + data[1] = 0x50 + data[2] = 0x00 + data[3] = 0x00 + data[4] = 0x00 + data[5] = 0x00 + data[6] = 0x00 + data[7] = 0x00 + // length = 5 (little-endian uint64) + data[8] = 0x05 + data[9] = 0x00 + data[10] = 0x00 + data[11] = 0x00 + data[12] = 0x00 + data[13] = 0x00 + data[14] = 0x00 + data[15] = 0x00 + + // imageMin=0x1000, imageMax=0x10000 (pointer 0x5000 is in range), maxSectionSize=1000 + candidates := findStringCandidates(data, 0x1000, true, true, 0x1000, 0x10000, 1000) + + if len(candidates) != 1 { + t.Fatalf("Expected 1 candidate, got %d", len(candidates)) + } + if candidates[0].Pointer != 0x5000 || candidates[0].Length != 5 { + t.Errorf("candidate[0] = {%#x, %d}, want {0x5000, 5}", candidates[0].Pointer, candidates[0].Length) + } + }) + + t.Run("skips zero pointer and length", func(t *testing.T) { + data := make([]byte, 16) + // pointer=0, length=0 -- should be skipped + candidates := findStringCandidates(data, 0x1000, true, true, 0x1000, 0x10000, 1000) + if len(candidates) != 0 { + t.Errorf("Expected 0 candidates for zero data, got %d", len(candidates)) + } + }) + + t.Run("skips pointer outside image range", func(t *testing.T) { + data := make([]byte, 16) + // pointer = 0x5000 (outside range [0x1000, 0x4000)) + data[0] = 0x00 + data[1] = 0x50 + data[8] = 0x05 + candidates := findStringCandidates(data, 0x1000, true, true, 0x1000, 0x4000, 1000) + if len(candidates) != 0 { + t.Errorf("Expected 0 candidates for out-of-range pointer, got %d", len(candidates)) + } + }) + + t.Run("skips length exceeding max section size", func(t *testing.T) { + data := make([]byte, 16) + // pointer = 0x2000 (in range), length = 500 (exceeds maxSectionSize=100) + data[0] = 0x00 + data[1] = 0x20 + data[8] = 0xF4 // 500 in little-endian + data[9] = 0x01 + candidates := findStringCandidates(data, 0x1000, true, true, 0x1000, 0x10000, 100) + if len(candidates) != 0 { + t.Errorf("Expected 0 candidates for oversized length, got %d", len(candidates)) + } + }) +} + +// Helper function to check if expected strings are present +func assertStringsPresent(t *testing.T, strings []string, expected []string) { + t.Helper() + stringSet := make(map[string]bool) + for _, s := range strings { + stringSet[s] = true + } + + for _, exp := range expected { + if !stringSet[exp] { + t.Errorf("Expected string %q not found in extracted strings", exp) + } + } +} diff --git a/testdata/floss_reference.txt b/testdata/floss_reference.txt new file mode 100644 index 0000000..0a627f6 --- /dev/null +++ b/testdata/floss_reference.txt @@ -0,0 +1,1750 @@ +0x22c8: UUUUUUUU +0x68d5: debugCal +0x6934: debugCal +0x6948: l128 +0x695a: l256 +0x6972: l512 +0x699a: debugCal +0x69aa: l102 +0x69cc: l204 +0x69f0: debugCal +0x6a00: l409 +0x6a19: l819 +0x6a32: debugCal +0x6a42: l163 +0x6a5d: l327 +0x6a78: l655 +0x7b15: runtime +0x7b24: error: +0x20408: UUUUUUUU +0x20447: wwwwwwww +0x35794: runtime. +0x460c8: lera +0x56a8f: gopa +0x58502: gopa +0x5856c: runtime. +0x58584: runtime. +0x73a50: reflect. +0x73a60: Valu +0x73a8a: reflect. +0xa540c: ping +0xa5410: true +0xa5414: 3125 +0xa5418: -Inf +0xa541c: +Inf +0xa5420: file +0xa5424: pipe +0xa5428: bool +0xa542c: int8 +0xa5430: uint +0xa5434: chan +0xa5438: func +0xa543c: call +0xa5440: kind +0xa5444: on +0xa5448: allg +0xa544c: allp +0xa5450: root +0xa5454: itab +0xa5458: sbrk +0xa545c: idle +0xa5460: dead +0xa5464: is +0xa5468: LEAF +0xa546c: base +0xa5470: of +0xa5474: ) = +0xa5478: <== +0xa547c: GOGC +0xa5480: ] = +0xa5484: pc= +0xa5488: : p= +0xa548c: cas1 +0xa5490: cas2 +0xa5494: cas3 +0xa5498: cas4 +0xa549c: cas5 +0xa54a0: cas6 +0xa54a4: at +0xa54a8: + m= +0xa54ac: sp= +0xa54b0: sp: +0xa54b4: lr: +0xa54b8: fp= +0xa54bc: gp= +0xa54c0: mp= +0xa54c4: ) m= +0xa54c8: bind +0xa54cc: erms +0xa54d0: sse3 +0xa54d4: avx2 +0xa54d8: bmi1 +0xa54dc: bmi2 +0xa54e0: false +0xa54e5: +0xa54ea: Error +0xa54ef: 15625 +0xa54f4: 78125 +0xa54f9: write +0xa54fe: close +0xa5503: int16 +0xa5508: int32 +0xa550d: int64 +0xa5512: uint8 +0xa5517: array +0xa551c: slice +0xa5521: and +0xa5526: defer +0xa552b: sweep +0xa5530: testR +0xa5535: testW +0xa553a: execW +0xa553f: execR +0xa5544: sched +0xa5549: hchan +0xa554e: sudog +0xa5553: gscan +0xa5558: mheap +0xa555d: trace +0xa5562: panic +0xa5567: sleep +0xa556c: cnt= +0xa5571: gcing +0xa5576: MB, +0xa557b: got= +0xa5580: ... + +0xa5585: max= +0xa558a: scav +0xa558f: ptr +0xa5594: ] = ( +0xa5599: usage +0xa559e: init +0xa55a3: ms, +0xa55a8: fault +0xa55ad: tab= +0xa55b2: top= +0xa55b7: [...] +0xa55bc: , fp: +0xa55c1: ntohs +0xa55c6: Greek +0xa55cb: sse41 +0xa55d0: sse42 +0xa55d5: ssse3 +0xa55da: String +0xa55e0: Format +0xa55e6: []byte +0xa55ec: string +0xa55f2: 390625 +0xa55f8: uint16 +0xa55fe: uint32 +0xa5604: uint64 +0xa560a: struct +0xa5610: chan<- +0xa5616: <-chan +0xa561c: Value +0xa5622: GetACP +0xa5628: sysmon +0xa562e: timers +0xa5634: efence +0xa563a: select +0xa5640: , not +0xa5646: object +0xa564c: next= +0xa5652: jobs= +0xa5658: goid +0xa565e: sweep +0xa5664: B -> +0xa566a: % util +0xa5670: alloc +0xa5676: free +0xa567c: span= +0xa5682: prev= +0xa5688: list= +0xa568e: , i = +0xa5694: code= +0xa569a: addr= +0xa56a0: m->p= +0xa56a6: p->m= +0xa56ac: SCHED +0xa56b2: curg= +0xa56b8: ctxt: +0xa56be: min= +0xa56c4: max= +0xa56ca: (...) + +0xa56d0: m=nil +0xa56d6: base +0xa56dc: listen +0xa56e2: socket +0xa56e8: Common +0xa56ee: rdtscp +0xa56f4: popcnt +0xa56fa: float32 +0xa5701: float64 +0xa5708: 1953125 +0xa570f: 9765625 +0xa5716: console +0xa571d: invalid +0xa5724: uintptr +0xa572b: ChanDir +0xa5732: Value> +0xa5739: forcegc +0xa5740: allocmW +0xa5747: cpuprof +0xa574e: allocmR +0xa5755: unknown +0xa575c: gctrace +0xa5763: IO wait +0xa576a: running +0xa5771: syscall +0xa5778: waiting +0xa577f: UNKNOWN +0xa5786: , goid= +0xa578d: s=nil + +0xa5794: (scan +0xa579b: MB in +0xa57a2: pacer: +0xa57a9: % CPU ( +0xa57b0: zombie +0xa57b7: , j0 = +0xa57be: head = +0xa57c5: panic: +0xa57cc: nmsys= +0xa57d3: locks= +0xa57da: dying= +0xa57e1: allocs +0xa57e8: GODEBUG +0xa57ef: m->g0= +0xa57f6: pad1= +0xa57fd: pad2= +0xa5804: text= +0xa580b: minpc= +0xa5812: value= +0xa5819: (scan) +0xa5820: types +0xa5827: : type +0xa582e: CopySid +0xa5835: WSARecv +0xa583c: WSASend +0xa5843: connect +0xa584a: avx512f +0xa5851: GoString +0xa5859: 48828125 +0xa5861: nil Pool +0xa5869: scavenge +0xa5871: pollDesc +0xa5879: traceBuf +0xa5881: deadlock +0xa5889: raceFini +0xa5891: panicnil +0xa5899: cgocheck +0xa58a1: runnable +0xa58a9: procid +0xa58b1: rax +0xa58b9: rbx +0xa58c1: rcx +0xa58c9: rdx +0xa58d1: rdi +0xa58d9: rsi +0xa58e1: rbp +0xa58e9: rsp +0xa58f1: r8 +0xa58f9: r9 +0xa5901: r10 +0xa5909: r11 +0xa5911: r12 +0xa5919: r13 +0xa5921: r14 +0xa5929: r15 +0xa5931: rip +0xa5939: rflags +0xa5941: cs +0xa5949: fs +0xa5951: gs +0xa5959: is not +0xa5961: pointer +0xa5969: packed= +0xa5971: BAD RANK +0xa5979: status +0xa5981: unknown( +0xa5989: trigger= +0xa5991: npages= +0xa5999: nalloc= +0xa59a1: nfreed= +0xa59a9: [signal +0xa59b1: newval= +0xa59b9: mcount= +0xa59c1: bytes, +0xa59c9: stack=[ +0xa59d1: minLC= +0xa59d9: maxpc= +0xa59e1: stack=[ +0xa59e9: minutes +0xa59f1: etypes +0xa59f9: no anode +0xa5a01: CancelIo +0xa5a09: ReadFile +0xa5a11: AcceptEx +0xa5a19: WSAIoctl +0xa5a21: shutdown +0xa5a29: wsaioctl +0xa5a31: avx512bw +0xa5a39: avx512vl +0xa5a41: 244140625 +0xa5a4a: complex64 +0xa5a53: interface +0xa5a5c: invalid n +0xa5a65: funcargs( +0xa5a6e: bad indir +0xa5a77: reflect: +0xa5a80: Interface +0xa5a89: psapi.dll +0xa5a92: profBlock +0xa5a9b: stackpool +0xa5aa4: hchanLeaf +0xa5aad: wbufSpans +0xa5ab6: mSpanDead +0xa5abf: scavtrace +0xa5ac8: inittrace +0xa5ad1: panicwait +0xa5ada: chan send +0xa5ae3: preempted +0xa5aec: coroutine +0xa5af5: copystack +0xa5afe: -> node= +0xa5b07: ms cpu, +0xa5b10: (forced) +0xa5b19: wbuf1.n= +0xa5b22: wbuf2.n= +0xa5b2b: s.limit= +0xa5b34: s.state= +0xa5b3d: B work ( +0xa5b46: B exp.) +0xa5b4f: marked +0xa5b58: unmarked +0xa5b61: in use) + +0xa5b6a: , size = +0xa5b73: bad prune +0xa5b7c: , tail = +0xa5b85: recover: +0xa5b8e: not in [ +0xa5b97: ctxt != 0 +0xa5ba0: , oldval= +0xa5ba9: , newval= +0xa5bb2: threads= +0xa5bbb: : status= +0xa5bc4: blocked= +0xa5bcd: lockedg= +0xa5bd6: atomicor8 +0xa5bdf: runtime= +0xa5be8: m->curg= +0xa5bf1: (unknown) +0xa5bfa: traceback +0xa5c03: } stack=[ +0xa5c0c: lockedm= +0xa5c15: FindClose +0xa5c1e: LocalFree +0xa5c27: MoveFileW +0xa5c30: WriteFile +0xa5c39: WSASendTo +0xa5c42: ntdll.dll +0xa5c4b: Inherited +0xa5c54: pclmulqdq +0xa5c5d: 1220703125 +0xa5c67: 6103515625 +0xa5c71: /dev/stdin +0xa5c7b: complex128 +0xa5c85: t.Kind == +0xa5c8f: LockFileEx +0xa5c99: WSASocketW +0xa5ca3: ws2_32.dll +0xa5cad: notifyList +0xa5cb7: profInsert +0xa5cc1: stackLarge +0xa5ccb: mSpanInUse +0xa5cd5: GOMAXPROCS +0xa5cdf: stop trace +0xa5ce9: disablethp +0xa5cf3: invalidptr +0xa5cfd: schedtrace +0xa5d07: semacquire +0xa5d11: debug call +0xa5d1b: flushGen +0xa5d25: MB goal, +0xa5d2f: s.state = +0xa5d39: s.base()= +0xa5d43: heapGoal= +0xa5d4d: GOMEMLIMIT +0xa5d57: KiB now, +0xa5d61: pages at +0xa5d6b: sweepgen= +0xa5d75: sweepgen +0xa5d7f: , bound = +0xa5d89: , limit = +0xa5d93: tracefree( +0xa5d9d: tracegc() + +0xa5da7: exitThread +0xa5db1: Bad varint +0xa5dbb: GC forced + +0xa5dc5: runqueue= +0xa5dcf: stopwait= +0xa5dd9: runqsize= +0xa5de3: gfreecnt= +0xa5ded: throwing= +0xa5df7: spinning= +0xa5e01: atomicand8 +0xa5e0b: float64nan +0xa5e15: float32nan +0xa5e1f: Exception +0xa5e29: ptrSize= +0xa5e33: targetpc= +0xa5e3d: until pc= +0xa5e47: unknown pc +0xa5e51: runtime: g +0xa5e5b: goroutine +0xa5e65: owner died +0xa5e6f: DnsQuery_W +0xa5e79: GetIfEntry +0xa5e83: CancelIoEx +0xa5e8d: CreatePipe +0xa5e97: GetVersion +0xa5ea1: WSACleanup +0xa5eab: WSAStartup +0xa5eb5: getsockopt +0xa5ebf: setsockopt +0xa5ec9: dnsapi.dll +0xa5ed3: 30517578125 +0xa5ede: short write +0xa5ee9: /dev/stdout +0xa5ef4: /dev/stderr +0xa5eff: CloseHandle +0xa5f0a: OpenProcess +0xa5f15: GetFileType +0xa5f20: bad argSize +0xa5f2b: methodargs( +0xa5f36: ProcessPrng +0xa5f41: MoveFileExW +0xa5f4c: NetShareAdd +0xa5f57: NetShareDel +0xa5f62: userenv.dll +0xa5f6d: assistQueue +0xa5f78: netpollInit +0xa5f83: reflectOffs +0xa5f8e: globalAlloc +0xa5f99: mSpanManual +0xa5fa4: start trace +0xa5faf: clobberfree +0xa5fba: gccheckmark +0xa5fc5: scheddetail +0xa5fd0: cgocall nil +0xa5fdb: unreachable +0xa5fe6: s.nelems= +0xa5ff1: of size +0xa5ffc: runtime: p +0xa6007: ms clock, +0xa6012: nBSSRoots= +0xa601d: runtime: P +0xa6028: exp.) for +0xa6033: minTrigger= +0xa603e: GOMEMLIMIT= +0xa6049: bad m value +0xa6054: , elemsize= +0xa605f: freeindex= +0xa606a: span.list= +0xa6075: , npages = +0xa6080: tracealloc( +0xa608b: p->status= +0xa6096: in status +0xa60a1: idleprocs= +0xa60ac: gcwaiting= +0xa60b7: schedtick= +0xa60c2: timerslen= +0xa60cd: mallocing= +0xa60d8: bad timediv +0xa60e3: float64nan1 +0xa60ee: float64nan2 +0xa60f9: float64nan3 +0xa6104: float32nan2 +0xa610f: GOTRACEBACK +0xa611a: ) at entry+ +0xa6125: (targetpc= +0xa6130: , plugin: +0xa613b: runtime: g +0xa6146: : frame.sp= +0xa6151: created by +0xa615c: broken pipe +0xa6167: bad message +0xa6172: file exists +0xa617d: bad address +0xa6188: RegCloseKey +0xa6193: CreateFileW +0xa619e: DeleteFileW +0xa61a9: ExitProcess +0xa61b4: FreeLibrary +0xa61bf: SetFileTime +0xa61ca: VirtualLock +0xa61d5: WSARecvFrom +0xa61e0: closesocket +0xa61eb: getpeername +0xa61f6: getsockname +0xa6201: crypt32.dll +0xa620c: mswsock.dll +0xa6217: secur32.dll +0xa6222: shell32.dll +0xa622d: i/o timeout +0xa6238: 152587890625 +0xa6244: 762939453125 +0xa6250: OpenServiceW +0xa625c: RevertToSelf +0xa6268: CreateEventW +0xa6274: GetConsoleCP +0xa6280: UnlockFileEx +0xa628c: VirtualQuery +0xa6298: advapi32.dll +0xa62a4: iphlpapi.dll +0xa62b0: kernel32.dll +0xa62bc: netapi32.dll +0xa62c8: sweepWaiters +0xa62d4: traceStrings +0xa62e0: spanSetSpine +0xa62ec: mspanSpecial +0xa62f8: gcBitsArenas +0xa6304: mheapSpecial +0xa6310: gcpacertrace +0xa631c: madvdontneed +0xa6328: harddecommit +0xa6334: dumping heap +0xa6340: chan receive +0xa634c: lfstack.push +0xa6358: span.limit= +0xa6364: span.state= +0xa6370: bad flushGen +0xa637c: MB stacks, +0xa6388: worker mode +0xa6394: nDataRoots= +0xa63a0: nSpanRoots= +0xa63ac: wbuf1= +0xa63b8: wbuf2= +0xa63c4: gcscandone +0xa63d0: runtime: gp= +0xa63dc: found at *( +0xa63e8: s.elemsize= +0xa63f4: B (∆goal +0xa6400: , cons/mark +0xa640c: maxTrigger= +0xa6418: pages/byte + +0xa6424: s.sweepgen= +0xa6430: allocCount +0xa643c: end tracegc + +0xa6454: bad g0 stack +0xa6460: self-preempt +0xa646c: [recovered] +0xa6478: bad recovery +0xa6484: bad g status +0xa6490: entersyscall +0xa649c: wirep: p->m= +0xa64a8: ) p->status= +0xa64b4: releasep: m= +0xa64c0: sysmonwait= +0xa64cc: preemptoff= +0xa64d8: cas64 failed +0xa64e4: m->gsignal= +0xa64f0: -byte limit + +0xa64fc: runtime: sp= +0xa6508: abi mismatch +0xa6514: invalid slot +0xa6520: host is down +0xa652c: illegal seek +0xa6538: GetLengthSid +0xa6544: GetLastError +0xa6550: GetStdHandle +0xa655c: GetTempPathW +0xa6568: LoadLibraryW +0xa6574: ReadConsoleW +0xa6580: SetEndOfFile +0xa658c: TransmitFile +0xa6598: GetAddrInfoW +0xa65a4: not pollable +0xa65b0: 3814697265625 +0xa65bd: GetTempPath2W +0xa65ca: Module32NextW +0xa65d7: wakeableSleep +0xa65e4: profMemActive +0xa65f1: profMemFuture +0xa65fe: traceStackTab +0xa660b: execRInternal +0xa6618: testRInternal +0xa6625: GC sweep wait +0xa6632: out of memory +0xa663f: is nil, not +0xa664c: value method +0xa6659: bad map state +0xa6666: span.base()= +0xa6673: bad flushGen +0xa6680: , not pointer +0xa668d: != sweepgen +0xa669a: MB globals, +0xa66a7: work.nproc= +0xa66b4: work.nwait= +0xa66c1: nStackRoots= +0xa66ce: flushedWork +0xa66db: double unlock +0xa66e8: s.spanclass= +0xa66f5: MB) workers= +0xa6702: min too large +0xa670f: -byte block ( +0xa671c: runtime: val= +0xa6729: runtime: seq= +0xa6736: fatal error: +0xa6743: idlethreads= +0xa6750: syscalltick= +0xa675d: load64 failed +0xa676a: xadd64 failed +0xa6777: xchg64 failed +0xa6784: nil stackbase +0xa6791: } + sched={pc: +0xa679e: , gp->status= +0xa67ab: pluginpath= +0xa67b8: runtime: pid= +0xa67c5: : unknown pc +0xa67d2: called from +0xa67df: level 3 reset +0xa67ec: srmount error +0xa67f9: timer expired +0xa6806: exchange full +0xa6813: RegEnumKeyExW +0xa6820: RegOpenKeyExW +0xa682d: CertOpenStore +0xa683a: FindNextFileW +0xa6847: MapViewOfFile +0xa6854: VirtualUnlock +0xa6861: WriteConsoleW +0xa686e: FreeAddrInfoW +0xa687b: gethostbyname +0xa6888: getservbyname +0xa6895: RegDeleteKeyW +0xa68a2: RegEnumValueW +0xa68af: 19073486328125 +0xa68bd: 95367431640625 +0xa68cb: unsafe.Pointer +0xa68d9: on zero Value +0xa68e7: unknown method +0xa68f5: OpenSCManagerW +0xa6903: Module32FirstW +0xa6911: userArenaState +0xa691f: read mem stats +0xa692d: allocfreetrace +0xa693b: gcstoptheworld +0xa6949: GC assist wait +0xa6957: finalizer wait +0xa6965: sync.Cond.Wait +0xa6973: s.allocCount= +0xa6981: nil elem type! +0xa698f: to finalizer +0xa699d: GC worker init +0xa69ab: runtime: full= +0xa69b9: runtime: want= +0xa69c7: MB; allocated +0xa69e3: bad restart PC +0xa69f1: -thread limit + +0xa69ff: stopm spinning +0xa6a0d: nmidlelocked= +0xa6a1b: needspinning= +0xa6a29: randinit twice +0xa6a37: store64 failed +0xa6a45: semaRoot queue +0xa6a53: bad allocCount +0xa6a61: bad span state +0xa6a6f: stack overflow +0xa6a7d: untyped args +0xa6a8b: out of range +0xa6a99: no module data +0xa6aa7: runtime: seq1= +0xa6ab5: runtime: goid= +0xa6ac3: in goroutine +0xa6ad1: file too large +0xa6adf: is a directory +0xa6aed: level 2 halted +0xa6afb: level 3 halted +0xa6b09: too many links +0xa6b17: no such device +0xa6b25: protocol error +0xa6b33: text file busy +0xa6b41: too many users +0xa6b4f: CryptGenRandom +0xa6b5d: CertCloseStore +0xa6b6b: CreateProcessW +0xa6b79: FindFirstFileW +0xa6b87: FormatMessageW +0xa6b95: GetConsoleMode +0xa6ba3: GetProcAddress +0xa6bb1: Process32NextW +0xa6bbf: SetFilePointer +0xa6bcd: NetUserGetInfo +0xa6bdb: GetUserNameExW +0xa6be9: TranslateNameW +0xa6bf7: getprotobyname +0xa6c05: procedure in +0xa6c13: winapi error # +0xa6c21: unreachable: +0xa6c2f: RegSetValueExW +0xa6c3d: 476837158203125 +0xa6c4c: GetProcessTimes +0xa6c5b: DuplicateHandle +0xa6c6a: invalid argSize +0xa6c79: +0xa6c88: ImpersonateSelf +0xa6c97: OpenThreadToken +0xa6ca6: allocmRInternal +0xa6cb5: write heap dump +0xa6cc4: asyncpreemptoff +0xa6cd3: force gc (idle) +0xa6ce2: sync.Mutex.Lock +0xa6cf1: malloc deadlock +0xa6d00: runtime error: +0xa6d0f: with GC prog + +0xa6d1e: scan missed a g +0xa6d2d: misaligned mask +0xa6d3c: runtime: min = +0xa6d4b: runtime: inUse= +0xa6d5a: runtime: max = +0xa6d69: bad panic stack +0xa6d78: recovery failed +0xa6d87: stopm holding p +0xa6d96: startm: m has p +0xa6da5: preempt SPWRITE +0xa6db4: missing mcache? +0xa6dc3: ms: gomaxprocs= +0xa6dd2: randinit missed +0xa6de1: ] + morebuf={pc: +0xa6df0: : no frame (sp= +0xa6dff: runtime: frame +0xa6e0e: runtimer: bad p +0xa6e1d: traceback stuck +0xa6e2c: advertise error +0xa6e3b: key has expired +0xa6e4a: network is down +0xa6e59: no medium found +0xa6e68: no such process +0xa6e77: GetAdaptersInfo +0xa6e86: CreateHardLinkW +0xa6e95: DeviceIoControl +0xa6ea4: FlushViewOfFile +0xa6eb3: GetCommandLineW +0xa6ec2: GetStartupInfoW +0xa6ed1: Process32FirstW +0xa6ee0: UnmapViewOfFile +0xa6eef: Failed to load +0xa6efe: Failed to find +0xa6f0d: RegCreateKeyExW +0xa6f1c: RegDeleteValueW +0xa6f2b: 2384185791015625 +0xa6f3b: 0123456789abcdef +0xa6f4b: 0123456789ABCDEF +0xa6f5b: TerminateProcess +0xa6f6b: DuplicateTokenEx +0xa6f7b: GetCurrentThread +0xa6f8b: RtlVirtualUnwind +0xa6f9b: integer overflow +0xa6fab: gcshrinkstackoff +0xa6fbb: tracefpunwindoff +0xa6fcb: GC scavenge wait +0xa6fdb: GC worker (idle) +0xa6feb: page trace flush +0xa6ffb: out of bounds [ +0xa700b: , not a function +0xa701b: gc: unswept span +0xa702b: KiB work (bg), +0xa703b: mheap.sweepgen= +0xa704b: runtime: nelems= +0xa705b: workbuf is empty +0xa706b: mSpanList.remove +0xa707b: mSpanList.insert +0xa708b: bad special kind +0xa709b: bad summary data +0xa70ab: runtime: addr = +0xa70bb: runtime: base = +0xa70cb: runtime: head = +0xa70eb: already; errno= +0xa70fb: +runtime stack: + +0xa710b: invalid g status +0xa711b: castogscanstatus +0xa712b: bad g transition +0xa713b: schedule: in cgo +0xa714b: reflect mismatch +0xa715b: untyped locals +0xa716b: missing stackmap +0xa717b: bad symbol table +0xa718b: non-Go function + +0xa719b: not in ranges: + +0xa71ab: invalid exchange +0xa71bb: no route to host +0xa71cb: invalid argument +0xa71db: message too long +0xa71eb: object is remote +0xa71fb: remote I/O error +0xa720b: SetFilePointerEx +0xa721b: OpenProcessToken +0xa722b: RegQueryInfoKeyW +0xa723b: RegQueryValueExW +0xa724b: DnsNameCompare_W +0xa725b: CreateDirectoryW +0xa726b: FlushFileBuffers +0xa727b: GetComputerNameW +0xa728b: GetFullPathNameW +0xa729b: GetLongPathNameW +0xa72ab: RemoveDirectoryW +0xa72bb: NetApiBufferFree +0xa72cb: GODEBUG: value " +0xa72db: 0123456789ABCDEFX +0xa72ec: 0123456789abcdefx +0xa72fd: reflect.Value.Int +0xa730e: 11920928955078125 +0xa731f: 59604644775390625 +0xa7330: unknown type kind +0xa7341: reflect: call of +0xa7352: reflect.Value.Len +0xa7363: goroutine profile +0xa7374: AllThreadsSyscall +0xa7385: GC assist marking +0xa7396: select (no cases) +0xa73a7: sync.RWMutex.Lock +0xa73b8: wait for GC cycle +0xa73c9: trace proc status +0xa73da: : missing method +0xa73eb: notetsleepg on g0 +0xa73fc: bad TinySizeClass +0xa740d: runtime: pointer +0xa741e: g already scanned +0xa742f: mark - bad status +0xa7440: scanobject n == 0 +0xa7451: swept cached span +0xa7462: markBits overflow +0xa7473: runtime: summary[ +0xa7484: runtime: level = +0xa7495: , p.searchAddr = +0xa74b7: runtime.newosproc +0xa74c8: runtime/internal/ +0xa74d9: thread exhaustion +0xa74ea: locked m0 woke up +0xa74fb: entersyscallblock +0xa750c: spinningthreads= +0xa751d: unknown caller pc +0xa752e: stack: frame={sp: +0xa753f: runtime: nameOff +0xa7550: runtime: typeOff +0xa7561: runtime: textOff +0xa7572: permission denied +0xa7583: wrong medium type +0xa7594: no data available +0xa75a5: exec format error +0xa75b6: LookupAccountSidW +0xa75c7: DnsRecordListFree +0xa75d8: GetCurrentProcess +0xa75e9: GetShortPathNameW +0xa75fa: WSAEnumProtocolsW +0xa760b: RegLoadMUIStringW +0xa761c: reflect.Value.Uint +0xa762e: 298023223876953125 +0xa7640: GetExitCodeProcess +0xa7652: reflect.Value.Elem +0xa7664: reflect.Value.Type +0xa7676: QueryServiceStatus +0xa7688: GetComputerNameExW +0xa769a: GetModuleFileNameW +0xa76ac: dontfreezetheworld +0xa76be: tracebackancestors +0xa76d0: adaptivestackstart +0xa76e2: traceadvanceperiod +0xa76f4: garbage collection +0xa7706: sync.RWMutex.RLock +0xa7718: GC worker (active) +0xa772a: stopping the world +0xa773c: bad lfnode address +0xa774e: system page size ( +0xa7760: but memory size +0xa7772: because dotdotdot +0xa7784: runtime: npages = +0xa7796: runtime: range = { +0xa77a8: index out of range +0xa77ba: runtime: gp: gp= +0xa77cc: runtime: getg: g= +0xa77de: forEachP: not done +0xa77f0: in async preempt + +0xa7802: bad manualFreeList +0xa7814: runtime: textAddr +0xa7826: frames elided... + +0xa7838: , locked to thread +0xa784a: runtime.semacreate +0xa785c: runtime.semawakeup +0xa786e: operation canceled +0xa7880: no child processes +0xa7892: connection refused +0xa78a4: RFS specific error +0xa78b6: identifier removed +0xa78c8: input/output error +0xa78da: multihop attempted +0xa78ec: file name too long +0xa78fe: no locks available +0xa7910: streams pipe error +0xa7922: LookupAccountNameW +0xa7934: CreateFileMappingW +0xa7946: GetFileAttributesW +0xa7958: SetFileAttributesW +0xa796a: CommandLineToArgvW +0xa797c: use of closed file +0xa798e: reflect.Value.IsNil +0xa79a1: reflect.Value.Float +0xa79b4: 1490116119384765625 +0xa79c7: 7450580596923828125 +0xa79da: WaitForSingleObject +0xa79ed: reflect.Value.Bytes +0xa7a00: reflect.Value.Field +0xa7a13: reflect.Value.Index +0xa7a26: SetTokenInformation +0xa7a39: MultiByteToWideChar +0xa7a4c: GC mark termination +0xa7a5f: panicwrap: no ( in +0xa7a72: panicwrap: no ) in +0xa7a85: called using nil * +0xa7a98: unknown wait reason +0xa7aab: notesleep not on g0 +0xa7abe: GC work not flushed +0xa7ad1: bad kind in runfinq +0xa7ae4: markroot: bad index +0xa7af7: nwait > work.nprocs +0xa7b0a: , gp->atomicstatus= +0xa7b1d: marking free object +0xa7b30: KiB work (eager), +0xa7b43: [controller reset] +0xa7b56: mspan.sweep: state= +0xa7b69: sysMemStat overflow +0xa7b7c: bad sequence number +0xa7b8f: ntdll.dll not found +0xa7ba2: winmm.dll not found +0xa7bb5: runtime: g0 stack [ +0xa7bc8: panic during malloc +0xa7bdb: panic holding locks +0xa7bee: missing deferreturn +0xa7c01: unexpected gp.param +0xa7c14: panic during panic + +0xa7c27: , g->atomicstatus= +0xa7c3a: unexpected g status +0xa7c4d: bad runtime·mstart +0xa7c60: m not found in allm +0xa7c73: stopm holding locks +0xa7c86: semaRoot rotateLeft +0xa7c99: bad notifyList size +0xa7cac: runtime: preempt g0 +0xa7cbf: runtime: pcdata is +0xa7cd2: bad ABI description +0xa7ce5: dodeltimer: wrong P +0xa7cf8: adjusttimers: bad p +0xa7d0b: bad file descriptor +0xa7d1e: disk quota exceeded +0xa7d31: too many open files +0xa7d44: device not a stream +0xa7d57: directory not empty +0xa7d6a: CryptReleaseContext +0xa7d7d: GetTokenInformation +0xa7d90: CreateSymbolicLinkW +0xa7da3: GetCurrentProcessId +0xa7db6: file already exists +0xa7dc9: file does not exist +0xa7ddc: file already closed +0xa7def: 37252902984619140625 +0xa7e03: GetAdaptersAddresses +0xa7e17: GetProcessMemoryInfo +0xa7e2b: bcryptprimitives.dll +0xa7e3f: floating point error +0xa7e53: GC sweep termination +0xa7e67: ResetDebugLog (test) +0xa7e7b: chan send (nil chan) +0xa7e8f: flushing proc caches +0xa7ea3: malloc during signal +0xa7eb7: close of nil channel +0xa7ecb: inconsistent lockedm +0xa7edf: notetsleep not on g0 +0xa7ef3: bad system page size +0xa7f07: to unallocated span +0xa7f1b: p mcache not flushed +0xa7f2f: markroot jobs done + +0xa7f43: pacer: assist ratio= +0xa7f57: workbuf is not empty +0xa7f6b: bad use of bucket.mp +0xa7f7f: bad use of bucket.bp +0xa7f93: runtime: double wait +0xa7fa7: ws2_32.dll not found +0xa7fbb: preempt off reason: +0xa7fcf: forcegc: phase error +0xa7fe3: gopark: bad g status +0xa7ff7: go of nil func value +0xa800b: semaRoot rotateRight +0xa801f: reflect.makeFuncStub +0xa8033: dodeltimer0: wrong P +0xa8047: trace: out of memory +0xa805b: wirep: already in go +0xa806f: invalid request code +0xa8083: bad font file format +0xa8097: is a named type file +0xa80ab: key has been revoked +0xa80bf: connection timed out +0xa80d3: CreateProcessAsUserW +0xa80e7: CryptAcquireContextW +0xa80fb: CertOpenSystemStoreW +0xa810f: GetCurrentDirectoryW +0xa8123: GetFileAttributesExW +0xa8137: SetCurrentDirectoryW +0xa814b: SetHandleInformation +0xa815f: GetAcceptExSockaddrs +0xa8173: Hello, this is a test +0xa8188: reflect.Value.Complex +0xa819d: unsupported operation +0xa81b2: 186264514923095703125 +0xa81c7: 931322574615478515625 +0xa81dc: bad type in compare: +0xa81f1: of unexported method +0xa8206: unexpected value step +0xa821b: reflect.Value.Pointer +0xa8230: AdjustTokenPrivileges +0xa8245: LookupPrivilegeValueW +0xa825a: NetUserGetLocalGroups +0xa826f: GetProfilesDirectoryW +0xa8284: negative shift amount +0xa8299: concurrent map writes +0xa82ae: runtime: work.nwait= +0xa82c3: previous allocCount= +0xa82d8: , levelBits[level] = +0xa82ed: runtime: searchIdx = +0xa8302: defer on system stack +0xa8317: panic on system stack +0xa832c: async stack too large +0xa8341: startm: m is spinning +0xa8356: startlockedm: m has p +0xa836b: findrunnable: wrong p +0xa8380: preempt at unknown pc +0xa8395: releasep: invalid arg +0xa83aa: checkdead: runnable g +0xa83bf: runtime: newstack at +0xa83d4: runtime: newstack sp= +0xa83e9: runtime: confused by +0xa83fe: pcHeader.textStart= +0xa8413: timer data corruption +0xa8428: link has been severed +0xa843d: package not installed +0xa8452: block device required +0xa8467: state not recoverable +0xa847c: read-only file system +0xa8491: stale NFS file handle +0xa84a6: ReadDirectoryChangesW +0xa84bb: NetGetJoinInformation +0xa84d0: 4656612873077392578125 +0xa84e6: unexpected method step +0xa84fc: RtlLookupFunctionEntry +0xa8512: CreateEnvironmentBlock +0xa8528: integer divide by zero +0xa853e: CountPagesInUse (test) +0xa8554: ReadMetricsSlow (test) +0xa856a: trace reader (blocked) +0xa8580: trace goroutine status +0xa8596: send on closed channel +0xa85ac: call not at safe point +0xa85c2: getenv before env init +0xa85d8: interface conversion: +0xa85ee: freeIndex is not valid +0xa8604: oldoverflow is not nil +0xa861a: s.freeindex > s.nelems +0xa8630: bad sweepgen in refill +0xa8646: span has no free space +0xa865c: runtime: work.nwait = +0xa8672: runtime:scanstack: gp= +0xa8688: scanstack - bad status +0xa869e: headTailIndex overflow +0xa86b4: runtime.main not on m0 +0xa86ca: set_crosscall2 missing +0xa86e0: bad g->status in ready +0xa86f6: wirep: invalid p state +0xa870c: assembly checks failed +0xa8722: stack not a power of 2 +0xa8738: minpc or maxpc invalid +0xa874e: compileCallback: type +0xa8764: non-Go function at pc= +0xa877a: argument list too long +0xa8790: address already in use +0xa87a6: network is unreachable +0xa87bc: cannot allocate memory +0xa87d2: protocol not available +0xa87e8: protocol not supported +0xa87fe: remote address changed +0xa8814: ConvertSidToStringSidW +0xa882a: ConvertStringSidToSidW +0xa8840: CreateIoCompletionPort +0xa8856: GetEnvironmentStringsW +0xa886c: GetTimeZoneInformation +0xa8882: RtlGetNtVersionNumbers +0xa8898: +0xa88af: 23283064365386962890625 +0xa88c6: reflect.Value.Interface +0xa88dd: reflect.Value.NumMethod +0xa88f4: DestroyEnvironmentBlock +0xa890b: index out of range [%x] +0xa8922: ReadMemStatsSlow (test) +0xa8939: runtimecontentionstacks +0xa8950: chan receive (nil chan) +0xa8967: garbage collection scan +0xa897e: makechan: bad alignment +0xa8995: close of closed channel +0xa89ac: ) must be a power of 2 + +0xa89c3: system huge page size ( +0xa89da: runtime: s.allocCount= +0xa89f1: s.allocCount > s.nelems +0xa8a08: missing type in runfinq +0xa8a1f: runtime: internal error +0xa8a36: work.nwait > work.nproc +0xa8a4d: left over markroot jobs +0xa8a64: gcDrain phase incorrect +0xa8a7b: MB during sweep; swept +0xa8a92: bad profile stack count +0xa8aa9: runtime: netpoll failed +0xa8aee: panic during preemptoff +0xa8b05: nanotime returning zero +0xa8b1c: fatal: morestack on g0 + +0xa8b33: the current g is not g0 +0xa8b4a: schedule: holding locks +0xa8b61: invalid m->lockedInt = +0xa8b78: procresize: invalid arg +0xa8b8f: span has no free stacks +0xa8ba6: stack growth after fork +0xa8bbd: shrinkstack at bad time +0xa8bd4: reflect.methodValueCall +0xa8beb: device or resource busy +0xa8c02: interrupted system call +0xa8c19: no space left on device +0xa8c30: operation not supported +0xa8c47: operation not permitted +0xa8c5e: CertGetCertificateChain +0xa8c75: FreeEnvironmentStringsW +0xa8c8c: GetEnvironmentVariableW +0xa8ca3: GetSystemTimeAsFileTime +0xa8cba: SetEnvironmentVariableW +0xa8cd1: ", missing CPU support + +0xa8ce8: 116415321826934814453125 +0xa8d00: 582076609134674072265625 +0xa8d18: hash of unhashable type +0xa8d30: span has no free objects +0xa8d48: runtime: found obj at *( +0xa8d60: runtime: VirtualFree of +0xa8d78: queuefinalizer during GC +0xa8d90: update during transition +0xa8da8: runtime: markroot index +0xa8dc0: can't scan our own stack +0xa8dd8: gcDrainN phase incorrect +0xa8df0: pageAlloc: out of memory +0xa8e08: runtime: p.searchAddr = +0xa8e20: range partially overlaps +0xa8e38: stack trace unavailable + +0xa8e50: bindm in unexpected GOOS +0xa8e68: runqsteal: runq overflow +0xa8e80: double traceGCSweepStart +0xa8e98: bad use of trace.seqlock +0xa8eb0: connection reset by peer +0xa8ec8: level 2 not synchronized +0xa8ee0: link number out of range +0xa8ef8: out of streams resources +0xa8f10: function not implemented +0xa8f28: structure needs cleaning +0xa8f40: not supported by windows +0xa8f58: CertFreeCertificateChain +0xa8f70: CreateToolhelp32Snapshot +0xa8f88: GetUserProfileDirectoryW +0xa8fa0: 2910383045673370361328125 +0xa8fb9: GetFinalPathNameByHandleW +0xa8fd2: goroutine profile cleanup +0xa8feb: chansend: spurious wakeup +0xa9004: runtime·lock: lock count +0xa901d: bad system huge page size +0xa9036: arena already initialized +0xa904f: to unused region of span +0xa9068: bytes failed with errno= +0xa9081: runtime: VirtualAlloc of +0xa909a: remaining pointer buffers +0xa90b3: slice bounds out of range +0xa90cc: _cgo_thread_start missing +0xa90e5: allgadd: bad status Gidle +0xa90fe: runtime: program exceeds +0xa9117: startm: p has runnable gs +0xa9130: stoplockedm: not runnable +0xa9149: releasep: invalid p state +0xa9162: checkdead: no p for timer +0xa917b: checkdead: no m for timer +0xa9194: unknown sigtramp callback +0xa91ad: unexpected fault address +0xa91c6: missing stack in newstack +0xa91df: bad status in shrinkstack +0xa91f8: missing traceGCSweepStart +0xa9211: resource deadlock avoided +0xa922a: operation now in progress +0xa9243: no buffer space available +0xa925c: no such device or address +0xa9275: socket type not supported +0xa928e: invalid cross-device link +0xa92a7: GetQueuedCompletionStatus +0xa92c0: UpdateProcThreadAttribute +0xa92d9: inconsistent poll.fdMutex +0xa92f2: GODEBUG: can not enable " +0xa930b: ExpandEnvironmentStringsW +0xa9324: 14551915228366851806640625 +0xa933e: 72759576141834259033203125 +0xa9358: GetFileInformationByHandle +0xa9372: unknown ABI parameter kind +0xa938c: SetFileInformationByHandle +0xa93a6: all goroutines stack trace +0xa93c0: call from unknown function +0xa93da: notewakeup - double wakeup +0xa93f4: persistentalloc: size == 0 +0xa940e: negative idle mark workers +0xa9428: use of invalid sweepLocker +0xa9442: runtime: bad span s.state= +0xa945c: freedefer with d.fn != nil +0xa9476: forEachP: P did not run fn +0xa9490: wakep: negative nmspinning +0xa94aa: startlockedm: locked to me +0xa94c4: entersyscall inconsistent +0xa94de: inittask with no functions +0xa94f8: corrupted semaphore ticket +0xa9512: out of memory (stackalloc) +0xa952c: shrinking stack in libcall +0xa9546: runtime: pcHeader: magic= +0xa9560: traceRegion: out of memory +0xa957a: invalid request descriptor +0xa9594: no CSI structure available +0xa95ae: required key not available +0xa95c8: no message of desired type +0xa95e2: name not unique on network +0xa95fc: CertFreeCertificateContext +0xa9616: PostQueuedCompletionStatus +0xa9630: 363797880709171295166015625 +0xa964b: reflect.Value.UnsafePointer +0xa9666: PageCachePagesLeaked (test) +0xa9681: makechan: size out of range +0xa969c: G waiting list is corrupted +0xa96b7: runtime·unlock: lock count +0xa96d2: progToPointerMask: overflow +0xa96ed: failed to set sweep barrier +0xa9708: work.nwait was > work.nproc +0xa9723: not in stack roots range [ +0xa973e: allocated pages below zero? +0xa9759: address not a stack address +0xa9774: mspan.sweep: bad span state +0xa978f: invalid profile bucket type +0xa97aa: runtime: corrupted polldesc +0xa97c5: runtime: netpollinit failed +0xa97e0: runtime: asyncPreemptStack= +0xa97fb: runtime: thread ID overflow +0xa9816: stopTheWorld: holding locks +0xa9831: gcstopm: not waiting for gc +0xa984c: internal lockOSThread error +0xa9867: runtime: checkdead: nmidle= +0xa9882: runtime: checkdead: find g +0xa989d: runlock of unlocked rwmutex +0xa98b8: sigsend: inconsistent state +0xa98d3: makeslice: len out of range +0xa98ee: makeslice: cap out of range +0xa9909: growslice: len out of range +0xa9924: stack size not a power of 2 +0xa993f: too many callback functions +0xa995a: timer when must be positive +0xa9975: : unexpected return pc for +0xa9990: channel number out of range +0xa99ab: communication error on send +0xa99c6: key was rejected by service +0xa99e1: not a XENIX named type file +0xa99fc: CertEnumCertificatesInStore +0xa9a17: abi.NewName: tag too long: +0xa9a32: 1818989403545856475830078125 +0xa9a4e: 9094947017729282379150390625 +0xa9a6a: GetFileInformationByHandleEx +0xa9a86: comparing uncomparable type +0xa9aa2: runtime: bad lfnode address +0xa9abe: region exceeds uintptr range +0xa9ada: gcBgMarkWorker: mode not set +0xa9af6: mspan.sweep: m is not locked +0xa9b12: found pointer to free object +0xa9b2e: mheap.freeSpanLocked - span +0xa9b4a: runtime.semasleep unexpected +0xa9b66: fatal: morestack on gsignal + +0xa9b82: runtime: casgstatus: oldval= +0xa9b9e: gcstopm: negative nmspinning +0xa9bba: findrunnable: netpoll with p +0xa9bd6: save on system g not allowed +0xa9bf2: newproc1: newg missing stack +0xa9c0e: newproc1: new g is not Gdead +0xa9c2a: FixedStack is not power-of-2 +0xa9c46: missing stack in shrinkstack +0xa9c62: args stack map entries for +0xa9c7e: invalid runtime symbol table +0xa9c9a: runtime: no module data for +0xa9cb6: traceRegion: alloc too large +0xa9cd2: [originating from goroutine +0xa9cee: file descriptor in bad state +0xa9d0a: destination address required +0xa9d26: protocol driver not attached +0xa9d42: CertCreateCertificateContext +0xa9d5e: abi.NewName: name too long: +0xa9d7a: 45474735088646411895751953125 +0xa9d97: GetVolumeInformationByHandleW +0xa9db4: executing on Go runtime stack +0xa9dd1: notesleep - waitm out of sync +0xa9dee: gc done but gcphase != _GCoff +0xa9e0b: runtime: p.gcMarkWorkerMode= +0xa9e28: scanobject of a noscan object +0xa9e45: runtime: marking free object +0xa9e62: addspecial on invalid pointer +0xa9e7f: runtime: summary max pages = +0xa9e9c: runtime: levelShift[level] = +0xa9eb9: doRecordGoroutineProfile gp1= +0xa9ed6: timeBegin/EndPeriod not found +0xa9ef3: runtime: sudog with non-nil c +0xa9f10: gfput: bad status (not Gdead) +0xa9f2d: semacquire not on the G stack +0xa9f4a: runtime: split stack overflow +0xa9f67: string concatenation too long +0xa9f84: invalid function symbol table +0xa9fa1: runtime: traceback stuck. pc= +0xa9fbe: runtime: impossible type kind +0xa9fdb: runtime.semasleep wait_failed +0xa9ff8: operation already in progress +0xaa015: no XENIX semaphores available +0xaa032: too many open files in system +0xaa04f: machine is not on the network +0xaa06c: protocol family not supported +0xaa089: numerical result out of range +0xaa0a6: DeleteProcThreadAttributeList +0xaa0c3: 227373675443232059478759765625 +0xaa0e1: reflect: Elem of invalid type +0xaa0ff: MapIter.Key called before Next +0xaa11d: sync: inconsistent mutex state +0xaa13b: sync: unlock of unlocked mutex +0xaa159: reflect: Len of non-array type +0xaa177: runtime: cgocallback with sp= +0xaa195: runtime: bad g in cgocallback + +0xaa1b3: (types from different scopes) +0xaa1d1: notetsleep - waitm out of sync +0xaa1ef: failed to get system page size +0xaa20d: assignment to entry in nil map +0xaa22b: runtime: found in object at *( +0xaa249: in prepareForSweep; sweepgen +0xaa267: bcryptprimitives.dll not found +0xaa285: panic called with nil argument +0xaa2a3: checkdead: inconsistent counts +0xaa2c1: runqputslow: queue is not full +0xaa2df: runtime: bad pointer in frame +0xaa2fd: invalid pointer found on stack +0xaa31b: locals stack map entries for +0xaa339: abi mismatch detected between +0xaa357: runtime: impossible type kind +0xaa375: unsafe.Slice: len out of range +0xaa393: socket operation on non-socket +0xaa3b1: inappropriate ioctl for device +0xaa3cf: protocol wrong type for socket +0xaa3ed: GODEBUG: unknown cpu feature " +0xaa40b: fmt: unknown base; can't happen +0xaa42a: 1136868377216160297393798828125 +0xaa449: 5684341886080801486968994140625 +0xaa468: reflect: Len of non-array type +0xaa487: slice bounds out of range [:%x] +0xaa4a6: slice bounds out of range [%x:] +0xaa4c5: call from within the Go runtime +0xaa4e4: internal error - misuse of itab +0xaa503: ) not in usable address space: +0xaa522: runtime: cannot allocate memory +0xaa541: checkmark found unmarked object +0xaa560: runtime: failed to commit pages +0xaa57f: pacer: sweep done at heap size +0xaa59e: non in-use span in unswept list +0xaa5bd: casgstatus: bad incoming values +0xaa5dc: resetspinning: not a spinning m +0xaa5fb: entersyscallblock inconsistent +0xaa61a: runtime: split stack overflow: +0xaa639: ...additional frames elided... + +0xaa658: unsafe.String: len out of range +0xaa677: cannot assign requested address +0xaa696: .lib section in a.out corrupted +0xaa6b5: 28421709430404007434844970703125 +0xaa6d5: MapIter.Value called before Next +0xaa6f5: slice bounds out of range [::%x] +0xaa715: slice bounds out of range [:%x:] +0xaa735: slice bounds out of range [%x::] +0xaa755: (types from different packages) +0xaa775: end outside usable address space +0xaa795: GCProg for type that isn't large +0xaa7b5: runtime: failed to release pages +0xaa7d5: runtime: fixalloc size too large +0xaa7f5: invalid limiter event type found +0xaa815: scanstack: goroutine not stopped +0xaa835: scavenger state is already wired +0xaa855: sweep increased allocation count +0xaa875: removespecial on invalid pointer +0xaa895: runtime: root level max pages = +0xaa8b5: WSAGetOverlappedResult not found +0xaa8d5: _cgo_pthread_key_created missing +0xaa8f5: runtime: sudog with non-nil elem +0xaa915: runtime: sudog with non-nil next +0xaa935: runtime: sudog with non-nil prev +0xaa955: runtime: mcall function returned +0xaa975: runtime: newstack called from g= +0xaa995: runtime: stack split at bad time +0xaa9b5: panic while printing panic value +0xaa9d5: runtime: setevent failed; errno= +0xaa9f5: runtime.semasleep wait_abandoned +0xaaa15: resource temporarily unavailable +0xaaa35: software caused connection abort +0xaaa55: numerical argument out of domain +0xaaa75: CertAddCertificateContextToStore +0xaaa95: CertVerifyCertificateChainPolicy +0xaaab5: use of closed network connection +0xaaad5: " not supported for cpu option " +0xaaaf5: 142108547152020037174224853515625 +0xaab16: 710542735760100185871124267578125 +0xaab37: reflect: slice index out of range +0xaab58: of method on nil interface value +0xaab79: reflect: Field index out of range +0xaab9a: reflect: array index out of range +0xaabbb: GetVolumeNameForVolumeMountPointW +0xaabdc: slice bounds out of range [%x:%y] +0xaabfd: base outside usable address space +0xaac1e: runtime: memory allocated by OS [ +0xaac3f: misrounded allocation in sysAlloc +0xaac60: concurrent map read and map write +0xaac81: runtime: failed to decommit pages +0xaaca2: min must be a non-zero power of 2 +0xaacc3: runtime: failed mSpanList.insert +0xaace4: runtime: castogscanstatus oldval= +0xaad05: stoplockedm: inconsistent locking +0xaad26: findrunnable: negative nmspinning +0xaad47: freeing stack not in a stack span +0xaad68: stackalloc not on scheduler stack +0xaad89: runtime: goroutine stack exceeds +0xaadaa: runtime: text offset out of range +0xaadcb: timer period must be non-negative +0xaadec: runtime: name offset out of range +0xaae0d: runtime: type offset out of range +0xaae2e: too many levels of symbolic links +0xaae4f: InitializeProcThreadAttributeList +0xaae70: waiting for unsupported file type +0xaae91: GODEBUG: no value specified for " +0xaaeb2: 3552713678800500929355621337890625 +0xaaed4: reflect: Field of non-struct type +0xaaef6: reflect: Field index out of bounds +0xaaf18: reflect: string index out of range +0xaaf3a: slice bounds out of range [:%x:%y] +0xaaf5c: slice bounds out of range [%x:%y:] +0xaaf7e: out of memory allocating allArenas +0xaafa0: runtime.SetFinalizer: cannot pass +0xaafc2: too many pages allocated in chunk? +0xaafe4: mspan.ensureSwept: m is not locked +0xab006: VirtualQuery for stack base failed +0xab028: forEachP: sched.safePointWait != 0 +0xab04a: schedule: spinning with local work +0xab06c: runtime: g is running but p is not +0xab08e: doaddtimer: P already set in timer +0xab0b0: too many references: cannot splice +0xab0d2: SetFileCompletionNotificationModes +0xab0f4: unexpected runtime.netpoll error: +0xab116: 17763568394002504646778106689453125 +0xab139: 88817841970012523233890533447265625 +0xab15c: ryuFtoaFixed32 called with prec > 9 +0xab17f: persistentalloc: align is too large +0xab1a2: greyobject: obj not pointer-aligned +0xab1c5: mismatched begin/end of activeSweep +0xab1e8: mheap.freeSpanLocked - invalid free +0xab20b: attempt to clear non-empty span set +0xab22e: runtime: close polldesc w/o unblock +0xab251: findrunnable: netpoll with spinning +0xab274: pidleput: P has non-empty run queue +0xab297: traceback did not unwind completely +0xab2ba: runtime: createevent failed; errno= +0xab2dd: network dropped connection on reset +0xab300: transport endpoint is not connected +0xab323: file type does not support deadline +0xab346: 444089209850062616169452667236328125 +0xab36a: ryuFtoaFixed64 called with prec > 18 +0xab38e: 0123456789abcdefghijklmnopqrstuvwxyz +0xab3b2: method ABI and value ABI don't align +0xab3d6: lfstack node allocated from the heap +0xab3fa: ) is larger than maximum page size ( +0xab41e: runtime: invalid typeBitsBulkBarrier +0xab442: uncaching span but s.allocCount == 0 +0xab466: user arena span is on the wrong list +0xab48a: runtime: marked free object in span +0xab4ae: runtime: unblock on closing polldesc +0xab4d2: Unable to determine system directory +0xab4f6: runtime: VirtualQuery failed; errno= +0xab51a: runtime: sudog with non-nil waitlink +0xab53e: runtime: mcall called on m->g0 stack +0xab562: startm: P required for spinning=true +0xab586: ) is not Grunnable or Gscanrunnable + +0xab5aa: runtime: bad notifyList size - sync= +0xab5ce: accessed data from freed user arena +0xab5f2: runtime: wrong goroutine in newstack +0xab616: runtime: invalid pc-encoded table f= +0xab63a: accessing a corrupted shared library +0xab65e: 2220446049250313080847263336181640625 +0xab683: reflect: funcLayout of non-func type +0xab6a8: reflect.Value.Bytes of non-byte slice +0xab6cd: reflect.Value.Bytes of non-byte array +0xab6f2: method ABI and value ABI do not align +0xab717: runtime: allocation size out of range +0xab73c: ) is smaller than minimum page size ( +0xab761: setprofilebucket: profile already set +0xab786: failed to reserve page summary memory +0xab7ab: runtime.minit: duplicatehandle failed +0xab7d0: _cgo_notify_runtime_init_done missing +0xab7f5: startTheWorld: inconsistent mp->nextp +0xab81a: runtime: unexpected SPWRITE function +0xab83f: all goroutines are asleep - deadlock! +0xab864: cannot exec a shared library directly +0xab889: value too large for defined data type +0xab8ae: internal error: unknown network type +0xab8d3: 11102230246251565404236316680908203125 +0xab8f9: 55511151231257827021181583404541015625 +0xab91f: index out of range [%x] with length %y +0xab945: m changed unexpectedly in cgocallbackg +0xab96b: makechan: invalid channel element type +0xab991: internal error: exit hook invoked exit +0xab9b7: unreachable method called. linker bug? +0xab9dd: concurrent map iteration and map write +0xaba03: gcBgMarkWorker: blackening not enabled +0xaba29: cannot read stack of running goroutine +0xaba4f: runtime: blocked read on free polldesc +0xaba75: runtime: sudog with non-false isSelect +0xaba9b: arg size to reflect.call more than 1GB +0xabac1: v could not fit in traceBytesPerNumber +0xabae7: can not access a needed shared library +0xabb0d: 277555756156289135105907917022705078125 +0xabb34: internal error: exit hook invoked panic +0xabb5b: mismatched count during itab table copy +0xabb82: out of memory allocating heap arena map +0xabba9: mspan.sweep: bad span state after sweep +0xabbd0: runtime: blocked write on free polldesc +0xabc1e: suspendG from non-preemptible goroutine +0xabc45: runtime: casfrom_Gscanstatus failed gp= +0xabc6c: stack growth not allowed in system call +0xabc93: traceback: unexpected SPWRITE function +0xabcba: transport endpoint is already connected +0xabce1: 1387778780781445675529539585113525390625 +0xabd09: 6938893903907228377647697925567626953125 +0xabd31: ryuFtoaFixed32 called with negative prec +0xabd59: MapIter.Key called on exhausted iterator +0xabd81: invalid span in heapArena for user arena +0xabda9: runtime: typeBitsBulkBarrier with type +0xabdd1: bulkBarrierPreWrite: unaligned arguments +0xabdf9: refill of span with free space remaining +0xabe21: runtime.SetFinalizer: first argument is +0xabe49: failed to acquire lock to reset capacity +0xabe71: markWorkerStop: unknown mark worker mode +0xabe99: cannot free workbufs when work.full != 0 +0xabec1: runtime: out of memory: cannot allocate +0xabee9: runtime.preemptM: duplicatehandle failed +0xabf11: global runq empty with non-zero runqsize +0xabf39: must be able to track idle limiter event +0xabf61: runtime: SyscallN has too many arguments +0xabf89: address family not supported by protocol +0xabfb1: 34694469519536141888238489627838134765625 +0xabfda: strconv: illegal AppendInt/FormatInt base +0xac003: can't call pointer on a non-pointer Value +0xac02c: MapIter.Next called on exhausted iterator +0xac055: runtime: typeBitsBulkBarrier without type +0xac07e: runtime.SetFinalizer: second argument is +0xac0a7: gcSweep being done but phase is not GCoff +0xac0d0: objects added out of order or overlapping +0xac0f9: mheap.freeSpanLocked - invalid stack free +0xac122: mheap.freeSpanLocked - invalid span state +0xac14b: attempted to add zero-sized address range +0xac174: runtime: blocked read on closing polldesc +0xac19d: stopTheWorld: not stopped (stopwait != 0) +0xac1c6: 173472347597680709441192448139190673828125 +0xac1f0: 867361737988403547205962240695953369140625 +0xac21a: MapIter.Value called on exhausted iterator +0xac244: persistentalloc: align is not a power of 2 +0xac26e: out of memory allocating checkmarks bitmap +0xac298: non-empty mark queue after concurrent mark +0xac2c2: sweep: tried to preserve a user arena span +0xac2ec: runtime: blocked write on closing polldesc +0xac316: acquireSudog: found s.elem != nil in cache +0xac340: fatal error: cgo callback before cgo call + +0xac36a: on a locked thread with no template thread +0xac394: unexpected signal during runtime execution +0xac3be: attempted to trace a bad status for a proc +0xac3e8: mult64bitPow10: power of 10 is out of range +0xac413: runtime.SetFinalizer: first argument is nil +0xac43e: runtime.SetFinalizer: finalizer already set +0xac469: gcBgMarkWorker: unexpected gcMarkWorkerMode +0xac494: non in-use span found with specials bit set +0xac4bf: grew heap, but no adequate free space found +0xac4ea: root level max pages doesn't fit in summary +0xac515: runtime: releaseSudog with non-nil gp.param +0xac540: unknown runnable goroutine during bootstrap +0xac56b: runtime: casfrom_Gscanstatus bad oldval gp= +0xac596: runtime:stoplockedm: lockedg (atomicstatus= +0xac5c1: methodValueCallFrameObjs is not in a module +0xac5ec: interrupted system call should be restarted +0xac617: mult128bitPow10: power of 10 is out of range +0xac643: reflect: funcLayout with interface receiver +0xac66f: span on userArena.faultList has invalid size +0xac69b: runtime: lfstack.push invalid packing: node= +0xac6c7: out of memory allocating heap arena metadata +0xac6f3: gcmarknewobject called while doing checkmark +0xac71f: active sweepers found at start of mark phase +0xac74b: no P available, write barriers are forbidden +0xac777: compileCallback: float results not supported +0xac7a3: cannot trace user goroutine on its own stack +0xac7cf: unsafe.Slice: ptr is nil and len is not zero +0xac7fb: reflect: internal error: invalid method index +0xac828: transitioning GC to the same state as before? +0xac855: produced a trigger greater than the heap goal +0xac882: tried to run scavenger from another goroutine +0xac8af: runtime: failed mSpanList.remove span.npages= +0xac8dc: runtime.minit: duplicatehandle failed; errno= +0xac909: runtime: CreateWaitableTimerEx failed; errno= +0xac936: exitsyscall: syscall frame is no longer valid +0xac963: unsafe.String: ptr is nil and len is not zero +0xac990: cannot send after transport endpoint shutdown +0xac9bd: slice bounds out of range [:%x] with length %y +0xac9eb: panicwrap: unexpected string after type name: +0xaca19: memory reservation exceeds address space limit +0xaca47: tried to park scavenger from another goroutine +0xaca75: released less than one physical page of memory +0xacaa3: (bad use of unsafe.Pointer? try -d=checkptr) + +0xacad1: sysGrow bounds not aligned to pallocChunkBytes +0xacaff: runtime: failed to create new OS thread (have +0xacb2d: runtime: panic before malloc heap initialized + +0xacb5b: stopTheWorld: not stopped (status != _Pgcstop) +0xacb89: signal arrived during external code execution + +0xacbb7: compileCallback: float arguments not supported +0xacbe5: runtime: name offset base pointer out of range +0xacc13: runtime: type offset base pointer out of range +0xacc41: runtime: text offset base pointer out of range +0xacc6f: unexpected error wrapping poll.ErrFileClosing: +0xacc9e: reflect.Value.Bytes of unaddressable byte array +0xacccd: slice bounds out of range [::%x] with length %y +0xaccfc: P has cached GC work at end of mark termination +0xacd2b: failed to acquire lock to start a GC transition +0xacd5a: finishGCTransition called without starting one? +0xacd89: tried to sleep scavenger from another goroutine +0xacdb8: runtime: CreateIoCompletionPort failed (errno= +0xacde7: racy sudog adjustment due to parking on channel +0xace16: function symbol table not sorted by PC offset: +0xace45: attempted to trace a bad status for a goroutine +0xace74: attempting to link in too many shared libraries +0xacea3: strconv: illegal AppendFloat/FormatFloat bitSize +0xaced3: not enough significant bits after mult64bitPow10 +0xacf03: slice bounds out of range [:%x] with capacity %y +0xacf33: runtime: waitforsingleobject unexpected; result= +0xacf63: CreateWaitableTimerEx when creating timer failed +0xacf93: runtime.preemptM: duplicatehandle failed; errno= +0xacfc3: runtime: waitforsingleobject wait_failed; errno= +0xacff3: not enough significant bits after mult128bitPow10 +0xad024: slice bounds out of range [::%x] with capacity %y +0xad055: invalid memory address or nil pointer dereference +0xad086: panicwrap: unexpected string after package name: +0xad0b7: runtime: unexpected waitm - semaphore out of sync +0xad0e8: s.allocCount != s.nelems && freeIndex == s.nelems +0xad119: delayed zeroing on data that may contain pointers +0xad14a: sweeper left outstanding across sweep generations +0xad17b: fully empty unfreed span set block found in reset +0xad1ac: casgstatus: waiting for Gwaiting but is Grunnable +0xad1dd: invalid or incomplete multibyte or wide character +0xad20e: runtime: unable to acquire - semaphore out of sync +0xad240: mallocgc called with gcphase == _GCmarktermination +0xad272: recursive call during initialization - linker skew +0xad2a4: attempt to execute system stack code on user stack +0xad2d6: compileCallback: function argument frame too large +0xad308: limiterEvent.stop: invalid limiter event type found +0xad33b: potentially overlapping in-use allocations detected +0xad36e: runtime: netpoll: PostQueuedCompletionStatus failed +0xad3a1: fatal: systemstack called from unexpected goroutine +0xad3d4: mallocgc called without a P or outside bootstrapping +0xad408: runtime.SetFinalizer: pointer not in allocated block +0xad43c: runtime: use of FixAlloc_Alloc before FixAlloc_Init + +0xad470: span set block with unpopped elements found in reset +0xad4a4: runtime: GetQueuedCompletionStatusEx failed (errno= +0xad4d8: casfrom_Gscanstatus: gp->status is not in scan state +0xad50c: non-concurrent sweep failed to drain all sweep queues +0xad541: compileCallback: argument size is larger than uintptr +0xad576: min size of malloc header is not a size class boundary +0xad5ac: gcControllerState.findRunnable: blackening not enabled +0xad5e2: no goroutines (main called runtime.Goexit) - deadlock! +0xad618: goroutine running on other thread; stack unavailable + +0xad64e: internal error: polling on unsupported descriptor type +0xad684: reflect: internal error: invalid use of makeMethodValue +0xad6bb: mheap.freeSpanLocked - invalid free of user arena chunk +0xad6f2: casfrom_Gscanstatus:top gp->status is not in scan state +0xad729: is currently not supported for use in system callbacks +0xad760: non-empty pointer map passed for non-pointer-size values +0xad798: failed to allocate aligned heap memory; too many retries +0xad7d0: profilealloc called without a P or outside bootstrapping +0xad808: in gcMark expecting to see gcphase as _GCmarktermination +0xad840: runtime: checkmarks found unexpected unmarked object obj= +0xad879: reflect: reflect.Value.Elem on an invalid notinheap pointer +0xad8b4: unexpected malloc header in delayed zeroing of large object +0xad8ef: reflect: call of reflect.Value.Len on ptr to non-array Value +0xad92b: manual span allocation called with non-manually-managed type +0xad967: addr range base and limit are not in the same memory segment +0xad9a3: runtime: netpoll: PostQueuedCompletionStatus failed (errno= +0xad9df: runtime: GetQueuedCompletionStatusEx returned invalid mode= +0xada1b: abiRegArgsType needs GC Prog, update methodValueCallFrameObjs +0xada58: reflect: reflect.Value.Pointer on an invalid notinheap pointer +0xada96: found bad pointer in Go heap (incorrect use of unsafe or cgo?) +0xadad4: limiterEvent.stop: found wrong event in p's limiter event slot +0xadb12: runtime: internal error: misuse of lockOSThread/unlockOSThread +0xadb50: malformed GOMEMLIMIT; see `go doc runtime/debug.SetMemoryLimit` +0xadb8f: runtime.SetFinalizer: first argument was allocated into an arena +0xadbcf: compileCallback: expected function with one uintptr-sized result +0xadc0f: user arena chunk size is not a multiple of the physical page size +0xadc50: runtime: function marked with #cgo nocallback called back into Go +0xadc91: runtime.SetFinalizer: pointer not at beginning of allocated block +0xadcd2: reflect: reflect.Value.UnsafePointer on an invalid notinheap pointer +0xadd16: too many concurrent operations on a single file or socket (max 1048575) +0xadd5d: MapIter.Next called on an iterator that does not have an associated map Value +0xaddaa: cannot convert slice with length %y to array or pointer to array with length %x +0xaddf9: reflect.Value.Interface: cannot return value obtained from unexported field or method +0xade4e: runtime: warning: IsLongPathAwareProcess failed to enable long paths; proceeding in fixup mode + +0xadead: cgocheck > 1 mode is no longer supported at runtime. Use GOEXPERIMENT=cgocheck2 at build time instead. +0xadf13: 00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899