loader/libclang: fix CGo-related crash

Sometimes when a GC happens while processing a C fragment with libclang,
a pointer-typed integer with value 0x1 ends up on the Go stack and the
GC will trip over it. This commit changes the offending struct type to
be uintptr_t instead of void*.

See https://go-review.googlesource.com/c/go/+/66332 for a similar
change.
Этот коммит содержится в:
Ayke van Laethem 2019-04-15 22:33:39 +02:00 коммит произвёл Ron Evans
родитель 586023b45d
коммит b716cf1afd
2 изменённых файлов: 98 добавлений и 23 удалений

Просмотреть файл

@ -16,9 +16,38 @@ import (
/* /*
#include <clang-c/Index.h> // if this fails, install libclang-8-dev #include <clang-c/Index.h> // if this fails, install libclang-8-dev
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
int tinygo_clang_globals_visitor(CXCursor c, CXCursor parent, CXClientData client_data); // This struct should be ABI-compatible on all platforms (uintptr_t has the same
int tinygo_clang_struct_visitor(CXCursor c, CXCursor parent, CXClientData client_data); // alignment etc. as void*) but does not include void* pointers that are not
// always real pointers.
// The Go garbage collector assumes that all non-nil pointer-typed integers are
// actually pointers. This is not always true, as data[1] often contains 0x1,
// which is clearly not a valid pointer. Usually the GC won't catch this issue,
// but occasionally it will leading to a crash with a vague error message.
typedef struct {
enum CXCursorKind kind;
int xdata;
uintptr_t data[3];
} GoCXCursor;
// Forwarding functions. They are implemented in libclang_stubs.c and forward to
// the real functions without doing anything else, thus they are entirely
// compatible with the versions without tinygo_ prefix. The only difference is
// the CXCursor type, which has been replaced with GoCXCursor.
GoCXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu);
unsigned tinygo_clang_visitChildren(GoCXCursor parent, CXCursorVisitor visitor, CXClientData client_data);
CXString tinygo_clang_getCursorSpelling(GoCXCursor c);
enum CXCursorKind tinygo_clang_getCursorKind(GoCXCursor c);
CXType tinygo_clang_getCursorType(GoCXCursor c);
GoCXCursor tinygo_clang_getTypeDeclaration(CXType t);
CXType tinygo_clang_getTypedefDeclUnderlyingType(GoCXCursor c);
CXType tinygo_clang_getCursorResultType(GoCXCursor c);
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
*/ */
import "C" import "C"
@ -123,29 +152,29 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam
ref := refMap.Put(info) ref := refMap.Put(info)
defer refMap.Remove(ref) defer refMap.Remove(ref)
cursor := C.clang_getTranslationUnitCursor(unit) cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
C.clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref)) C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
return nil return nil
} }
//export tinygo_clang_globals_visitor //export tinygo_clang_globals_visitor
func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.int { func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
info := refMap.Get(unsafe.Pointer(client_data)).(*fileInfo) info := refMap.Get(unsafe.Pointer(client_data)).(*fileInfo)
kind := C.clang_getCursorKind(c) kind := C.tinygo_clang_getCursorKind(c)
switch kind { switch kind {
case C.CXCursor_FunctionDecl: case C.CXCursor_FunctionDecl:
name := getString(C.clang_getCursorSpelling(c)) name := getString(C.tinygo_clang_getCursorSpelling(c))
cursorType := C.clang_getCursorType(c) cursorType := C.tinygo_clang_getCursorType(c)
if C.clang_isFunctionTypeVariadic(cursorType) != 0 { if C.clang_isFunctionTypeVariadic(cursorType) != 0 {
return C.CXChildVisit_Continue // not supported return C.CXChildVisit_Continue // not supported
} }
numArgs := int(C.clang_Cursor_getNumArguments(c)) numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
fn := &functionInfo{} fn := &functionInfo{}
info.functions[name] = fn info.functions[name] = fn
for i := 0; i < numArgs; i++ { for i := 0; i < numArgs; i++ {
arg := C.clang_Cursor_getArgument(c, C.uint(i)) arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
argName := getString(C.clang_getCursorSpelling(arg)) argName := getString(C.tinygo_clang_getCursorSpelling(arg))
argType := C.clang_getArgType(cursorType, C.uint(i)) argType := C.clang_getArgType(cursorType, C.uint(i))
if argName == "" { if argName == "" {
argName = "$" + strconv.Itoa(i) argName = "$" + strconv.Itoa(i)
@ -155,7 +184,7 @@ func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientDa
typeExpr: info.makeASTType(argType), typeExpr: info.makeASTType(argType),
}) })
} }
resultType := C.clang_getCursorResultType(c) resultType := C.tinygo_clang_getCursorResultType(c)
if resultType.kind != C.CXType_Void { if resultType.kind != C.CXType_Void {
fn.results = &ast.FieldList{ fn.results = &ast.FieldList{
List: []*ast.Field{ List: []*ast.Field{
@ -166,9 +195,9 @@ func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientDa
} }
} }
case C.CXCursor_TypedefDecl: case C.CXCursor_TypedefDecl:
typedefType := C.clang_getCursorType(c) typedefType := C.tinygo_clang_getCursorType(c)
name := getString(C.clang_getTypedefName(typedefType)) name := getString(C.clang_getTypedefName(typedefType))
underlyingType := C.clang_getTypedefDeclUnderlyingType(c) underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
expr := info.makeASTType(underlyingType) expr := info.makeASTType(underlyingType)
if strings.HasPrefix(name, "_Cgo_") { if strings.HasPrefix(name, "_Cgo_") {
expr := expr.(*ast.Ident) expr := expr.(*ast.Ident)
@ -203,8 +232,8 @@ func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientDa
typeExpr: expr, typeExpr: expr,
} }
case C.CXCursor_VarDecl: case C.CXCursor_VarDecl:
name := getString(C.clang_getCursorSpelling(c)) name := getString(C.tinygo_clang_getCursorSpelling(c))
cursorType := C.clang_getCursorType(c) cursorType := C.tinygo_clang_getCursorType(c)
info.globals[name] = &globalInfo{ info.globals[name] = &globalInfo{
typeExpr: info.makeASTType(cursorType), typeExpr: info.makeASTType(cursorType),
} }
@ -305,7 +334,7 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
underlying := C.clang_Type_getNamedType(typ) underlying := C.clang_Type_getNamedType(typ)
return info.makeASTType(underlying) return info.makeASTType(underlying)
case C.CXType_Record: case C.CXType_Record:
cursor := C.clang_getTypeDeclaration(typ) cursor := C.tinygo_clang_getTypeDeclaration(typ)
fieldList := &ast.FieldList{ fieldList := &ast.FieldList{
Opening: info.importCPos, Opening: info.importCPos,
Closing: info.importCPos, Closing: info.importCPos,
@ -315,8 +344,8 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
info *fileInfo info *fileInfo
}{fieldList, info}) }{fieldList, info})
defer refMap.Remove(ref) defer refMap.Remove(ref)
C.clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(uintptr(ref))) C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
switch C.clang_getCursorKind(cursor) { switch C.tinygo_clang_getCursorKind(cursor) {
case C.CXCursor_StructDecl: case C.CXCursor_StructDecl:
return &ast.StructType{ return &ast.StructType{
Struct: info.importCPos, Struct: info.importCPos,
@ -368,18 +397,18 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr {
} }
//export tinygo_clang_struct_visitor //export tinygo_clang_struct_visitor
func tinygo_clang_struct_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.int { func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
passed := refMap.Get(unsafe.Pointer(client_data)).(struct { passed := refMap.Get(unsafe.Pointer(client_data)).(struct {
fieldList *ast.FieldList fieldList *ast.FieldList
info *fileInfo info *fileInfo
}) })
fieldList := passed.fieldList fieldList := passed.fieldList
info := passed.info info := passed.info
if C.clang_getCursorKind(c) != C.CXCursor_FieldDecl { if C.tinygo_clang_getCursorKind(c) != C.CXCursor_FieldDecl {
panic("expected field inside cursor") panic("expected field inside cursor")
} }
name := getString(C.clang_getCursorSpelling(c)) name := getString(C.tinygo_clang_getCursorSpelling(c))
typ := C.clang_getCursorType(c) typ := C.tinygo_clang_getCursorType(c)
field := &ast.Field{ field := &ast.Field{
Type: info.makeASTType(typ), Type: info.makeASTType(typ),
} }

46
loader/libclang_stubs.c Обычный файл
Просмотреть файл

@ -0,0 +1,46 @@
// This file implements some small trampoline functions. The signatures
// are slightly different from the ones defined in libclang.go, but they
// should be ABI compatible.
#include <clang-c/Index.h> // if this fails, install libclang-8-dev
CXCursor tinygo_clang_getTranslationUnitCursor(CXTranslationUnit tu) {
return clang_getTranslationUnitCursor(tu);
}
unsigned tinygo_clang_visitChildren(CXCursor parent, CXCursorVisitor visitor, CXClientData client_data) {
return clang_visitChildren(parent, visitor, client_data);
}
CXString tinygo_clang_getCursorSpelling(CXCursor c) {
return clang_getCursorSpelling(c);
}
enum CXCursorKind tinygo_clang_getCursorKind(CXCursor c) {
return clang_getCursorKind(c);
}
CXType tinygo_clang_getCursorType(CXCursor c) {
return clang_getCursorType(c);
}
CXCursor tinygo_clang_getTypeDeclaration(CXType t) {
return clang_getTypeDeclaration(t);
}
CXType tinygo_clang_getTypedefDeclUnderlyingType(CXCursor c) {
return clang_getTypedefDeclUnderlyingType(c);
}
CXType tinygo_clang_getCursorResultType(CXCursor c) {
return clang_getCursorResultType(c);
}
int tinygo_clang_Cursor_getNumArguments(CXCursor c) {
return clang_Cursor_getNumArguments(c);
}
CXCursor tinygo_clang_Cursor_getArgument(CXCursor c, unsigned i) {
return clang_Cursor_getArgument(c, i);
}