golang/go · error

symbol %s: invalid section number %d

Error message

symbol %s: invalid section number %d

What it means

In `findPESymbol`, when iterating the PE symbol table, each symbol's `SectionNumber` must be positive (> 0) to reference a real section. Values ≤ 0 have special meaning (N_UNDEF = 0, N_ABS = -1, N_DEBUG = -2) and do not point to a section. This error fires when `findPESymbol` is looking up a specific named symbol that has one of these special section numbers, making it impossible to resolve its section-relative address.

Source

Thrown at src/cmd/internal/objfile/pe.go:159

		return 0, nil, err
	}

	sect := f.pe.Section(".text")
	if sect == nil {
		return 0, nil, fmt.Errorf("text section not found")
	}
	textStart = imageBase + uint64(sect.VirtualAddress)
	text, err = sect.Data()
	return
}

func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
	for _, s := range f.Symbols {
		if s.Name != name {
			continue
		}
		if s.SectionNumber <= 0 {
			return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
		}
		if len(f.Sections) < int(s.SectionNumber) {
			return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
		}
		return s, nil
	}
	return nil, fmt.Errorf("no %s symbol found", name)
}

func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
	ssym, err := findPESymbol(f, sname)
	if err != nil {
		return nil, err
	}
	esym, err := findPESymbol(f, ename)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Rebuild the binary without symbol stripping: remove `-ldflags=-s -w`.
  2. Verify the symbol exists and has a valid section: `dumpbin /symbols <file> | grep pclntab`.
  3. Ensure the Go linker version matches the compiler version.
  4. If the binary must be stripped, accept that some symbol lookups will fail and handle the error gracefully.

Example fix

// before — binary built with stripping
$ go build -ldflags="-s -w" -o app.exe
// findPESymbol("runtime.pclntab") fails: SectionNumber <= 0

// after — build without stripping for analysis
$ go build -o app.exe
// findPESymbol("runtime.pclntab") succeeds
Defensive patterns

Strategy: validation

Validate before calling

// Check symbol section number validity before lookup
func findPESymbolSafe(f *pe.File, name string) (*pe.Symbol, error) {
    for _, s := range f.Symbols {
        if s.Name != name { continue }
        if s.SectionNumber <= 0 {
            return nil, fmt.Errorf("symbol %s has special section number %d (undefined/absolute/debug)", name, s.SectionNumber)
        }
        if int(s.SectionNumber) > len(f.Sections) {
            return nil, fmt.Errorf("symbol %s section %d exceeds section count %d", name, s.SectionNumber, len(f.Sections))
        }
        return s, nil
    }
    return nil, fmt.Errorf("symbol %s not found", name)
}

Try / catch

sym, err := findPESymbol(pe, "runtime.pclntab")
if err != nil {
    if strings.Contains(err.Error(), "invalid section number") {
        // Symbol is undefined/debug — binary may be stripped
        return fmt.Errorf("runtime symbol is undefined — rebuild without -ldflags=-s")
    }
    return err
}

Prevention

When it happens

Trigger: Calling `findPESymbol` (internally during `loadPETable` or `pcln`) for a symbol that exists in the PE symbol table but has `SectionNumber <= 0`, indicating it is undefined, absolute, or a debug symbol. This is common when looking for runtime tables (e.g., `runtime.pclntab`) in a binary where those symbols are marked as undefined.

Common situations: Analyzing a Windows `.exe` or `.dll` where the Go runtime tables were stripped or marked undefined. Linking issues where the linker did not resolve all symbols. Examining a PE binary compiled with `-ldflags=-s` that strips symbol information. Using a PE binary built by an older or non-standard Go linker.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/07e660f487bbc41e. Report an issue: GitHub.