diff options
author | Michael Stapelberg <stapelberg@debian.org> | 2013-03-04 21:27:36 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-03-04 21:27:36 +0100 |
commit | 04b08da9af0c450d645ab7389d1467308cfc2db8 (patch) | |
tree | db247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/cmd/cgo | |
parent | 917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff) | |
download | golang-04b08da9af0c450d645ab7389d1467308cfc2db8.tar.gz |
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/cmd/cgo')
-rw-r--r-- | src/cmd/cgo/ast.go | 26 | ||||
-rw-r--r-- | src/cmd/cgo/doc.go | 506 | ||||
-rw-r--r-- | src/cmd/cgo/gcc.go | 143 | ||||
-rw-r--r-- | src/cmd/cgo/godefs.go | 2 | ||||
-rw-r--r-- | src/cmd/cgo/main.go | 19 | ||||
-rw-r--r-- | src/cmd/cgo/out.go | 338 | ||||
-rw-r--r-- | src/cmd/cgo/util.go | 53 |
7 files changed, 923 insertions, 164 deletions
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index 381e606ef..dbae3b7b1 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -78,7 +78,7 @@ func (f *File) ReadGo(name string) { } if cg != nil { f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) - f.Preamble += cg.Text() + "\n" + f.Preamble += commentText(cg) + "\n" } } } @@ -131,6 +131,30 @@ func (f *File) ReadGo(name string) { f.AST = ast2 } +// Like ast.CommentGroup's Text method but preserves +// leading blank lines, so that line numbers line up. +func commentText(g *ast.CommentGroup) string { + if g == nil { + return "" + } + var pieces []string + for _, com := range g.List { + c := string(com.Text) + // Remove comment markers. + // The parser has given us exactly the comment text. + switch c[1] { + case '/': + //-style comment (no newline at the end) + c = c[2:] + "\n" + case '*': + /*-style comment */ + c = c[2 : len(c)-2] + } + pieces = append(pieces, c) + } + return strings.Join(pieces, "") +} + // Save references to C.xxx for later processing. func (f *File) saveRef(x interface{}, context string) { n, ok := x.(*ast.Expr) diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 1bb48f44e..955b7c495 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -65,11 +65,13 @@ struct_, union_, or enum_, as in C.struct_stat. Go structs cannot embed fields with C types. -Any C function that returns a value may be called in a multiple -assignment context to retrieve both the return value and the -C errno variable as an error. For example: +Any C function (even void functions) may be called in a multiple +assignment context to retrieve both the return value (if any) and the +C errno variable as an error (use _ to skip the result value if the +function returns void). For example: n, err := C.atoi("abc") + _, err := C.voidFunc() In C, a function argument written as a fixed size array actually requires a pointer to the first element of the array. @@ -83,7 +85,8 @@ by making copies of the data. In pseudo-Go definitions: // Go string to C string // The C string is allocated in the C heap using malloc. // It is the caller's responsibility to arrange for it to be - // freed, such as by calling C.free. + // freed, such as by calling C.free (be sure to include stdlib.h + // if C.free is needed). func C.CString(string) *C.char // C string to Go string @@ -113,6 +116,11 @@ copied from the cgo input files. Functions with multiple return values are mapped to functions returning a struct. Not all Go types can be mapped to C types in a useful way. +Using //export in a file places a restriction on the preamble: +since it is copied into two different C output files, it must not +contain any definitions, only declarations. Definitions must be +placed in preambles in other files, or in C source files. + Cgo transforms the input file into four output files: two Go source files, a C file for 6c (or 8c or 5c), and a C file for gcc. @@ -125,4 +133,492 @@ Cgo does not yet work with gccgo. See "C? Go? Cgo!" for an introduction to using cgo: http://golang.org/doc/articles/c_go_cgo.html */ -package documentation +package main + +/* +Implementation details. + +Cgo provides a way for Go programs to call C code linked into the same +address space. This comment explains the operation of cgo. + +Cgo reads a set of Go source files and looks for statements saying +import "C". If the import has a doc comment, that comment is +taken as literal C code to be used as a preamble to any C code +generated by cgo. A typical preamble #includes necessary definitions: + + // #include <stdio.h> + import "C" + +For more details about the usage of cgo, see the documentation +comment at the top of this file. + +Understanding C + +Cgo scans the Go source files that import "C" for uses of that +package, such as C.puts. It collects all such identifiers. The next +step is to determine each kind of name. In C.xxx the xxx might refer +to a type, a function, a constant, or a global variable. Cgo must +decide which. + +The obvious thing for cgo to do is to process the preamble, expanding +#includes and processing the corresponding C code. That would require +a full C parser and type checker that was also aware of any extensions +known to the system compiler (for example, all the GNU C extensions) as +well as the system-specific header locations and system-specific +pre-#defined macros. This is certainly possible to do, but it is an +enormous amount of work. + +Cgo takes a different approach. It determines the meaning of C +identifiers not by parsing C code but by feeding carefully constructed +programs into the system C compiler and interpreting the generated +error messages, debug information, and object files. In practice, +parsing these is significantly less work and more robust than parsing +C source. + +Cgo first invokes gcc -E -dM on the preamble, in order to find out +about simple #defines for constants and the like. These are recorded +for later use. + +Next, cgo needs to identify the kinds for each identifier. For the +identifiers C.foo and C.bar, cgo generates this C program: + + <preamble> + void __cgo__f__(void) { + #line 1 "cgo-test" + foo; + enum { _cgo_enum_0 = foo }; + bar; + enum { _cgo_enum_1 = bar }; + } + +This program will not compile, but cgo can look at the error messages +to infer the kind of each identifier. The line number given in the +error tells cgo which identifier is involved. + +An error like "unexpected type name" or "useless type name in empty +declaration" or "declaration does not declare anything" tells cgo that +the identifier is a type. + +An error like "statement with no effect" or "expression result unused" +tells cgo that the identifier is not a type, but not whether it is a +constant, function, or global variable. + +An error like "not an integer constant" tells cgo that the identifier +is not a constant. If it is also not a type, it must be a function or +global variable. For now, those can be treated the same. + +Next, cgo must learn the details of each type, variable, function, or +constant. It can do this by reading object files. If cgo has decided +that t1 is a type, v2 and v3 are variables or functions, and c4, c5, +and c6 are constants, it generates: + + <preamble> + typeof(t1) *__cgo__1; + typeof(v2) *__cgo__2; + typeof(v3) *__cgo__3; + typeof(c4) *__cgo__4; + enum { __cgo_enum__4 = c4 }; + typeof(c5) *__cgo__5; + enum { __cgo_enum__5 = c5 }; + typeof(c6) *__cgo__6; + enum { __cgo_enum__6 = c6 }; + + long long __cgo_debug_data[] = { + 0, // t1 + 0, // v2 + 0, // v3 + c4, + c5, + c6, + 1 + }; + +and again invokes the system C compiler, to produce an object file +containing debug information. Cgo parses the DWARF debug information +for __cgo__N to learn the type of each identifier. (The types also +distinguish functions from global variables.) If using a standard gcc, +cgo can parse the DWARF debug information for the __cgo_enum__N to +learn the identifier's value. The LLVM-based gcc on OS X emits +incomplete DWARF information for enums; in that case cgo reads the +constant values from the __cgo_debug_data from the object file's data +segment. + +At this point cgo knows the meaning of each C.xxx well enough to start +the translation process. + +Translating Go + +[The rest of this comment refers to 6g and 6c, the Go and C compilers +that are part of the amd64 port of the gc Go toolchain. Everything here +applies to another architecture's compilers as well.] + +Given the input Go files x.go and y.go, cgo generates these source +files: + + x.cgo1.go # for 6g + y.cgo1.go # for 6g + _cgo_gotypes.go # for 6g + _cgo_defun.c # for 6c + x.cgo2.c # for gcc + y.cgo2.c # for gcc + _cgo_export.c # for gcc + _cgo_main.c # for gcc + +The file x.cgo1.go is a copy of x.go with the import "C" removed and +references to C.xxx replaced with names like _Cfunc_xxx or _Ctype_xxx. +The definitions of those identifiers, written as Go functions, types, +or variables, are provided in _cgo_gotypes.go. + +Here is a _cgo_gotypes.go containing definitions for C.flush (provided +in the preamble) and C.puts (from stdio): + + type _Ctype_char int8 + type _Ctype_int int32 + type _Ctype_void [0]byte + + func _Cfunc_CString(string) *_Ctype_char + func _Cfunc_flush() _Ctype_void + func _Cfunc_puts(*_Ctype_char) _Ctype_int + +For functions, cgo only writes an external declaration in the Go +output. The implementation is in a combination of C for 6c (meaning +any gc-toolchain compiler) and C for gcc. + +The 6c file contains the definitions of the functions. They all have +similar bodies that invoke runtime·cgocall to make a switch from the +Go runtime world to the system C (GCC-based) world. + +For example, here is the definition of _Cfunc_puts: + + void _cgo_be59f0f25121_Cfunc_puts(void*); + + void + ·_Cfunc_puts(struct{uint8 x[1];}p) + { + runtime·cgocall(_cgo_be59f0f25121_Cfunc_puts, &p); + } + +The hexadecimal number is a hash of cgo's input, chosen to be +deterministic yet unlikely to collide with other uses. The actual +function _cgo_be59f0f25121_Cfunc_puts is implemented in a C source +file compiled by gcc, the file x.cgo2.c: + + void + _cgo_be59f0f25121_Cfunc_puts(void *v) + { + struct { + char* p0; + int r; + char __pad12[4]; + } __attribute__((__packed__)) *a = v; + a->r = puts((void*)a->p0); + } + +It extracts the arguments from the pointer to _Cfunc_puts's argument +frame, invokes the system C function (in this case, puts), stores the +result in the frame, and returns. + +Linking + +Once the _cgo_export.c and *.cgo2.c files have been compiled with gcc, +they need to be linked into the final binary, along with the libraries +they might depend on (in the case of puts, stdio). 6l has been +extended to understand basic ELF files, but it does not understand ELF +in the full complexity that modern C libraries embrace, so it cannot +in general generate direct references to the system libraries. + +Instead, the build process generates an object file using dynamic +linkage to the desired libraries. The main function is provided by +_cgo_main.c: + + int main() { return 0; } + void crosscall2(void(*fn)(void*, int), void *a, int c) { } + void _cgo_allocate(void *a, int c) { } + void _cgo_panic(void *a, int c) { } + +The extra functions here are stubs to satisfy the references in the C +code generated for gcc. The build process links this stub, along with +_cgo_export.c and *.cgo2.c, into a dynamic executable and then lets +cgo examine the executable. Cgo records the list of shared library +references and resolved names and writes them into a new file +_cgo_import.c, which looks like: + + #pragma cgo_dynamic_linker "/lib64/ld-linux-x86-64.so.2" + #pragma cgo_import_dynamic puts puts#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic __libc_start_main __libc_start_main#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic stdout stdout#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic fflush fflush#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic _ _ "libpthread.so.0" + #pragma cgo_import_dynamic _ _ "libc.so.6" + +In the end, the compiled Go package, which will eventually be +presented to 6l as part of a larger program, contains: + + _go_.6 # 6g-compiled object for _cgo_gotypes.go *.cgo1.go + _cgo_defun.6 # 6c-compiled object for _cgo_defun.c + _all.o # gcc-compiled object for _cgo_export.c, *.cgo2.c + _cgo_import.6 # 6c-compiled object for _cgo_import.c + +The final program will be a dynamic executable, so that 6l can avoid +needing to process arbitrary .o files. It only needs to process the .o +files generated from C files that cgo writes, and those are much more +limited in the ELF or other features that they use. + +In essence, the _cgo_import.6 file includes the extra linking +directives that 6l is not sophisticated enough to derive from _all.o +on its own. Similarly, the _all.o uses dynamic references to real +system object code because 6l is not sophisticated enough to process +the real code. + +The main benefits of this system are that 6l remains relatively simple +(it does not need to implement a complete ELF and Mach-O linker) and +that gcc is not needed after the package is compiled. For example, +package net uses cgo for access to name resolution functions provided +by libc. Although gcc is needed to compile package net, gcc is not +needed to link programs that import package net. + +Runtime + +When using cgo, Go must not assume that it owns all details of the +process. In particular it needs to coordinate with C in the use of +threads and thread-local storage. The runtime package, in its own +(6c-compiled) C code, declares a few uninitialized (default bss) +variables: + + bool runtime·iscgo; + void (*libcgo_thread_start)(void*); + void (*initcgo)(G*); + +Any package using cgo imports "runtime/cgo", which provides +initializations for these variables. It sets iscgo to 1, initcgo to a +gcc-compiled function that can be called early during program startup, +and libcgo_thread_start to a gcc-compiled function that can be used to +create a new thread, in place of the runtime's usual direct system +calls. + +[NOTE: From here down is planned but not yet implemented.] + +Internal and External Linking + +The text above describes "internal" linking, in which 6l parses and +links host object files (ELF, Mach-O, PE, and so on) into the final +executable itself. Keeping 6l simple means we cannot possibly +implement the full semantics of the host linker, so the kinds of +objects that can be linked directly into the binary is limited (other +code can only be used as a dynamic library). On the other hand, when +using internal linking, 6l can generate Go binaries by itself. + +In order to allow linking arbitrary object files without requiring +dynamic libraries, cgo will soon support an "external" linking mode +too. In external linking mode, 6l does not process any host object +files. Instead, it collects all the Go code and writes a single go.o +object file containing it. Then it invokes the host linker (usually +gcc) to combine the go.o object file and any supporting non-Go code +into a final executable. External linking avoids the dynamic library +requirement but introduces a requirement that the host linker be +present to create such a binary. + +Most builds both compile source code and invoke the linker to create a +binary. When cgo is involved, the compile step already requires gcc, so +it is not problematic for the link step to require gcc too. + +An important exception is builds using a pre-compiled copy of the +standard library. In particular, package net uses cgo on most systems, +and we want to preserve the ability to compile pure Go code that +imports net without requiring gcc to be present at link time. (In this +case, the dynamic library requirement is less significant, because the +only library involved is libc.so, which can usually be assumed +present.) + +This conflict between functionality and the gcc requirement means we +must support both internal and external linking, depending on the +circumstances: if net is the only cgo-using package, then internal +linking is probably fine, but if other packages are involved, so that there +are dependencies on libraries beyond libc, external linking is likely +to work better. The compilation of a package records the relevant +information to support both linking modes, leaving the decision +to be made when linking the final binary. + +Linking Directives + +In either linking mode, package-specific directives must be passed +through to 6l. These are communicated by writing #pragma directives +in a C source file compiled by 6c. The directives are copied into the .6 object file +and then processed by the linker. + +The directives are: + +#pragma cgo_import_dynamic <local> [<remote> ["<library>"]] + + In internal linking mode, allow an unresolved reference to + <local>, assuming it will be resolved by a dynamic library + symbol. The optional <remote> specifies the symbol's name and + possibly version in the dynamic library, and the optional "<library>" + names the specific library where the symbol should be found. + + In the <remote>, # or @ can be used to introduce a symbol version. + + Examples: + #pragma cgo_import_dynamic puts + #pragma cgo_import_dynamic puts puts#GLIBC_2.2.5 + #pragma cgo_import_dynamic puts puts#GLIBC_2.2.5 "libc.so.6" + + A side effect of the cgo_dynamic_import directive with a + library is to make the final binary depend on that dynamic + library. To get the dependency without importing any specific + symbols, use _ for local and remote. + + Example: + #pragma cgo_import_dynamic _ _ "libc.so.6" + + For compatibility with current versions of SWIG, + #pragma dynimport is an alias for #pragma cgo_dynamic_import. + +#pragma cgo_dynamic_linker "<path>" + + In internal linking mode, use "<path>" as the dynamic linker + in the final binary. This directive is only needed from one + package when constructing a binary; by convention it is + supplied by runtime/cgo. + + Example: + #pragma cgo_dynamic_linker "/lib/ld-linux.so.2" + +#pragma cgo_export <local> <remote> + + In both internal and external linking modes, put the Go symbol + named <local> into the program's exported symbol table as + <remote>, so that C code can refer to it by that name. This + mechanism makes it possible for C code to call back into Go or + to share Go's data. + + For compatibility with current versions of SWIG, + #pragma dynexport is an alias for #pragma cgo_export. + +#pragma cgo_import_static <local> + + In external linking mode, allow unresolved references to + <local> in the go.o object file prepared for the host linker, + under the assumption that <local> will be supplied by the + other object files that will be linked with go.o. + + Example: + #pragma cgo_import_static puts_wrapper + +#pragma cgo_ldflag "<arg>" + + In external linking mode, invoke the host linker (usually gcc) + with "<arg>" as a command-line argument following the .o files. + Note that the arguments are for "gcc", not "ld". + + Example: + #pragma cgo_ldflag "-lpthread" + #pragma cgo_ldflag "-L/usr/local/sqlite3/lib" + +A package compiled with cgo will include directives for both +internal and external linking; the linker will select the appropriate +subset for the chosen linking mode. + +Example + +As a simple example, consider a package that uses cgo to call C.sin. +The following code will be generated by cgo: + + // compiled by 6g + + type _Ctype_double float64 + func _Cfunc_sin(_Ctype_double) _Ctype_double + + // compiled by 6c + + #pragma cgo_import_dynamic sin sin#GLIBC_2.2.5 "libm.so.6" + + #pragma cgo_import_static _cgo_gcc_Cfunc_sin + #pragma cgo_ldflag "-lm" + + void _cgo_gcc_Cfunc_sin(void*); + + void + ·_Cfunc_sin(struct{uint8 x[16];}p) + { + runtime·cgocall(_cgo_gcc_Cfunc_sin, &p); + } + + // compiled by gcc, into foo.cgo2.o + + void + _cgo_gcc_Cfunc_sin(void *v) + { + struct { + double p0; + double r; + } __attribute__((__packed__)) *a = v; + a->r = sin(a->p0); + } + +What happens at link time depends on whether the final binary is linked +using the internal or external mode. If other packages are compiled in +"external only" mode, then the final link will be an external one. +Otherwise the link will be an internal one. + +The directives in the 6c-compiled file are used according to the kind +of final link used. + +In internal mode, 6l itself processes all the host object files, in +particular foo.cgo2.o. To do so, it uses the cgo_dynamic_import and +cgo_dynamic_linker directives to learn that the otherwise undefined +reference to sin in foo.cgo2.o should be rewritten to refer to the +symbol sin with version GLIBC_2.2.5 from the dynamic library +"libm.so.6", and the binary should request "/lib/ld-linux.so.2" as its +runtime dynamic linker. + +In external mode, 6l does not process any host object files, in +particular foo.cgo2.o. It links together the 6g- and 6c-generated +object files, along with any other Go code, into a go.o file. While +doing that, 6l will discover that there is no definition for +_cgo_gcc_Cfunc_sin, referred to by the 6c-compiled source file. This +is okay, because 6l also processes the cgo_import_static directive and +knows that _cgo_gcc_Cfunc_sin is expected to be supplied by a host +object file, so 6l does not treat the missing symbol as an error when +creating go.o. Indeed, the definition for _cgo_gcc_Cfunc_sin will be +provided to the host linker by foo2.cgo.o, which in turn will need the +symbol 'sin'. 6l also processes the cgo_ldflag directives, so that it +knows that the eventual host link command must include the -lm +argument, so that the host linker will be able to find 'sin' in the +math library. + +6l Command Line Interface + +The go command and any other Go-aware build systems invoke 6l +to link a collection of packages into a single binary. By default, 6l will +present the same interface it does today: + + 6l main.a + +produces a file named 6.out, even if 6l does so by invoking the host +linker in external linking mode. + +By default, 6l will decide the linking mode as follows: if the only +packages using cgo are those on a whitelist of standard library +packages (net, os/user, runtime/cgo), 6l will use internal linking +mode. Otherwise, there are non-standard cgo packages involved, and 6l +will use external linking mode. The first rule means that a build of +the godoc binary, which uses net but no other cgo, can run without +needing gcc available. The second rule means that a build of a +cgo-wrapped library like sqlite3 can generate a standalone executable +instead of needing to refer to a dynamic library. The specific choice +can be overridden using a command line flag: 6l -cgolink=internal or +6l -cgolink=external. + +In an external link, 6l will create a temporary directory, write any +host object files found in package archives to that directory (renamed +to avoid conflicts), write the go.o file to that directory, and invoke +the host linker. The default value for the host linker is $CC, split +into fields, or else "gcc". The specific host linker command line can +be overridden using a command line flag: 6l -hostld='gcc -ggdb' + +These defaults mean that Go-aware build systems can ignore the linking +changes and keep running plain '6l' and get reasonable results, but +they can also control the linking details if desired. + +*/ diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 98a847e6f..4b0a521a8 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -391,9 +391,9 @@ func (p *Package) guessKinds(f *File) []*Name { b.WriteString(builtinProlog) b.WriteString(f.Preamble) b.WriteString("void __cgo__f__(void) {\n") - b.WriteString("#line 0 \"cgo-test\"\n") + b.WriteString("#line 1 \"cgo-test\"\n") for i, n := range toSniff { - fmt.Fprintf(&b, "%s; enum { _cgo_enum_%d = %s }; /* cgo-test:%d */\n", n.C, i, n.C, i) + fmt.Fprintf(&b, "%s; /* #%d */\nenum { _cgo_enum_%d = %s }; /* #%d */\n", n.C, i, i, n.C, i) } b.WriteString("}\n") stderr := p.gccErrors(b.Bytes()) @@ -423,14 +423,18 @@ func (p *Package) guessKinds(f *File) []*Name { if err != nil { continue } + i = (i - 1) / 2 what := "" switch { default: continue - case strings.Contains(line, ": useless type name in empty declaration"): + case strings.Contains(line, ": useless type name in empty declaration"), + strings.Contains(line, ": declaration does not declare anything"), + strings.Contains(line, ": unexpected type name"): what = "type" isConst[i] = false - case strings.Contains(line, ": statement with no effect"): + case strings.Contains(line, ": statement with no effect"), + strings.Contains(line, ": expression result unused"): what = "not-type" // const or func or var case strings.Contains(line, "undeclared"): error_(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) @@ -508,7 +512,12 @@ func (p *Package) loadDWARF(f *File, names []*Name) { fmt.Fprintf(&b, "\t0,\n") } } - fmt.Fprintf(&b, "\t0\n") + // for the last entry, we can not use 0, otherwise + // in case all __cgodebug_data is zero initialized, + // LLVM-based gcc will place the it in the __DATA.__common + // zero-filled section (our debug/macho doesn't support + // this) + fmt.Fprintf(&b, "\t1\n") fmt.Fprintf(&b, "};\n") d, bo, debugData := p.gccDebug(b.Bytes()) @@ -596,7 +605,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // Record types and typedef information. var conv typeConv - conv.Init(p.PtrSize) + conv.Init(p.PtrSize, p.IntSize) for i, n := range names { if types[i] == nil { continue @@ -618,7 +627,9 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // Remove injected enum to ensure the value will deep-compare // equally in future loads of the same constant. delete(n.Type.EnumValues, k) - } else if n.Kind == "const" && i < len(enumVal) { + } + // Prefer debug data over DWARF debug output, if we have it. + if n.Kind == "const" && i < len(enumVal) { n.Const = fmt.Sprintf("%#x", enumVal[i]) } } @@ -637,7 +648,14 @@ func (p *Package) rewriteRef(f *File) { n.Kind = "var" } if n.Mangle == "" { - n.Mangle = "_C" + n.Kind + "_" + n.Go + // When using gccgo variables have to be + // exported so that they become global symbols + // that the C code can refer to. + prefix := "_C" + if *gccgo && n.Kind == "var" { + prefix = "C" + } + n.Mangle = prefix + n.Kind + "_" + n.Go } } @@ -662,9 +680,6 @@ func (p *Package) rewriteRef(f *File) { break } if r.Context == "call2" { - if r.Name.FuncType.Result == nil { - error_(r.Pos(), "assignment count mismatch: 2 = 0") - } // Invent new Name for the two-result function. n := f.Name["2"+r.Name.Go] if n == nil { @@ -720,23 +735,30 @@ func (p *Package) rewriteRef(f *File) { } } -// gccName returns the name of the compiler to run. Use $GCC if set in +// gccName returns the name of the compiler to run. Use $CC if set in // the environment, otherwise just "gcc". -func (p *Package) gccName() (ret string) { - if ret = os.Getenv("GCC"); ret == "" { - ret = "gcc" +func (p *Package) gccName() string { + // Use $CC if set, since that's what the build uses. + if ret := os.Getenv("CC"); ret != "" { + return ret } - return + // Fall back to $GCC if set, since that's what we used to use. + if ret := os.Getenv("GCC"); ret != "" { + return ret + } + return "gcc" } -// gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". +// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". func (p *Package) gccMachine() []string { switch goarch { case "amd64": return []string{"-m64"} case "386": return []string{"-m32"} + case "arm": + return []string{"-marm"} // not thumb } return nil } @@ -755,9 +777,22 @@ func (p *Package) gccCmd() []string { "-o" + gccTmp(), // write object to tmp "-gdwarf-2", // generate DWARF v2 debugging symbols "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise - "-c", // do not link - "-xc", // input language is C + "-c", // do not link + "-xc", // input language is C + } + if strings.Contains(p.gccName(), "clang") { + c = append(c, + "-ferror-limit=0", + // Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn) + // doesn't have -Wno-unneeded-internal-declaration, so we need yet another + // flag to disable the warning. Yes, really good diagnostics, clang. + "-Wno-unknown-warning-option", + "-Wno-unneeded-internal-declaration", + "-Wno-unused-function", + "-Qunused-arguments", + ) } + c = append(c, p.GccOptions...) c = append(c, p.gccMachine()...) c = append(c, "-") //read input from standard input @@ -795,15 +830,30 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) return d, f.ByteOrder, data } - // Can skip debug data block in ELF and PE for now. - // The DWARF information is complete. - if f, err := elf.Open(gccTmp()); err == nil { d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } - return d, f.ByteOrder, nil + var data []byte + symtab, err := f.Symbols() + if err == nil { + for i := range symtab { + s := &symtab[i] + if s.Name == "__cgodebug_data" { + // Found it. Now find data section. + if i := int(s.Section); 0 <= i && i < len(f.Sections) { + sect := f.Sections[i] + if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size { + if sdat, err := sect.Data(); err == nil { + data = sdat[s.Value-sect.Addr:] + } + } + } + } + } + } + return d, f.ByteOrder, data } if f, err := pe.Open(gccTmp()); err == nil { @@ -811,7 +861,20 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } - return d, binary.LittleEndian, nil + var data []byte + for _, s := range f.Symbols { + if s.Name == "_"+"__cgodebug_data" { + if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { + sect := f.Sections[i] + if s.Value < sect.Size { + if sdat, err := sect.Data(); err == nil { + data = sdat[s.Value:] + } + } + } + } + } + return d, binary.LittleEndian, data } fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp()) @@ -889,16 +952,19 @@ type typeConv struct { void ast.Expr unsafePointer ast.Expr string ast.Expr + goVoid ast.Expr // _Ctype_void, denotes C's void ptrSize int64 + intSize int64 } var tagGen int var typedef = make(map[string]*Type) var goIdent = make(map[string]*ast.Ident) -func (c *typeConv) Init(ptrSize int64) { +func (c *typeConv) Init(ptrSize, intSize int64) { c.ptrSize = ptrSize + c.intSize = intSize c.m = make(map[dwarf.Type]*Type) c.bool = c.Ident("bool") c.byte = c.Ident("byte") @@ -918,6 +984,7 @@ func (c *typeConv) Init(ptrSize int64) { c.unsafePointer = c.Ident("unsafe.Pointer") c.void = c.Ident("void") c.string = c.Ident("string") + c.goVoid = c.Ident("_Ctype_void") } // base strips away qualifiers and typedefs to get the underlying type @@ -988,6 +1055,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { return t } + // clang won't generate DW_AT_byte_size for pointer types, + // so we have to fix it here. + if dt, ok := base(dtype).(*dwarf.PtrType); ok && dt.ByteSize == -1 { + dt.ByteSize = c.ptrSize + } + t := new(Type) t.Size = dtype.Size() t.Align = -1 @@ -1032,7 +1105,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { case *dwarf.BoolType: t.Go = c.bool - t.Align = c.ptrSize + t.Align = 1 case *dwarf.CharType: if t.Size != 1 { @@ -1163,11 +1236,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { t.Go = name // publish before recursive calls goIdent[name.Name] = name switch dt.Kind { - case "union", "class": + case "class", "union": t.Go = c.Opaque(t.Size) if t.C.Empty() { t.C.Set("typeof(unsigned char[%d])", t.Size) } + t.Align = 1 // TODO: should probably base this on field alignment. typedef[name.Name] = t case "struct": g, csyntax, align := c.Struct(dt, pos) @@ -1245,8 +1319,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { } case *dwarf.VoidType: - t.Go = c.void + t.Go = c.goVoid t.C.Set("void") + t.Align = 1 } switch dtype.(type) { @@ -1334,7 +1409,9 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType { } var r *Type var gr []*ast.Field - if _, ok := dtype.ReturnType.(*dwarf.VoidType); !ok && dtype.ReturnType != nil { + if _, ok := dtype.ReturnType.(*dwarf.VoidType); ok { + gr = []*ast.Field{{Type: c.goVoid}} + } else if dtype.ReturnType != nil { r = c.Type(dtype.ReturnType, pos) gr = []*ast.Field{{Type: r.Go}} } @@ -1493,8 +1570,8 @@ func godefsFields(fld []*ast.Field) { npad := 0 for _, f := range fld { for _, n := range f.Names { - if strings.HasPrefix(n.Name, prefix) && n.Name != prefix { - n.Name = n.Name[len(prefix):] + if n.Name != prefix { + n.Name = strings.TrimPrefix(n.Name, prefix) } if n.Name == "_" { // Use exported name instead. @@ -1522,7 +1599,7 @@ func godefsFields(fld []*ast.Field) { } // fieldPrefix returns the prefix that should be removed from all the -// field names when generating the C or Go code. For generated +// field names when generating the C or Go code. For generated // C, we leave the names as is (tv_sec, tv_usec), since that's what // people are used to seeing in C. For generated Go code, such as // package syscall's data structures, we drop a common prefix @@ -1539,7 +1616,7 @@ func fieldPrefix(fld []*ast.Field) string { // named, say, _pad in an otherwise prefixed header. // If the struct has 3 fields tv_sec, tv_usec, _pad1, then we // still want to remove the tv_ prefix. - // The check for "orig_" here handles orig_eax in the + // The check for "orig_" here handles orig_eax in the // x86 ptrace register sets, which otherwise have all fields // with reg_ prefixes. if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") { diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go index fec70a334..20376170d 100644 --- a/src/cmd/cgo/godefs.go +++ b/src/cmd/cgo/godefs.go @@ -180,7 +180,7 @@ func (p *Package) cdefs(f *File, srcfile string) string { for _, line := range lines { line = strings.TrimSpace(line) if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") { - s := line[len("type ") : len(line)-len(" struct {")] + s := strings.TrimSuffix(strings.TrimPrefix(line, "type "), " struct {") printf("typedef struct %s %s;\n", s, s) } } diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 60165961a..7adc795de 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -31,6 +31,7 @@ type Package struct { PackageName string // name of package PackagePath string PtrSize int64 + IntSize int64 GccOptions []string CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS) Written map[string]bool @@ -129,12 +130,19 @@ var ptrSizeMap = map[string]int64{ "arm": 4, } +var intSizeMap = map[string]int64{ + "386": 4, + "amd64": 8, + "arm": 4, +} + var cPrefix string var fset = token.NewFileSet() var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") var dynout = flag.String("dynout", "", "write -dynobj output to this file") +var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in dynimport mode") // These flags are for bootstrapping a new Go implementation, // to generate Go and C headers that match the data layout and @@ -144,8 +152,10 @@ var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C var objDir = flag.String("objdir", "", "object directory") var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo") -var gccgoprefix = flag.String("gccgoprefix", "go", "prefix of symbols generated by gccgo") +var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo") +var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo") var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code") +var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code") var goarch, goos string func main() { @@ -287,7 +297,11 @@ func newPackage(args []string) *Package { } ptrSize := ptrSizeMap[goarch] if ptrSize == 0 { - fatalf("unknown $GOARCH %q", goarch) + fatalf("unknown ptrSize for $GOARCH %q", goarch) + } + intSize := intSizeMap[goarch] + if intSize == 0 { + fatalf("unknown intSize for $GOARCH %q", goarch) } // Reset locale variables so gcc emits English errors [sic]. @@ -296,6 +310,7 @@ func newPackage(args []string) *Package { p := &Package{ PtrSize: ptrSize, + IntSize: intSize, GccOptions: gccOptions, CgoFlags: make(map[string]string), Written: make(map[string]bool), diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 5dfc16a02..a126cf17f 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -14,6 +14,7 @@ import ( "go/printer" "go/token" "os" + "sort" "strings" ) @@ -26,6 +27,8 @@ func (p *Package) writeDefs() { fc := creat(*objDir + "_cgo_defun.c") fm := creat(*objDir + "_cgo_main.c") + var gccgoInit bytes.Buffer + fflg := creat(*objDir + "_cgo_flags") for k, v := range p.CgoFlags { fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) @@ -50,19 +53,33 @@ func (p *Package) writeDefs() { fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") - fmt.Fprintf(fgo2, "import \"syscall\"\n\n") + if *importSyscall { + fmt.Fprintf(fgo2, "import \"syscall\"\n\n") + } if !*gccgo && *importRuntimeCgo { fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") } fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n") - fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int) { *dst = syscall.Errno(x) }\n") + if *importSyscall { + fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int32) { *dst = syscall.Errno(x) }\n") + } - for name, def := range typedef { + typedefNames := make([]string, 0, len(typedef)) + for name := range typedef { + typedefNames = append(typedefNames, name) + } + sort.Strings(typedefNames) + for _, name := range typedefNames { + def := typedef[name] fmt.Fprintf(fgo2, "type %s ", name) conf.Fprint(fgo2, fset, def.Go) fmt.Fprintf(fgo2, "\n\n") } - fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") + if *gccgo { + fmt.Fprintf(fgo2, "type _Ctype_void byte\n") + } else { + fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") + } if *gccgo { fmt.Fprintf(fc, cPrologGccgo) @@ -70,6 +87,8 @@ func (p *Package) writeDefs() { fmt.Fprintf(fc, cProlog) } + gccgoSymbolPrefix := p.gccgoSymbolPrefix() + cVars := make(map[string]bool) for _, key := range nameKeys(p.Name) { n := p.Name[key] @@ -86,7 +105,12 @@ func (p *Package) writeDefs() { cVars[n.C] = true } - fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + if *gccgo { + fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle) + fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C) + } else { + fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + } fmt.Fprintf(fc, "\n") fmt.Fprintf(fgo2, "var %s ", n.Mangle) @@ -116,6 +140,14 @@ func (p *Package) writeDefs() { p.writeExports(fgo2, fc, fm) } + init := gccgoInit.String() + if init != "" { + fmt.Fprintln(fc, "static void init(void) __attribute__ ((constructor));") + fmt.Fprintln(fc, "static void init(void) {") + fmt.Fprint(fc, init) + fmt.Fprintln(fc, "}") + } + fgo2.Close() fc.Close() } @@ -131,6 +163,15 @@ func dynimport(obj string) { } if f, err := elf.Open(obj); err == nil { + if *dynlinker { + // Emit the cgo_dynamic_linker line. + if sec := f.Section(".interp"); sec != nil { + if data, err := sec.Data(); err == nil && len(data) > 1 { + // skip trailing \0 in data + fmt.Fprintf(stdout, "#pragma cgo_dynamic_linker %q\n", string(data[:len(data)-1])) + } + } + } sym, err := f.ImportedSymbols() if err != nil { fatalf("cannot load imported symbols from ELF file %s: %v", obj, err) @@ -138,16 +179,16 @@ func dynimport(obj string) { for _, s := range sym { targ := s.Name if s.Version != "" { - targ += "@" + s.Version + targ += "#" + s.Version } - fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library) } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) } for _, l := range lib { - fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic _ _ %q\n", l) } return } @@ -161,14 +202,14 @@ func dynimport(obj string) { if len(s) > 0 && s[0] == '_' { s = s[1:] } - fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s, s, "") + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic %s %s %q\n", s, s, "") } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) } for _, l := range lib { - fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic _ _ %q\n", l) } return } @@ -180,7 +221,8 @@ func dynimport(obj string) { } for _, s := range sym { ss := strings.Split(s, ":") - fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) + name := strings.Split(ss[0], "@")[0] + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1])) } return } @@ -247,6 +289,7 @@ func (p *Package) structType(n *Name) (string, int64) { func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { name := n.Go gtype := n.FuncType.Go + void := gtype.Results == nil || len(gtype.Results.List) == 0 if n.AddError { // Add "error" to return type list. // Type list is known to be 0 or 1 element - it's a C function. @@ -271,38 +314,58 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { if *gccgo { // Gccgo style hooks. - // we hook directly into C. gccgo goes not support cgocall yet. - if !n.AddError { - fmt.Fprintf(fgo2, "//extern %s\n", n.C) - conf.Fprint(fgo2, fset, d) - fmt.Fprint(fgo2, "\n") - } else { - // write a small wrapper to retrieve errno. - cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle) - paramnames := []string(nil) - for i, param := range d.Type.Params.List { - paramName := fmt.Sprintf("p%d", i) - param.Names = []*ast.Ident{ast.NewIdent(paramName)} - paramnames = append(paramnames, paramName) + fmt.Fprint(fgo2, "\n") + cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle) + paramnames := []string(nil) + for i, param := range d.Type.Params.List { + paramName := fmt.Sprintf("p%d", i) + param.Names = []*ast.Ident{ast.NewIdent(paramName)} + paramnames = append(paramnames, paramName) + } + + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, " {\n") + fmt.Fprint(fgo2, "\tdefer syscall.CgocallDone()\n") + fmt.Fprint(fgo2, "\tsyscall.Cgocall()\n") + if n.AddError { + fmt.Fprint(fgo2, "\tsyscall.SetErrno(0)\n") + } + fmt.Fprint(fgo2, "\t") + if !void { + fmt.Fprint(fgo2, "r := ") + } + fmt.Fprintf(fgo2, "%s(%s)\n", cname, strings.Join(paramnames, ", ")) + + if n.AddError { + fmt.Fprint(fgo2, "\te := syscall.GetErrno()\n") + fmt.Fprint(fgo2, "\tif e != 0 {\n") + fmt.Fprint(fgo2, "\t\treturn ") + if !void { + fmt.Fprint(fgo2, "r, ") } - conf.Fprint(fgo2, fset, d) - fmt.Fprintf(fgo2, "{\n") - fmt.Fprintf(fgo2, "\tsyscall.SetErrno(0)\n") - fmt.Fprintf(fgo2, "\tr := %s(%s)\n", cname, strings.Join(paramnames, ", ")) - fmt.Fprintf(fgo2, "\te := syscall.GetErrno()\n") - fmt.Fprintf(fgo2, "\tif e != 0 {\n") - fmt.Fprintf(fgo2, "\t\treturn r, e\n") - fmt.Fprintf(fgo2, "\t}\n") - fmt.Fprintf(fgo2, "\treturn r, nil\n") - fmt.Fprintf(fgo2, "}\n") - // declare the C function. - fmt.Fprintf(fgo2, "//extern %s\n", n.C) - d.Name = ast.NewIdent(cname) + fmt.Fprint(fgo2, "e\n") + fmt.Fprint(fgo2, "\t}\n") + fmt.Fprint(fgo2, "\treturn ") + if !void { + fmt.Fprint(fgo2, "r, ") + } + fmt.Fprint(fgo2, "nil\n") + } else if !void { + fmt.Fprint(fgo2, "\treturn r\n") + } + + fmt.Fprint(fgo2, "}\n") + + // declare the C function. + fmt.Fprintf(fgo2, "//extern %s\n", n.C) + d.Name = ast.NewIdent(cname) + if n.AddError { l := d.Type.Results.List d.Type.Results.List = l[:len(l)-1] - conf.Fprint(fgo2, fset, d) - fmt.Fprint(fgo2, "\n") } + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, "\n") + return } conf.Fprint(fgo2, fset, d) @@ -317,6 +380,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { _, argSize = p.structType(n) // C wrapper calls into gcc, passing a pointer to the argument frame. + fmt.Fprintf(fc, "#pragma cgo_import_static _cgo%s%s\n", cPrefix, n.Mangle) fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) fmt.Fprintf(fc, "\n") fmt.Fprintf(fc, "void\n") @@ -456,11 +520,13 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcch, "%s\n", p.Preamble) - fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog) + fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog()) fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") + fmt.Fprintf(fgcc, "\nextern void crosscall2(void (*fn)(void *, int), void *, int);\n\n") + for _, exp := range p.ExpFunc { fn := exp.Func @@ -552,7 +618,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { s += ")" fmt.Fprintf(fgcch, "\nextern %s;\n", s) - fmt.Fprintf(fgcc, "extern _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "\t%s __attribute__((packed)) a;\n", ctype) @@ -585,7 +651,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { if fn.Recv != nil { goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname } - fmt.Fprintf(fc, "#pragma dynexport %s %s\n", goname, goname) + fmt.Fprintf(fc, "#pragma cgo_export %s\n", goname) fmt.Fprintf(fc, "extern void ·%s();\n\n", goname) fmt.Fprintf(fc, "#pragma textflag 7\n") // no split stack, so no use of m or g fmt.Fprintf(fc, "void\n") @@ -641,32 +707,22 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) { fgcc := creat(*objDir + "_cgo_export.c") fgcch := creat(*objDir + "_cgo_export.h") - _ = fgcc + + gccgoSymbolPrefix := p.gccgoSymbolPrefix() fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcch, "%s\n", p.Preamble) - fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog) - fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n") + fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog()) - clean := func(r rune) rune { - switch { - case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', - '0' <= r && r <= '9': - return r - } - return '_' - } - gccgoSymbolPrefix := strings.Map(clean, *gccgoprefix) + fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") + + fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n") for _, exp := range p.ExpFunc { - // TODO: support functions with receivers. fn := exp.Func fntype := fn.Type - if !ast.IsExported(fn.Name.Name) { - fatalf("cannot export unexported function %s with gccgo", fn.Name) - } - cdeclBuf := new(bytes.Buffer) resultCount := 0 forFieldList(fntype.Results, @@ -692,28 +748,135 @@ func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) { fmt.Fprintf(cdeclBuf, "struct %s_result", exp.ExpName) } - // The function name. - fmt.Fprintf(cdeclBuf, " "+exp.ExpName) - gccgoSymbol := fmt.Sprintf("%s.%s.%s", gccgoSymbolPrefix, p.PackageName, exp.Func.Name) - fmt.Fprintf(cdeclBuf, " (") + cRet := cdeclBuf.String() + + cdeclBuf = new(bytes.Buffer) + fmt.Fprintf(cdeclBuf, "(") + if fn.Recv != nil { + fmt.Fprintf(cdeclBuf, "%s recv", p.cgoType(fn.Recv.List[0].Type).C.String()) + } // Function parameters. forFieldList(fntype.Params, func(i int, atype ast.Expr) { - if i > 0 { + if i > 0 || fn.Recv != nil { fmt.Fprintf(cdeclBuf, ", ") } t := p.cgoType(atype) fmt.Fprintf(cdeclBuf, "%s p%d", t.C, i) }) fmt.Fprintf(cdeclBuf, ")") - cdecl := cdeclBuf.String() + cParams := cdeclBuf.String() + + goName := "Cgoexp_" + exp.ExpName + fmt.Fprintf(fgcch, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, goName) + fmt.Fprint(fgcch, "\n") + + fmt.Fprint(fgcc, "\n") + fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams) + fmt.Fprint(fgcc, "\t") + if resultCount > 0 { + fmt.Fprint(fgcc, "return ") + } + fmt.Fprintf(fgcc, "%s(", goName) + if fn.Recv != nil { + fmt.Fprint(fgcc, "recv") + } + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + fmt.Fprintf(fgcc, ", ") + } + fmt.Fprintf(fgcc, "p%d", i) + }) + fmt.Fprint(fgcc, ");\n") + fmt.Fprint(fgcc, "}\n") - fmt.Fprintf(fgcch, "extern %s __asm__(\"%s\");\n", cdecl, gccgoSymbol) // Dummy declaration for _cgo_main.c - fmt.Fprintf(fm, "%s {}\n", cdecl) + fmt.Fprintf(fm, "%s %s %s {}\n", cRet, goName, cParams) + + // For gccgo we use a wrapper function in Go, in order + // to call CgocallBack and CgocallBackDone. + + // This code uses printer.Fprint, not conf.Fprint, + // because we don't want //line comments in the middle + // of the function types. + fmt.Fprint(fgo2, "\n") + fmt.Fprintf(fgo2, "func %s(", goName) + if fn.Recv != nil { + fmt.Fprint(fgo2, "recv ") + printer.Fprint(fgo2, fset, fn.Recv.List[0].Type) + } + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + fmt.Fprintf(fgo2, ", ") + } + fmt.Fprintf(fgo2, "p%d ", i) + printer.Fprint(fgo2, fset, atype) + }) + fmt.Fprintf(fgo2, ")") + if resultCount > 0 { + fmt.Fprintf(fgo2, " (") + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprint(fgo2, ", ") + } + printer.Fprint(fgo2, fset, atype) + }) + fmt.Fprint(fgo2, ")") + } + fmt.Fprint(fgo2, " {\n") + fmt.Fprint(fgo2, "\tsyscall.CgocallBack()\n") + fmt.Fprint(fgo2, "\tdefer syscall.CgocallBackDone()\n") + fmt.Fprint(fgo2, "\t") + if resultCount > 0 { + fmt.Fprint(fgo2, "return ") + } + if fn.Recv != nil { + fmt.Fprint(fgo2, "recv.") + } + fmt.Fprintf(fgo2, "%s(", exp.Func.Name) + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprint(fgo2, ", ") + } + fmt.Fprintf(fgo2, "p%d", i) + }) + fmt.Fprint(fgo2, ")\n") + fmt.Fprint(fgo2, "}\n") } } +// Return the package prefix when using gccgo. +func (p *Package) gccgoSymbolPrefix() string { + if !*gccgo { + return "" + } + + clean := func(r rune) rune { + switch { + case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', + '0' <= r && r <= '9': + return r + } + return '_' + } + + if *gccgopkgpath != "" { + return strings.Map(clean, *gccgopkgpath) + } + if *gccgoprefix == "" && p.PackageName == "main" { + return "main" + } + prefix := strings.Map(clean, *gccgoprefix) + if prefix == "" { + prefix = "go" + } + return prefix + "." + p.PackageName +} + // Call a function for each entry in an ast.FieldList, passing the // index into the list and the type. func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) { @@ -742,8 +905,8 @@ func c(repr string, args ...interface{}) *TypeRepr { var goTypes = map[string]*Type{ "bool": {Size: 1, Align: 1, C: c("GoUint8")}, "byte": {Size: 1, Align: 1, C: c("GoUint8")}, - "int": {Size: 4, Align: 4, C: c("GoInt")}, - "uint": {Size: 4, Align: 4, C: c("GoUint")}, + "int": {Size: 0, Align: 0, C: c("GoInt")}, + "uint": {Size: 0, Align: 0, C: c("GoUint")}, "rune": {Size: 4, Align: 4, C: c("GoInt32")}, "int8": {Size: 1, Align: 1, C: c("GoInt8")}, "uint8": {Size: 1, Align: 1, C: c("GoUint8")}, @@ -804,12 +967,21 @@ func (p *Package) cgoType(e ast.Expr) *Type { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoUintptr")} } if t.Name == "string" { - return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: c("GoString")} + // The string data is 1 pointer + 1 int, but this always + // rounds to 2 pointers due to alignment. + return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoString")} } if t.Name == "error" { return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} } if r, ok := goTypes[t.Name]; ok { + if r.Size == 0 { // int or uint + rr := new(Type) + *rr = *r + rr.Size = p.IntSize + rr.Align = p.IntSize + r = rr + } if r.Align > p.PtrSize { r.Align = p.PtrSize } @@ -898,18 +1070,21 @@ const cPrologGccgo = ` #include <stdint.h> #include <string.h> +typedef unsigned char byte; +typedef intptr_t intgo; + struct __go_string { const unsigned char *__data; - int __length; + intgo __length; }; typedef struct __go_open_array { void* __values; - int __count; - int __capacity; + intgo __count; + intgo __capacity; } Slice; -struct __go_string __go_byte_array_to_string(const void* p, int len); +struct __go_string __go_byte_array_to_string(const void* p, intgo len); struct __go_open_array __go_string_to_byte_array (struct __go_string str); const char *CString(struct __go_string s) { @@ -917,23 +1092,25 @@ const char *CString(struct __go_string s) { } struct __go_string GoString(char *p) { - int len = (p != NULL) ? strlen(p) : 0; + intgo len = (p != NULL) ? strlen(p) : 0; return __go_byte_array_to_string(p, len); } -struct __go_string GoStringN(char *p, int n) { +struct __go_string GoStringN(char *p, intgo n) { return __go_byte_array_to_string(p, n); } -Slice GoBytes(char *p, int n) { +Slice GoBytes(char *p, intgo n) { struct __go_string s = { (const unsigned char *)p, n }; return __go_string_to_byte_array(s); } ` +func (p *Package) gccExportHeaderProlog() string { + return strings.Replace(gccExportHeaderProlog, "GOINTBITS", fmt.Sprint(8*p.IntSize), -1) +} + const gccExportHeaderProlog = ` -typedef int GoInt; -typedef unsigned int GoUint; typedef signed char GoInt8; typedef unsigned char GoUint8; typedef short GoInt16; @@ -942,6 +1119,8 @@ typedef int GoInt32; typedef unsigned int GoUint32; typedef long long GoInt64; typedef unsigned long long GoUint64; +typedef GoIntGOINTBITS GoInt; +typedef GoUintGOINTBITS GoUint; typedef __SIZE_TYPE__ GoUintptr; typedef float GoFloat32; typedef double GoFloat64; @@ -952,4 +1131,5 @@ typedef struct { char *p; int n; } GoString; typedef void *GoMap; typedef void *GoChan; typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; int len; int cap; } GoSlice; ` diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index a0f216614..4e7800d12 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -5,9 +5,9 @@ package main import ( + "bytes" "fmt" "go/token" - "io/ioutil" "os" "os/exec" ) @@ -16,50 +16,17 @@ import ( // It returns the output to standard output and standard error. // ok indicates whether the command exited successfully. func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { - cmd, err := exec.LookPath(argv[0]) - if err != nil { - fatalf("exec %s: %s", argv[0], err) - } - r0, w0, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - r1, w1, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - r2, w2, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - p, err := os.StartProcess(cmd, argv, &os.ProcAttr{Files: []*os.File{r0, w1, w2}}) - if err != nil { - fatalf("%s", err) - } - r0.Close() - w1.Close() - w2.Close() - c := make(chan bool) - go func() { - w0.Write(stdin) - w0.Close() - c <- true - }() - go func() { - stdout, _ = ioutil.ReadAll(r1) - r1.Close() - c <- true - }() - stderr, _ = ioutil.ReadAll(r2) - r2.Close() - <-c - <-c - - state, err := p.Wait() - if err != nil { + p := exec.Command(argv[0], argv[1:]...) + p.Stdin = bytes.NewReader(stdin) + var bout, berr bytes.Buffer + p.Stdout = &bout + p.Stderr = &berr + err := p.Run() + if _, ok := err.(*exec.ExitError); err != nil && !ok { fatalf("%s", err) } - ok = state.Success() + ok = p.ProcessState.Success() + stdout, stderr = bout.Bytes(), berr.Bytes() return } |