This seems to imply that Go's binaries are otherwise compatible with multiple platforms like amd64 and arm64, other than the issue with linking dynamic libraries. I suspect that's not true either even if it might be technically possible to achieve it through some trickery (and why not risc-v, and other architectures too?).
Go is portable, until it isn't
21–30 of 138 posts
Re: Go is portable, until it isn't
#22Earlier quoted context omitted.
Of course you still need one binary per CPU architecture. But when you rely on a dynamic link, you need to build from the same architecture as the target system. At that point cross-compiling stops being reliable.
Is it some tooling issue? Why is is an issue to cross-compile programs with dynamic linking?
Re: Go is portable, until it isn't
#23I ran into this issue when porting term.everything[0] from typescript to go. I had some c library dependencies that I did need to link, so I had to use cgo. My solution was to do the build process on alpine linux[1] and use static linking[2]. This way it statically links musl libc, which is much friendlier with static linking than glibc. Now, I have a static binary that runs in alpine, Debian, and even bare container…
1. https://github.com/mmulet/term.everything/blob/main/resource...>
Re: Go is portable, until it isn't
#24Earlier quoted context omitted.
Is it some tooling issue? Why is is an issue to cross-compile programs with dynamic linking?
In general, cross compilers can do dynamic linking.
The usual workaround, I think, is to use dlopen/dlsym from within the program. This is how the Nim language handles libraries in the general case: at compile time, C imports are converted into a block of dlopen/dl* calls, with compiler options for indicating some (or all) libraries should be passed to the linker instead, either for static or dynamic linking.
Alternatively I think you could "trick" the linker with a stub library just containing the symbol names it wants, but never tried that.
Re: Go is portable, until it isn't
#25Earlier quoted context omitted.
Of course you still need one binary per CPU architecture. But when you rely on a dynamic link, you need to build from the same architecture as the target system. At that point cross-compiling stops being reliable.
Is it some tooling issue? Why is is an issue to cross-compile programs with dynamic linking?
Traditionally, cross-compilers generally didn't even work the way that the Zig and Go toolchains approach it—achieving cross-compilation could be expected to be a much more trying process. The Zig folks and the Go folks broke with tradition by choosing to architect their compilers more sensibly for the 21st century, but the effects of the older convention remains.
Re: Go is portable, until it isn't
#26Once you use CGO, portability is gone. Your binary is no longer staticly compiled. This can happen subtley without you knowing it. If you use a function in the standard library that happens to call into a CGO function, you are no longer static. This happens with things like os.UserHomeDir or some networking things like DNS lookups. You can "force" go to do static compiling by disabling CGO, but that means you can't u…
> This happens with things like os.UserHomeDir or some networking things like DNS lookups. The docs do not mention this CGO dependency, are you sure? https://pkg.go.dev/os#UserHomeDir
Re: Go is portable, until it isn't
#27You hit this real quick when trying to build container images from the scratch. Theoretically you can drop a Go binary into a blank rootfs and it will run. This works most of the time, but anything that depends on Go's Postgres client requires libpq which requires libc. Queue EFILE runtime errors after running the container.
Re: Go is portable, until it isn't
#28Interesting that it uses the C API to collect journals. I would’ve thought to just invoke journalctl CLI. On platforms like macOS where the CLI doesn’t exist it’s an error when you exec, not a build time error.
The weird bit is the analysis[1], which complains that a Go binary doesn't run on Alpine Linux, a system which is explicitly and intentionally (also IMHO ridiculously, but that's editorializing) binary-incompatible with the stable Linux C ABI as it's existed for almost three decades now. It's really no more "Linux" than is Android, for the same reason, and you don't complain that your Go binaries don't run there.
[1] I'll just skip without explaination how weird it was to see the author complain that the build breaks because they can't get systemd log output on... a mac.
Re: Go is portable, until it isn't
#29Re: Go is portable, until it isn't
#30Earlier quoted context omitted.
> This happens with things like os.UserHomeDir or some networking things like DNS lookups. The docs do not mention this CGO dependency, are you sure? https://pkg.go.dev/os#UserHomeDir
I was surprised too, that I had to check the docs, so I assume the user was misinformed.
There are multiple standard library functions that do it.. I recall some in "net" and some in "os".