diff options
author | Ayke van Laethem <[email protected]> | 2019-04-15 22:33:39 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2019-04-20 10:07:26 +0200 |
commit | b716cf1afdb6365cbbeb4af974c27c916db36a9a (patch) | |
tree | c7f57b12493050fc0a7e1566b4fbc504488a45cd | |
parent | 586023b45df58d1d2e5e79ab6c4009aaee0b2b7f (diff) | |
download | tinygo-b716cf1afdb6365cbbeb4af974c27c916db36a9a.tar.gz tinygo-b716cf1afdb6365cbbeb4af974c27c916db36a9a.zip |
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.
-rw-r--r-- | loader/libclang.go | 75 | ||||
-rw-r--r-- | loader/libclang_stubs.c | 46 |
2 files changed, 98 insertions, 23 deletions
diff --git a/loader/libclang.go b/loader/libclang.go index a7495432a..4adf141a4 100644 --- a/loader/libclang.go +++ b/loader/libclang.go @@ -16,9 +16,38 @@ import ( /* #include <clang-c/Index.h> // if this fails, install libclang-8-dev #include <stdlib.h> +#include <stdint.h> -int tinygo_clang_globals_visitor(CXCursor c, CXCursor parent, CXClientData client_data); -int tinygo_clang_struct_visitor(CXCursor c, CXCursor parent, CXClientData client_data); +// This struct should be ABI-compatible on all platforms (uintptr_t has the same +// 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" @@ -123,29 +152,29 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam ref := refMap.Put(info) defer refMap.Remove(ref) - cursor := C.clang_getTranslationUnitCursor(unit) - C.clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref)) + cursor := C.tinygo_clang_getTranslationUnitCursor(unit) + C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref)) return nil } //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) - kind := C.clang_getCursorKind(c) + kind := C.tinygo_clang_getCursorKind(c) switch kind { case C.CXCursor_FunctionDecl: - name := getString(C.clang_getCursorSpelling(c)) - cursorType := C.clang_getCursorType(c) + name := getString(C.tinygo_clang_getCursorSpelling(c)) + cursorType := C.tinygo_clang_getCursorType(c) if C.clang_isFunctionTypeVariadic(cursorType) != 0 { return C.CXChildVisit_Continue // not supported } - numArgs := int(C.clang_Cursor_getNumArguments(c)) + numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c)) fn := &functionInfo{} info.functions[name] = fn for i := 0; i < numArgs; i++ { - arg := C.clang_Cursor_getArgument(c, C.uint(i)) - argName := getString(C.clang_getCursorSpelling(arg)) + arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i)) + argName := getString(C.tinygo_clang_getCursorSpelling(arg)) argType := C.clang_getArgType(cursorType, C.uint(i)) if argName == "" { 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), }) } - resultType := C.clang_getCursorResultType(c) + resultType := C.tinygo_clang_getCursorResultType(c) if resultType.kind != C.CXType_Void { fn.results = &ast.FieldList{ List: []*ast.Field{ @@ -166,9 +195,9 @@ func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientDa } } case C.CXCursor_TypedefDecl: - typedefType := C.clang_getCursorType(c) + typedefType := C.tinygo_clang_getCursorType(c) name := getString(C.clang_getTypedefName(typedefType)) - underlyingType := C.clang_getTypedefDeclUnderlyingType(c) + underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c) expr := info.makeASTType(underlyingType) if strings.HasPrefix(name, "_Cgo_") { expr := expr.(*ast.Ident) @@ -203,8 +232,8 @@ func tinygo_clang_globals_visitor(c, parent C.CXCursor, client_data C.CXClientDa typeExpr: expr, } case C.CXCursor_VarDecl: - name := getString(C.clang_getCursorSpelling(c)) - cursorType := C.clang_getCursorType(c) + name := getString(C.tinygo_clang_getCursorSpelling(c)) + cursorType := C.tinygo_clang_getCursorType(c) info.globals[name] = &globalInfo{ typeExpr: info.makeASTType(cursorType), } @@ -305,7 +334,7 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr { underlying := C.clang_Type_getNamedType(typ) return info.makeASTType(underlying) case C.CXType_Record: - cursor := C.clang_getTypeDeclaration(typ) + cursor := C.tinygo_clang_getTypeDeclaration(typ) fieldList := &ast.FieldList{ Opening: info.importCPos, Closing: info.importCPos, @@ -315,8 +344,8 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr { info *fileInfo }{fieldList, info}) defer refMap.Remove(ref) - C.clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(uintptr(ref))) - switch C.clang_getCursorKind(cursor) { + C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref)) + switch C.tinygo_clang_getCursorKind(cursor) { case C.CXCursor_StructDecl: return &ast.StructType{ Struct: info.importCPos, @@ -368,18 +397,18 @@ func (info *fileInfo) makeASTType(typ C.CXType) ast.Expr { } //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 { fieldList *ast.FieldList info *fileInfo }) fieldList := passed.fieldList 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") } - name := getString(C.clang_getCursorSpelling(c)) - typ := C.clang_getCursorType(c) + name := getString(C.tinygo_clang_getCursorSpelling(c)) + typ := C.tinygo_clang_getCursorType(c) field := &ast.Field{ Type: info.makeASTType(typ), } diff --git a/loader/libclang_stubs.c b/loader/libclang_stubs.c new file mode 100644 index 000000000..638c67880 --- /dev/null +++ b/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); +} |