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.
Этот коммит содержится в:
родитель
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
Обычный файл
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);
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче