Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to statically link Go programs, even with cgo #207

Merged
merged 37 commits into from
May 8, 2022
Merged

Conversation

probonopd
Copy link
Owner

@probonopd probonopd commented May 7, 2022

Use zig as a cc substitute to statically link Go programs, even with cgo, using musl libc. Doing it this way has the advantage that no Docker container or chroots are needed for building multiple architectures.

Continuation of #203

@probonopd
Copy link
Owner Author

Looks like a couple of AppImages get built successfully, but then:

# github.com/probonopd/go-appimage/src/appimaged
loadinternal: cannot find runtime/cgo
/opt/hostedtoolcache/go/1.17.9/x64/pkg/tool/linux_amd64/link: running /usr/local/musl/bin/musl-gcc failed: exit status 1
/usr/bin/ld: i386 architecture of input file `/tmp/go-link-752300456/go.o' is incompatible with i386:x86-64 output
collect2: error: ld returned 1 exit status

Seems like there are some leftovers from earlier runs?

@probonopd
Copy link
Owner Author

So, we can build static binaries but currently only for the host architecture.
Any help appreciated.

@probonopd probonopd added the help wanted Extra attention is needed label May 7, 2022
@probonopd probonopd marked this pull request as draft May 7, 2022 19:48
@probonopd
Copy link
Owner Author

probonopd commented May 7, 2022

Possibly use https://dev.to/kristoff/zig-makes-go-cross-compilation-just-work-29ho
Thanks @dominikh for the hint and @kristoff-it for the article.

Looks like it can be used with musl libc:
ziglang/zig#514 (comment)

@kristoff-it
Copy link

You can use this approach to deal with the outer directory from the Zig tarball:

https://github.com/ziglang/www.ziglang.org/blob/master/.github/workflows/build.yml#L30

@kristoff-it
Copy link

Are you sure you need musleabi instead of just musl for aarch64?

@probonopd
Copy link
Owner Author

For this arch we error out.

CC: zig cc -target aarch64-linux-musleabi
+ zig env
/usr/local/bin/zig
{
 "zig_exe": "/usr/local/bin/zig",
 "lib_dir": "/usr/local/bin/lib",
 "std_dir": "/usr/local/bin/lib/std",
 "global_cache_dir": "/home/runner/.cache/zig",
 "version": "0.10.0-dev.2112+0df28f9d4"
}
+ CGO_ENABLED=1
+ go build -ldflags '-v -linkmode=external' --tags extended /home/runner/work/go-appimage/go-appimage/src/appimaged
# runtime/cgo
_cgo_export.c:3:10: fatal error: 'stdlib.h' file not found
#include <stdlib.h>
         ^~~~~~~~~~
1 error generated.
Error: Process completed with exit code 2.

@probonopd
Copy link
Owner Author

Are you sure you need musleabi instead of just musl for aarch64?

No, it was a wild guess.

@kristoff-it
Copy link

You probably do need to use a different musl version for 32bit arm though. I'm not familiar enough but IIRC you need to know a bit more about your target. @MasterQ32 where can one find a concise guide that explains how to select the correct ABI for arm?

@andrewrk
Copy link

andrewrk commented May 8, 2022

zig targets | jq .libc:

[
  "aarch64_be-linux-gnu",
  "aarch64_be-linux-musl",
  "aarch64_be-windows-gnu",
  "aarch64-linux-gnu",
  "aarch64-linux-musl",
  "aarch64-windows-gnu",
  "aarch64-macos-gnu",
  "aarch64-macos-gnu",
  "armeb-linux-gnueabi",
  "armeb-linux-gnueabihf",
  "armeb-linux-musleabi",
  "armeb-linux-musleabihf",
  "armeb-windows-gnu",
  "arm-linux-gnueabi",
  "arm-linux-gnueabihf",
  "arm-linux-musleabi",
  "arm-linux-musleabihf",
  "thumb-linux-gnueabi",
  "thumb-linux-gnueabihf",
  "thumb-linux-musleabi",
  "thumb-linux-musleabihf",
  "arm-windows-gnu",
  "csky-linux-gnueabi",
  "csky-linux-gnueabihf",
  "i386-linux-gnu",
  "i386-linux-musl",
  "i386-windows-gnu",
  "m68k-linux-gnu",
  "m68k-linux-musl",
  "mips64el-linux-gnuabi64",
  "mips64el-linux-gnuabin32",
  "mips64el-linux-musl",
  "mips64-linux-gnuabi64",
  "mips64-linux-gnuabin32",
  "mips64-linux-musl",
  "mipsel-linux-gnueabi",
  "mipsel-linux-gnueabihf",
  "mipsel-linux-musl",
  "mips-linux-gnueabi",
  "mips-linux-gnueabihf",
  "mips-linux-musl",
  "powerpc64le-linux-gnu",
  "powerpc64le-linux-musl",
  "powerpc64-linux-gnu",
  "powerpc64-linux-musl",
  "powerpc-linux-gnueabi",
  "powerpc-linux-gnueabihf",
  "powerpc-linux-musl",
  "riscv64-linux-gnu",
  "riscv64-linux-musl",
  "s390x-linux-gnu",
  "s390x-linux-musl",
  "sparc-linux-gnu",
  "sparcv9-linux-gnu",
  "wasm32-freestanding-musl",
  "wasm32-wasi-musl",
  "x86_64-linux-gnu",
  "x86_64-linux-gnux32",
  "x86_64-linux-musl",
  "x86_64-windows-gnu",
  "x86_64-macos-gnu",
  "x86_64-macos-gnu",
  "x86_64-macos-gnu"
]

@probonopd
Copy link
Owner Author

It built something, which is good.

But the binaries are neither static nor stripped it seems:

unzip artifacts.zip
chmod +x *x86_64.AppImage
./appimagetool-694-x86_64.AppImage --appimage-extract
file squashfs-root/usr/bin/appimagetool
squashfs-root/usr/bin/appimagetool: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, Go BuildID=MdOg4ubY6Wqu6zBUkYlO/-tfmbJID3VCB37_YQMfW/DwYNCDuhb2uwRRKQqxeb/AwznReSfU21Oz_8xQP20, with debug_info, not stripped

@kristoff-it
Copy link

Can you run ldd on it?

@probonopd
Copy link
Owner Author

probonopd commented May 8, 2022

It just executes it, no ldd output. So it seems "semi-static"?

@kristoff-it
Copy link

kristoff-it commented May 8, 2022

ldd should say "not a dynamic executable" or list the dependencies, it shouldn't run anything. You probably ran ld (great names I know :^))

@kristoff-it
Copy link

kristoff-it commented May 8, 2022

note that those flags that you added in the last commit (--no-gc-sections) are only useful if you remove all the external linker stuff from the Go command. It's meant to fix an issue when using Go as the linker. If you don't plan gong that route, you don't want those, so either try them with the simplified Go build command, or don't add them.

@probonopd
Copy link
Owner Author

ed62251 behaves best so far.

FreeBSD% file squashfs-root/usr/bin/appimagetool
squashfs-root/usr/bin/appimagetool: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=1e3usIBxKzQmAPw5WMiE/P9QU1M_apYHeqahwX4hP/DwYNCDuhb2uwRRKQqxeb/8V-Y2SxUJjeCL_h4GlnE, with debug_info, not stripped
FreeBSD% ldd squashfs-root/usr/bin/appimagetool
ldd: squashfs-root/usr/bin/appimagetool: not a dynamic ELF executable

But it is still not stripped...

@probonopd
Copy link
Owner Author

What the *

strip appimaged
strip: Unable to recognise the format of the input file `appimaged'

@probonopd
Copy link
Owner Author

What got built is looking good now. Next step: Functionality testing on a Linux client machine.

@probonopd
Copy link
Owner Author

First quick tests looking good! @kristoff-it @andrewrk thank you very, very much for your help.

@probonopd probonopd marked this pull request as ready for review May 8, 2022 06:43
@probonopd probonopd merged commit 914111a into master May 8, 2022
@probonopd probonopd deleted the static-link branch May 8, 2022 06:47
@motiejus
Copy link

motiejus commented May 9, 2022

Re. stripping: what about passing -Wl,--strip-all to the CFLAGS?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants