cgo: mangle identifier names

This mangles CGo identifier names to something like "_Cgo_foo" instead
of using literal identifiers like "C.foo". This works around
https://github.com/golang/go/issues/71777.

I don't like this solution, but I hope we'll find a better solution in
the future. In that case we can revert this commit.
This commit is contained in:
Ayke van Laethem
2025-02-16 14:29:32 +01:00
committed by Ron Evans
parent a6cd072fa1
commit ea53ace270
9 changed files with 367 additions and 383 deletions
+45 -64
View File
@@ -92,18 +92,18 @@ type noescapingFunc struct {
// cgoAliases list type aliases between Go and C, for types that are equivalent
// in both languages. See addTypeAliases.
var cgoAliases = map[string]string{
"C.int8_t": "int8",
"C.int16_t": "int16",
"C.int32_t": "int32",
"C.int64_t": "int64",
"C.uint8_t": "uint8",
"C.uint16_t": "uint16",
"C.uint32_t": "uint32",
"C.uint64_t": "uint64",
"C.uintptr_t": "uintptr",
"C.float": "float32",
"C.double": "float64",
"C._Bool": "bool",
"_Cgo_int8_t": "int8",
"_Cgo_int16_t": "int16",
"_Cgo_int32_t": "int32",
"_Cgo_int64_t": "int64",
"_Cgo_uint8_t": "uint8",
"_Cgo_uint16_t": "uint16",
"_Cgo_uint32_t": "uint32",
"_Cgo_uint64_t": "uint64",
"_Cgo_uintptr_t": "uintptr",
"_Cgo_float": "float32",
"_Cgo_double": "float64",
"_Cgo__Bool": "bool",
}
// builtinAliases are handled specially because they only exist on the Go side
@@ -145,48 +145,46 @@ typedef unsigned long long _Cgo_ulonglong;
// The string/bytes functions below implement C.CString etc. To make sure the
// runtime doesn't need to know the C int type, lengths are converted to uintptr
// first.
// These functions will be modified to get a "C." prefix, so the source below
// doesn't reflect the final AST.
const generatedGoFilePrefixBase = `
import "syscall"
import "unsafe"
var _ unsafe.Pointer
//go:linkname C.CString runtime.cgo_CString
func CString(string) *C.char
//go:linkname _Cgo_CString runtime.cgo_CString
func _Cgo_CString(string) *_Cgo_char
//go:linkname C.GoString runtime.cgo_GoString
func GoString(*C.char) string
//go:linkname _Cgo_GoString runtime.cgo_GoString
func _Cgo_GoString(*_Cgo_char) string
//go:linkname C.__GoStringN runtime.cgo_GoStringN
func __GoStringN(*C.char, uintptr) string
//go:linkname _Cgo___GoStringN runtime.cgo_GoStringN
func _Cgo___GoStringN(*_Cgo_char, uintptr) string
func GoStringN(cstr *C.char, length C.int) string {
return C.__GoStringN(cstr, uintptr(length))
func _Cgo_GoStringN(cstr *_Cgo_char, length _Cgo_int) string {
return _Cgo___GoStringN(cstr, uintptr(length))
}
//go:linkname C.__GoBytes runtime.cgo_GoBytes
func __GoBytes(unsafe.Pointer, uintptr) []byte
//go:linkname _Cgo___GoBytes runtime.cgo_GoBytes
func _Cgo___GoBytes(unsafe.Pointer, uintptr) []byte
func GoBytes(ptr unsafe.Pointer, length C.int) []byte {
return C.__GoBytes(ptr, uintptr(length))
func _Cgo_GoBytes(ptr unsafe.Pointer, length _Cgo_int) []byte {
return _Cgo___GoBytes(ptr, uintptr(length))
}
//go:linkname C.__CBytes runtime.cgo_CBytes
func __CBytes([]byte) unsafe.Pointer
//go:linkname _Cgo___CBytes runtime.cgo_CBytes
func _Cgo___CBytes([]byte) unsafe.Pointer
func CBytes(b []byte) unsafe.Pointer {
return C.__CBytes(b)
func _Cgo_CBytes(b []byte) unsafe.Pointer {
return _Cgo___CBytes(b)
}
//go:linkname C.__get_errno_num runtime.cgo_errno
func __get_errno_num() uintptr
//go:linkname _Cgo___get_errno_num runtime.cgo_errno
func _Cgo___get_errno_num() uintptr
`
const generatedGoFilePrefixOther = generatedGoFilePrefixBase + `
func __get_errno() error {
return syscall.Errno(C.__get_errno_num())
func _Cgo___get_errno() error {
return syscall.Errno(_Cgo___get_errno_num())
}
`
@@ -197,7 +195,7 @@ func __get_errno() error {
// map the errno values to match the values in the syscall package.
// Source of the errno values: lib/mingw-w64/mingw-w64-headers/crt/errno.h
const generatedGoFilePrefixWindows = generatedGoFilePrefixBase + `
var __errno_mapping = [...]syscall.Errno{
var _Cgo___errno_mapping = [...]syscall.Errno{
1: syscall.EPERM,
2: syscall.ENOENT,
3: syscall.ESRCH,
@@ -238,10 +236,10 @@ var __errno_mapping = [...]syscall.Errno{
42: syscall.EILSEQ,
}
func __get_errno() error {
num := C.__get_errno_num()
if num < uintptr(len(__errno_mapping)) {
if mapped := __errno_mapping[num]; mapped != 0 {
func _Cgo___get_errno() error {
num := _Cgo___get_errno_num()
if num < uintptr(len(_Cgo___errno_mapping)) {
if mapped := _Cgo___errno_mapping[num]; mapped != 0 {
return mapped
}
}
@@ -304,23 +302,6 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
// If the Comments field is not set to nil, the go/format package will get
// confused about where comments should go.
p.generated.Comments = nil
// Adjust some of the functions in there.
for _, decl := range p.generated.Decls {
switch decl := decl.(type) {
case *ast.FuncDecl:
switch decl.Name.Name {
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes", "CBytes", "__CBytes", "__get_errno_num", "__get_errno", "__errno_mapping":
// Adjust the name to have a "C." prefix so it is correctly
// resolved.
decl.Name.Name = "C." + decl.Name.Name
}
}
}
// Patch some types, for example *C.char in C.CString.
cf := p.newCGoFile(nil, -1) // dummy *cgoFile for the walker
astutil.Apply(p.generated, func(cursor *astutil.Cursor) bool {
return cf.walker(cursor, nil)
}, nil)
// Find `import "C"` C fragments in the file.
p.cgoHeaders = make([]string, len(files)) // combined CGo header fragment for each file
@@ -399,7 +380,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
Tok: token.TYPE,
}
for _, name := range builtinAliases {
typeSpec := p.getIntegerType("C."+name, names["_Cgo_"+name])
typeSpec := p.getIntegerType("_Cgo_"+name, names["_Cgo_"+name])
gen.Specs = append(gen.Specs, typeSpec)
}
p.generated.Decls = append(p.generated.Decls, gen)
@@ -1272,7 +1253,7 @@ func (p *cgoPackage) getUnnamedDeclName(prefix string, itf interface{}) string {
func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) string {
// Some types are defined in stdint.h and map directly to a particular Go
// type.
if alias := cgoAliases["C."+name]; alias != "" {
if alias := cgoAliases["_Cgo_"+name]; alias != "" {
return alias
}
node := f.getASTDeclNode(name, found)
@@ -1282,7 +1263,7 @@ func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) st
}
return node.Name.Name
}
return "C." + name
return "_Cgo_" + name
}
// getASTDeclNode will declare the given C AST node (if not already defined) and
@@ -1382,8 +1363,8 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
case *elaboratedTypeInfo:
// Add struct bitfields.
for _, bitfield := range elaboratedType.bitfields {
f.createBitfieldGetter(bitfield, "C."+name)
f.createBitfieldSetter(bitfield, "C."+name)
f.createBitfieldGetter(bitfield, "_Cgo_"+name)
f.createBitfieldSetter(bitfield, "_Cgo_"+name)
}
if elaboratedType.unionSize != 0 {
// Create union getters/setters.
@@ -1392,7 +1373,7 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
f.addError(elaboratedType.pos, fmt.Sprintf("union must have field with a single name, it has %d names", len(field.Names)))
continue
}
f.createUnionAccessor(field, "C."+name)
f.createUnionAccessor(field, "_Cgo_"+name)
}
}
}
@@ -1441,7 +1422,7 @@ func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) b
node.Rhs = append(node.Rhs, &ast.CallExpr{
Fun: &ast.Ident{
NamePos: node.Lhs[1].End(),
Name: "C.__get_errno",
Name: "_Cgo___get_errno",
},
})
}
@@ -1466,7 +1447,7 @@ func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) b
return true
}
if x.Name == "C" {
name := "C." + node.Sel.Name
name := "_Cgo_" + node.Sel.Name
if found, ok := names[node.Sel.Name]; ok {
name = f.getASTDeclName(node.Sel.Name, found, false)
}