golang/go · error

: invalid symbol binding

Error message

%s: invalid symbol binding %d

What it means

The Go linker's PE (Windows) object loader encountered a COFF symbol whose StorageClass value does not match any of the recognized classes (IMAGE_SYM_CLASS_EXTERNAL, IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL). The symbol's storage class determines how it is bound during linking; an unrecognized class means the linker cannot decide whether to treat it as a global, local, or label symbol. This error surfaces the raw numeric storage class value so the offending symbol and its provenance can be identified.

Solutions

  1. Inspect the exact storage class number in the error output and cross-reference it with the PE/COFF IMAGE_SYM_CLASS_* constants in debug/pe to understand what symbol type is being emitted.
  2. Update to the latest Go release — support for additional storage classes is added over time; check the Go issue tracker for your specific storage class value.
  3. Recompile the offending C/object file with a different toolchain (e.g., switch between MSVC and MinGW-w64, or update to a newer compiler version) to emit standard storage classes.
  4. File a bug at https://go.dev/issue with the object file, the symbol name, and the storage class value so the linker can be extended to handle it.
  5. If you control the C source, mark symbols explicitly as `extern` (IMAGE_SYM_CLASS_EXTERNAL) or `static` (IMAGE_SYM_CLASS_STATIC) to force a recognized storage class.

Example fix

// C source: ensure symbols use standard storage classes
// before (weak symbol — may emit IMAGE_SYM_CLASS_WEAK_EXTERNAL):
__attribute__((weak)) int my_symbol(void) { return 0; }

// after (force a recognized storage class):
int my_symbol(void) { return 0; }
Defensive patterns

Strategy: validation

Validate before calling

// Before linking, inspect object files for unsupported storage classes
// This is a linker-internal check; end users validate by inspecting .obj files
// Use dumpbin (MSVC) or objdump (MinGW) to review symbol storage classes:
//   dumpbin /symbols file.obj
//   objdump -t file.obj
// Look for storage class values outside EXTERNAL(2), NULL(3), STATIC(3), LABEL(6)

Type guard

// Not applicable — this is an internal linker error with no runtime API surface.
// The PE loader is invoked during go build/link, not callable by user code.

Prevention

When it happens

Trigger: Triggered during `readpesym` when processing a COFF symbol from a PE/COFF object file (typically a .obj or .o produced by an external C/C++ compiler via cgo on Windows). The switch on `pesym.StorageClass` at ldpe.go:735 falls through to the default branch. The symbol's type field bits 4-7 must be FUNCTION or NULL (checked at ldpe.go:730), so this only fires for symbols with a valid complex type but an unsupported storage class.

Common situations: Using cgo on Windows with a C compiler that emits unusual COFF storage classes (e.g., IMAGE_SYM_CLASS_WEAK_EXTERNAL, IMAGE_SYM_CLASS_SECTION, IMAGE_SYM_CLASS_EXTERNAL_DEF). Mixing object files from non-standard toolchains (LLVM/Clang variants, older MSVC, or unusual MinGW builds). Corrupt or truncated object files where the storage class byte is garbage. Upgrading a C compiler that starts emitting symbols the Go linker hasn't been taught to handle.

Related errors


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

Appendix: source

Thrown at src/cmd/link/internal/loadpe/ldpe.go:745

	// Microsoft's PE documentation is contradictory. It says that the symbol's complex type
	// is stored in the pesym.Type most significant byte, but MSVC, LLVM, and mingw store it
	// in the 4 high bits of the less significant byte.
	switch uint8(pesym.Type&0xf0) >> 4 {
	default:
		return nil, 0, fmt.Errorf("%s: invalid symbol type %d", symname, pesym.Type)

	case IMAGE_SYM_DTYPE_FUNCTION, IMAGE_SYM_DTYPE_NULL:
		switch pesym.StorageClass {
		case IMAGE_SYM_CLASS_EXTERNAL: //global
			s = state.l.LookupOrCreateCgoExport(name, 0)

		case IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_LABEL:
			s = state.l.LookupOrCreateCgoExport(name, state.localSymVersion)
			bld = makeUpdater(state.l, bld, s)
			bld.SetDuplicateOK(true)

		default:
			return nil, 0, fmt.Errorf("%s: invalid symbol binding %d", symname, pesym.StorageClass)
		}
	}

	if s != 0 && state.l.SymType(s) == 0 && (pesym.StorageClass != IMAGE_SYM_CLASS_STATIC || pesym.Value != 0) {
		bld = makeUpdater(state.l, bld, s)
		bld.SetType(sym.SXREF)
	}

	return bld, s, nil
}

// preprocessSymbols walks the COFF symbols for the PE file we're
// reading and looks for cases where we have both a symbol definition
// for "XXX" and an "__imp_XXX" symbol, recording these cases in a map
// in the state struct. This information will be used in readpesym()
// above to give such symbols special treatment. This function also
// gathers information about COMDAT sections/symbols for later use
// in readpesym().

View on GitHub (pinned to b6b368adc5)