Can't compile when using archive/tar - go

I am trying to use archive/tar within my Golang project. However, when I compile it, I get the following error:
/usr/local/go/pkg/tool/linux_amd64/link: /go-cache/10/1020ddd253c007d4f0bbed6c73d75297ac475bbc40d485e357efc1e7584bc24f-d(_go_.o): cannot use dynamic imports with -d flag
/usr/local/go/pkg/tool/linux_amd64/link: /go-cache/73/735aa16c44473681079e1f6b4a517de41fcac801aa803f5f84f6ebcb6436f3e6-d(_go_.o): cannot use dynamic imports with -d flag
Here is how I am compiling my project, within an golang:1.17-alpine3.14 Docker container:
go build -ldflags "-d -X main.Something=$SOMETHING -X main.Another=$ANOTHER -linkmode external -extldflags -static" -tags netgo -o prog cmd/prog/*.go
Without the import, everything compiles fine. All I need to do to trigger this is the following:
import (
archive/tar
...
)
...
func someFunc() {
...
tarWriter := tar.NewWriter(file)
defer tarWriter.Close()
}
Allowing this to be dynamically linked isn't something I can do, given requirements of the program. How can I get this to link statically?

Instead of using -ldflags to avoid your program being dynamically linked, you can just disable it through the environment:
CGO_ENABLED=0 go build -ldflags "-X main.Something=$SOMETHING -X main.Another=$ANOTHER" -o prog $PACKAGE_PATH
E.g, this minimal (panicking) program:
package main
import (
"archive/tar"
)
func main() {
tarWriter := tar.NewWriter(nil)
defer tarWriter.Close()
}
compiles perfectly with:
CGO_ENABLED=0 go build -o prog .

Related

How to resolve circular dependencies when using go modules and cgo

In my project, I am using callbacks for bi-directional calls from C into go and vice versa using CGO. I resolved the issue of circular dependencies by compiling the C part into a library, then compiling the go part into a library, then a final linker pass puts it all together. This is working fine when not using go modules. Go source files are listed on the command line explicitly. I have been told that as of go 1.12 "this is not the right way to do it".
As the project has grown, I now want to use go modules. Unfortunately, this changes the behaviour of the go compiler. It now wants to resolve external dependencies and implicitly includes them in the output file. Due to the circular dependency, it now always ends up with an undefined reference or multiple definitions. How to resolve circular dependencies when using cgo and go modules "the right way"?
This is a minimal example to illustrate the problem. Remove the file-name "hello.go" from the call to go in the Makefile to see how it falls apart.
This is the error message:
hello.c:3: multiple definition of `c_hello'; $WORK/b001/_cgo_hello.o:/tmp/go-build/hello.c:3: first defined here
Makefile:
libchello.a: Makefile hello.c
gcc -fPIC -c -o chello.o hello.c
ar r libchello.a chello.o
libgohello.a: Makefile hello.go libchello.a
env CGO_LDFLAGS=libchello.a go build -buildmode=c-archive -o libgohello.a hello.go
main: Makefile main.c libgohello.a libchello.a
gcc -o main main.c libchello.a libgohello.a -pthread
.PHONY: clean
clean:
rm -f main *.a *.o
echo "extern void go_hello();" > libgohello.h
hello.go:
package main
/*
extern void c_hello();
*/
import "C"
import "time"
import "fmt"
//export go_hello
func go_hello() {
fmt.Printf("Hello from go\n")
time.Sleep(1 * time.Second)
C.c_hello()
}
func main() {}
libgohello.h:
extern void go_hello();
hello.c:
#include "libgohello.h"
#include <stdio.h>
void c_hello() {
printf("Hello from c\n");
go_hello();
}
main.c:
void c_hello();
int main() {
c_hello();
}
go.mod:
module hehoe.de/cgocircular
If you look at the verbose output from the go build command, you will see that when compiling the directory as a complete go package, the main.c file is being included as part of the C code used in hello.go.
From the documentation:
When the Go tool sees that one or more Go files use the special import "C", it will look for other non-Go files in the directory and compile them as part of the Go package
The easiest solution here is to separate the main C and Go packages, so that they don't interfere with each other's build process. Testing this out, removing the main.c file will build libchello.a and libgohello.a, and then adding it back in will complete the build of main.

Building Static Binary with ODBC in Go

I'm kinda new to the concept of static building in go.
I was trying to build a static binary for Linux that includes ODBC (which needs cgo) inside of it and was failing to do that. This is the library I'm trying to build with alexbrainman/odbc.
I have tried a couple commands, not sure if they even fit the scenario here;
CGO_ENABLED=0 GOOS=linux go build -a -ldflags="-s -w"
CGO_ENABLED=0 go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"'
CGO_ENABLED=0 go build -ldflags '-w -extldflags "-static"'
They all fail just like running CGO_ENABLED=0 go build.
Is this even possible to do?
Thanks

Golang c-archive with libc instead of glibc

Is it possible?
I have the following Go function I'd like to call from a C program:
// package name: test
package main
import "C"
//export Start
func Start() {
println("Hello world")
}
func main() {
}
I'm using the following command to build the archive
go build -buildmode=c-archive -o test.a main.go
Using gcc I can get this C program working:
#include <stdio.h>
#include "test.h"
int main() {
Start();
return 0;
}
I'm using the following command to build the executable:
gcc main.c sdlgotest.a -o main -lpthread
This all works fine for amd64, but I'd like to use this archive in a aarch64 targed development environment (using libtransistor)
libtransistor build system uses LLVM but it has it's own set of standard includes (libc, etc.) and it doesn't use glibc.
So when I'm trying to get libtransistor to link my archive I get the following errors:
/usr/lib/llvm-5.0/bin/ld.lld: error: undefined symbol: stderr
>>> referenced by gcc_libinit.c:29
>>> 000006.o:(x_cgo_sys_thread_create) in archive ./sdlgotest.a
/usr/lib/llvm-5.0/bin/ld.lld: error: undefined symbol: stderr
>>> referenced by gcc_libinit.c:29
>>> 000006.o:(x_cgo_sys_thread_create) in archive ./sdlgotest.a
libtransistor by the way is compiling the code with flags like this:
-nostdlib -nostdlibinc -isystem /opt/libtransistor/include/
So I guess the problem is that the linker can't resolve those glibc symbols.
Is there a way to compile the Go runtime without those glibc symbols so I can use the archive like I intend to? (without relying on GCC toolchain or glibc)
From the docs:
The default C and C++ compilers may be changed by the CC and CXX environment variables, respectively; those environment variables may include command line options.
So I'd try something like
$ CC=clang go build -buildmode=c-archive -o test.a main.go
and see what happens.
Run go env and see the list of env parameters set to go. You can change certain parameters based on what you need.
Following are some of the samples that I have used to build a shared dll from go program.
GOARCH=386 GOOS=windows CGO_ENABLED=1 GOPATH=`pwd` CC=i686-w64-mingw32-gcc go build -o go-shared-lib.dll -buildmode=c-shared go-shared-libs
GOARCH=amd64 GOOS=windows CGO_ENABLED=1 GOPATH=`pwd` CC=x86_64-w64-mingw32-gcc go build -o go-shared-lib.dll -buildmode=c-shared go-shared-libs

Import path difference using "build" and "tool compile"

I'm trying to compile this example (which is saved as main.go in current working dir):
package main
import (
"flag"
"log"
"runtime"
"github.com/nats-io/go-nats"
)
// rest of the code
This works:
$ go build ./
But this does not:
$ go tool compile -o main.o main.go
main.go:8:2: can't find import: "github.com/nats-io/go-nats"
Both of examples above were run on the same terminal with same environment variables, so I don't understand why the 2nd one does not work. I've tried -D and -I parameters with various directories as $GOPATH, $GOPATH/src and so on with no success.
I know what it's considered best practice to not use go tool compile and so on, but my goal is to add my go source to existing C++ makefile project and useage of go tool will make it more consistent.
Correct syntax of go tool compile is as follows:
go tool compile -I $GOPATH/pkg/linux_amd64 -o main.o main.go
The problem was that by default, compile performs lookup only in $GOROOT and ignores $GOPATH.

How to import non-standard-library packages use gccgo

First of all, all these code can be built successfully using go tool(e.g. go build, go install)
For say I got an a.go which tries to import a non-standard-library pkg from github:
package a
import (
"fmt"
"github.com/usr/pkg"
)
func init() {
fmt.Println("Import pkg", pkg.somevar)
}
when I try to compile it with gccgo:
$ gccgo -c a.go
a.go:5:20: error: import file ‘github.com/usr/pkg’ not found
...
And then I read the Setting up and using gccgo , it says
When you import the package FILE with gccgo, it will look for the
import data in the following files, and use the first one that it
finds.
FILE.gox
FILE.o
libFILE.so
libFILE.a
The gccgo compiler will look in the current directory for import files
So I cp the $GOPATH/pkg/github.com/usr/pkg.a to the current directory and rename it as libpkg.a.
It seems failed again:
$ gccgo -c a.go
a.go:9:4: error: libpkg.a: malformed archive header name at 8
a.go:9:4: error: libpkg.a exists but does not contain any Go export data
And yes, I use gccgo 4.7.2
I've got no experience woking with gcc, so I search for some help here.
The simplest way is to use the go command from the gc distribution, and run go build -compiler gccgo .
Your idea of copying pkg.a does not work because pkg.a was not built with gccgo.

Resources