Skip to content

runtime/cgo: improve error message if a Go function called by C returns an unpinned pointer #75856

@ariel-anieli

Description

@ariel-anieli

Go version

go version go1.25.1 X:nodwarf5 linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/root/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/root/.config/go/env'
GOEXE=''
GOEXPERIMENT='nodwarf5'
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1863904488=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/mnt/chexit/go.mod'
GOMODCACHE='/root/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/root/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/root/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/lib/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.25.1 X:nodwarf5'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

Hello maintainers,

Thanks for the great work you guys are doing. Let me know if I could be of any help; I am eager to learn about the internals of Go compilation.

I am calling a Go binary from python using cffi. The binary got compiled though it does not follow the SystemV convention.

For instance, using string as output of a function exported through a shared library.

package main

import (
        "C"
)

//export foo
func foo(CLine *C.char) string {
        return C.GoString(CLine)
}


func main() {
}

The compilation emits no warning:

# go build -buildmode=c-shared -o parse.so parse.go; echo $?
0

Only when Python called the function did an error appear:

# .venv/bin/python test.py
panic: runtime error: cgo result is unpinned Go pointer or points to unpinned Go pointer

goroutine 17 [running, locked to thread]:
panic({0x7e7ca113c360?, 0xc00021c000?})
        /usr/lib/go/src/runtime/panic.go:802 +0x168
runtime.cgoCheckArg(0x7e7ca1135620, 0xc000066e50, 0x0?, 0x0, {0x7e7ca11021ca, 0x42})
        /usr/lib/go/src/runtime/cgocall.go:679 +0x35b
runtime.cgoCheckResult({0x7e7ca1135620, 0xc000066e50})
        /usr/lib/go/src/runtime/cgocall.go:795 +0x4b
_cgoexp_d14a34698a2e_foo(0x7ffe00692b40)
        _cgo_gotypes.go:65 +0x5d
runtime.cgocallbackg1(0x7e7ca10f65e0, 0x7ffe00692b40, 0x0)
        /usr/lib/go/src/runtime/cgocall.go:446 +0x289
runtime.cgocallbackg(0x7e7ca10f65e0, 0x7ffe00692b40, 0x0)
        /usr/lib/go/src/runtime/cgocall.go:350 +0x132
runtime.cgocallbackg(0x7e7ca10f65e0, 0x7ffe00692b40, 0x0)
        <autogenerated>:1 +0x2b
runtime.cgocallback(0x0, 0x0, 0x0)
        /usr/lib/go/src/runtime/asm_amd64.s:1082 +0xcd
runtime.goexit({})
        /usr/lib/go/src/runtime/asm_amd64.s:1693 +0x1

Here the Python script,

import os
from cffi import FFI

# FFI
cffi = FFI()
cffi.cdef("char* foo(char* lookup);")
dll = cffi.dlopen(os.path.abspath("parse.so"))

dll.foo("bar".encode())
cffi.dlclose(dll)

What did you see happen?

The compilation emits no warning:

# go build -buildmode=c-shared -o parse.so parse.go; echo $?
0

What did you expect to see?

The compiler could emit a warning, as the Zig compiler does:

const std = @import("std");

pub export fn foo(line: []const u8) []const u8 {
    return line;
}

And it does ease the debugging:

# zig build-lib -dynamic test.zig
src/string.zig:3:19: error: parameter of type '[]const u8' not allowed in function with calling convention 'x86_64_sysv'
pub export fn foo(line: []const u8) []const u8 {
                  ^~~~~~~~~~~~~~~~
src/string.zig:3:19: note: slices have no guaranteed in-memory representation
referenced by:
    root: /usr/lib/zig/std/start.zig:3:22
    comptime: /usr/lib/zig/std/start.zig:31:9
    2 reference(s) hidden; use '-freference-trace=4' to see all references

# zig version 
0.15.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsFixThe path to resolution is known, but the work has not been done.compiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type

    Projects

    Status

    Todo

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions