1. 写在最前面

问题:笔者用 kaniko 构建了一个 docker 镜像,基础镜像是基于 Alpine。构建好后,运行编译后的 go 二进制程序。一直提示下面的错误:

# ./example.exe
sh: ./example.exe: not found

在此处明显看到 example.exe 的文件是存在的。怀疑是 sh有问题,笔者又按照了 bash 后继续尝试

# apk add bash
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz(1/4) Installing ncurses-terminfo-base (6.2_p20210109-r0)
(2/4) Installing ncurses-libs (6.2_p20210109-r0)(3/4) Installing readline (8.1.0-r0)
(4/4) Installing bash (5.1.0-r0)
Executing bash-5.1.0-r0.post-install
Executing busybox-1.32.1-r6.trigger
OK: 8 MiB in 18 packages
# bash
bash-5.1# /bin/bash example.exe
example.exe: example.exe: cannot execute binary file

到此处错误变为了二进制无法执行。百思不得其解的笔者,只能硬着头皮继续排查。

2. 排查方向

2.1 非 root 用户

# whoami

注:此选项排除,笔者是以 root 的用户登陆的。若此处为非 root 用户,可以执行 chmod +x program

2.2 编译目标与执行环境不同

bash-5.1# file example.exeexample.exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=r403apSf9gVnhAa92Ma3/CtPwwvYC_Td44-00QCD7/LNwlhyvp3IK5oGI_6pca/H9YPNEE1hvs-0EBN4ZA0, not stripped
bash-5.1# uname -a
Linux d015a01bdfbc 4.15.0-158-generic #166-Ubuntu SMP Fri Sep 17 19:37:52 UTC 2021 x86_64 Linux

注:执行 file 和 uname 命令对比发现,程序的编译目标与执行环境相同,排除此选项

2.3 程序需要的动态库或静态库缺失

「排除一切不可能的,剩下的就算再不可能也是真相」

bash-5.1# ldd example.exe
        /lib64/ld-linux-x86-64.so.2 (0x7ff2cb417000)        libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7ff2cb417000)
        libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7ff2cb417000)
Error relocating example.exe: __vfprintf_chk: symbol not found
Error relocating example.exe: __fprintf_chk: symbol not found
bash-5.1# ls /lib64
ls: /lib64: No such file or directory

注:检查程序的依赖库,发现本地缺少 lib64 的动态依赖。原因是 Alpine 使用的标准库与大多数发行版不同,它使用的是musl libc ,这个库虽然相比 glibc 更小,更简单,更安全,但是与大家常用的标准 glibc 并不兼容。

2.3.1 解决办法

采用维基百科的建议方案是,安装 glic 作为 musl libc 的补充的方案。

If you want to run glibc programs in Alpine Linux, there are a few ways of doing so. You could install glibc as additional to musl (you would have to do this manually), or you could do it the easy way and use either Flatpak (the easiest) or a chroot.

Because there are different use cases, this is just a slight overview about what's possible and what's intelligent.

安装 build-base gcompat

bash-5.1# apk add build-base gcompat(1/22) Upgrading musl (1.2.2-r0 -> 1.2.2-r1)
(2/22) Installing libgcc (10.2.1_pre1-r3)(3/22) Installing libstdc++ (10.2.1_pre1-r3)
(4/22) Installing binutils (2.35.2-r1)(5/22) Installing libgomp (10.2.1_pre1-r3)
(6/22) Installing libatomic (10.2.1_pre1-r3)(7/22) Installing libgphobos (10.2.1_pre1-r3)
(8/22) Installing gmp (6.2.1-r0)
(9/22) Installing isl22 (0.22-r0)
(10/22) Installing mpfr4 (4.1.0-r0)
(11/22) Installing mpc1 (1.2.0-r0)
(12/22) Installing gcc (10.2.1_pre1-r3)
(13/22) Installing musl-dev (1.2.2-r1)
(14/22) Installing libc-dev (0.7.2-r3)
(15/22) Installing g++ (10.2.1_pre1-r3)
(16/22) Installing make (4.3-r0)
(17/22) Installing fortify-headers (1.1-r0)
(18/22) Installing patch (2.7.6-r7)
(19/22) Installing build-base (0.5-r2)
(20/22) Installing musl-obstack (1.1-r1)
(21/22) Installing libucontext (1.0-r0)
(22/22) Installing gcompat (1.0.0-r1)
Executing busybox-1.32.1-r6.trigger
OK: 198 MiB in 41 packages
bash-5.1# ./example.exe
Long: 0, ip:time="2022-01-17T12:04:04Z" level=info msg="[NewServer] Start to run http server" file="example.go:30"

4. 等等

笔者开发的是 go 语言,默认应该就是静态链接。为什么此处使用了动态链接呢?重新关注下 file 命令的执行。

bash-5.1# file example.exe
example.exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=r403apSf9gVnhAa92Ma3/CtPwwvYC_Td44-00QCD7/LNwlhyvp3IK5oGI_6pca/H9YPNEE1hvs-0EBN4ZA0, not stripped

go 本身没有依赖 glibc,但是笔者开发的程序有 cgo 的使用,所以有 libc 的使用,而笔者本地的编译器 clang 和编译镜像里的编译器 gcc 对依赖库的处理有所不同。

4.1 真·解决办法

4.1.1 使用 CGO_ENABLED=0

使用 CGO_ENABLED=0 关掉允许动态链接。

4.1.2 更换依赖的基础镜像

Alpine Linux 默认缺少 glibc 的动态库,改为 ubuntu 18.04.x 可解。

5. 碎碎念

至此,上周踩到的一个神奇的问题才算是初步解决了,当然你要是想知道编译器的区别,也还是可以深究的,但是笔者还有开发要搞,就先记录的到这里吧。

  • 如果一个人影响到了你的情绪,你的焦点应该放在控制自己的情绪上,而不是影响你情绪的人身上。只有这样,才能真正的自信起来。
  • 如果觉得身边的一切都太不如意,那就去喜欢的地方,做喜欢的事,买喜欢的东西。
  • 如果偶尔快乐,那就是生活的意义。
  • 6. 参考资料

  • Go Execution Modes
  • how can i resolve the error cannot execute binary file
  • Running glibc programs
  • 不要轻易使用 Alpine 镜像来构建 Docker 镜像,有坑!
  • No such file or directory
  • interpreter /lib64/ld-linux-x86-64.so.2?
  • Go-compiled binary won't run in an alpine docker container on Ubuntu host
  • 分类:
    后端
    标签: