Docker Alpine Executable Binary Not Found Even If in Path

Docker Alpine executable binary not found even if in PATH

On Alpine Linux, the not found error is a typical symptom of dynamic link failure. It is indeed a rather confusing error by musl's ldd linker.

Most of the world Linux software is linked against glibc, the GNU libc library (libc provides the standard C library and POSIX API). Most Linux distributions are based on glibc. OTOH, Alpine Linux is based on the musl libc library, which is a minimal implementation and strictly POSIX compliant. Executables built on glibc distributions depend on /lib/x86_64-linux-gnu/libc.so.6, for example, which is not available on Alpine (unless, they are statically linked).

Except for this dependency, it's important to note that while musl attempts to maintain glibc compatibility to some extent, it is far from being fully compatible, and complex software that's built against glibc won't work with musl-libc, so simply symlinking /lib/ld-musl-x86_64.so.1 to the glibc path isn't likely going to work.

Generally, there are several ways for running glibc binaries on Alpine:

  1. Install one the glibc compatibility packages, libc6-compat or gcompat:
# apk add gcompat
apk add libc6-compat

Both packages provide a light weight glibc compatibility layer which may be suitable for running simple glibc applications.
libc6-compat implements glibc compatibility APIs and provides symlinks to glibc shared libraries such as libm.so, libpthread.so and libcrypt.so. The gcompat package is based on Adelie Linux gcompat project and does the same but provides a single library libgcompat.so. Both libraries install loader stubs. Depdending on the application, one of them may work while the other won't, so it's good to try both.


  1. Install proper glibc on Alpine, for providing all glibc methods and functionalities. There are glibc builds available for Alpine, which should be installed in the following procedure (example):
# Source: https://github.com/anapsix/docker-alpine-java

ENV GLIBC_REPO=https://github.com/sgerrand/alpine-pkg-glibc
ENV GLIBC_VERSION=2.30-r0

RUN set -ex && \
apk --update add libstdc++ curl ca-certificates && \
for pkg in glibc-${GLIBC_VERSION} glibc-bin-${GLIBC_VERSION}; \
do curl -sSL ${GLIBC_REPO}/releases/download/${GLIBC_VERSION}/${pkg}.apk -o /tmp/${pkg}.apk; done && \
apk add --allow-untrusted /tmp/*.apk && \
rm -v /tmp/*.apk && \
/usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib

  1. Use statically linked executables. Static executables don't carry dynamic dependencies and could run on any Linux.

  2. Alternatively, the software may be built from source on Alpine.

For LibreDWG, let's first verify the issue:

/usr/local/bin # ./dwg2dxf
/bin/sh: ./dwg2dxf: not found
/usr/local/bin
/usr/local/bin # ldd ./dwg2dxf
/lib64/ld-linux-x86-64.so.2 (0x7fd375538000)
libredwg.so.0 => /usr/local/lib/libredwg.so.0 (0x7fd3744db000)
libm.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7fd375538000)
libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7fd375538000)
Error relocating /usr/local/lib/libredwg.so.0: __strcat_chk: symbol not found
Error relocating /usr/local/lib/libredwg.so.0: __snprintf_chk: symbol not found
Error relocating /usr/local/lib/libredwg.so.0: __memcpy_chk: symbol not found
Error relocating /usr/local/lib/libredwg.so.0: __stpcpy_chk: symbol not found
Error relocating /usr/local/lib/libredwg.so.0: __strcpy_chk: symbol not found
Error relocating /usr/local/lib/libredwg.so.0: __printf_chk: symbol not found
Error relocating /usr/local/lib/libredwg.so.0: __fprintf_chk: symbol not found
Error relocating /usr/local/lib/libredwg.so.0: __strncat_chk: symbol not found
Error relocating /usr/local/lib/libredwg.so.0: __sprintf_chk: symbol not found
Error relocating ./dwg2dxf: __snprintf_chk: symbol not found
Error relocating ./dwg2dxf: __printf_chk: symbol not found
Error relocating ./dwg2dxf: __fprintf_chk: symbol not found

You can see that dwg2dxf depends on several glibc symbols.
Now, let's follow option 2 for installing glibc:

/usr/src/app # cd /usr/local/bin
/usr/local/bin # ls
dwg2SVG dwg2dxf dwgadd dwgbmp dwgfilter dwggrep dwglayers dwgread dwgrewrite dwgwrite dxf2dwg dxfwrite
/usr/local/bin # ./dwg2dxf
/bin/sh: ./dwg2dxf: not found
/usr/local/bin # export GLIBC_REPO=https://github.com/sgerrand/alpine-pkg-glibc && \
> export GLIBC_VERSION=2.30-r0 && \
> apk --update add libstdc++ curl ca-certificates && \
> for pkg in glibc-${GLIBC_VERSION} glibc-bin-${GLIBC_VERSION}; \
> do curl -sSL ${GLIBC_REPO}/releases/download/${GLIBC_VERSION}/${pkg}.apk -o /tmp/${pkg}.apk; done && \
> apk add --allow-untrusted /tmp/*.apk && \
> rm -v /tmp/*.apk && \
> /usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib
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/1) Installing curl (7.74.0-r1)
Executing busybox-1.32.1-r3.trigger
OK: 629 MiB in 126 packages
(1/2) Installing glibc (2.30-r0)
(2/2) Installing glibc-bin (2.30-r0)
Executing glibc-bin-2.30-r0.trigger
/usr/glibc-compat/sbin/ldconfig: /usr/local/lib/libredwg.so.0 is not a symbolic link
/usr/glibc-compat/sbin/ldconfig: /usr/glibc-compat/lib/ld-linux-x86-64.so.2 is not a symbolic link
OK: 640 MiB in 128 packages
removed '/tmp/glibc-2.30-r0.apk'
removed '/tmp/glibc-bin-2.30-r0.apk'
/usr/glibc-compat/sbin/ldconfig: /usr/glibc-compat/lib/ld-linux-x86-64.so.2 is not a symbolic link

/usr/glibc-compat/sbin/ldconfig: /usr/local/lib/libredwg.so.0 is not a symbolic link

Voila:

/usr/local/bin # ./dwg2dxf

Usage: dwg2dxf [-v[N]] [--as rNNNN] [-m|--minimal] [-b|--binary] DWGFILES...

Installed Go binary not found in path on Alpine Linux Docker


RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2

Since the musl and glibc so are compatible, you can make this symlink and it will fix the missing dependency.

Cannot run executables with Alpine and Busybox docker images

TL;DR:

The error lua: not found is a symptom of a dynamic linking failure, and is common when trying to run mainland Linux binaries on musl-libc based Linux, such as Alpine Linux and the busybox based image.

To fix this, switch to a lightweight glibc based image (e.g. Debian Slim) or install glibc on the Alpine container. Making this work for BusyBox is unpractical.

Full Explanation:

A bit of background. libc, the standard C library, provides the C and POSIX APIs to Linux programs and is an intrinsic part of the Linux system. Most Linux distributions are based on glibc, the GNU C library. However, both Alpine Linux and BusyBox images are based on musl standard C library, which is generally incompatible with glibc. Consequently, executables that are built on glibc-based distros such as Ubuntu, Debian or Arch Linux, won't work out of the box on Alpine Linux or BusyBox.

The linking error is manifested when trying to run the glibc executable. You could verify this by switching the image to alpine and running ldd:

/ataraxis # ldd /usr/local/bin/luarocks 
/lib64/ld-linux-x86-64.so.2 (0x7fbd14310000)
libdl.so.2 => /lib64/ld-linux-x86-64.so.2 (0x7fbd14310000)
libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7fbd14310000)
libm.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7fbd14310000)
libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7fbd14310000)
Error relocating /usr/local/bin/luarocks: __fprintf_chk: symbol not found
Error relocating /usr/local/bin/luarocks: makecontext: symbol not found
Error relocating /usr/local/bin/luarocks: setcontext: symbol not found
Error relocating /usr/local/bin/luarocks: __register_atfork: symbol not found
Error relocating /usr/local/bin/luarocks: __strdup: symbol not found
Error relocating /usr/local/bin/luarocks: __libc_alloca_cutoff: symbol not found
Error relocating /usr/local/bin/luarocks: __stpncpy: symbol not found
Error relocating /usr/local/bin/luarocks: __syslog_chk: symbol not found
Error relocating /usr/local/bin/luarocks: getcontext: symbol not found
Error relocating /usr/local/bin/luarocks: __open_2: symbol not found
Error relocating /usr/local/bin/luarocks: errno: symbol not found

The simple and safe solution for working with Alpine Linux is installing compatible software using the Alpine package manager, apk. However, the desired package may not exist for Alpine, for the specific package version may not be available. In this case, you have two options:

  • Use a glibc-based Docker image, such as Debian slim image (e.g. debian:buster-slim - 27MB compressed), instead of Alpine/BusyBox
  • Install glibc on the musl based image, making it compatible with glibc programs, but also increasing the image size considerably.

Why not BusyBox:

BusyBox is not suitable for this customization. Since it doesn't even have a package manager, all changes and additions to it must be be done manually. It is surely an extremely tedious and lengthy procedure. Alpine is still a very lightweight image, where you could install glibc fairly simply.

Updating the image to Alpine with glibc:

First, replace busybox with Alpine, preferably alpine:3.14 which is the latest Alpine release (in both places - line 1 and line 37).

Second, add the following lines after the COPY command:

ENV GLIBC_REPO=https://github.com/sgerrand/alpine-pkg-glibc
ENV GLIBC_VERSION=2.30-r0
RUN set -ex && \
apk --update add libstdc++ curl ca-certificates && \
for pkg in glibc-${GLIBC_VERSION} glibc-bin-${GLIBC_VERSION}; \
do curl -sSL ${GLIBC_REPO}/releases/download/${GLIBC_VERSION}/${pkg}.apk -o /tmp/${pkg}.apk; done && \
apk add --allow-untrusted /tmp/*.apk && \
rm -v /tmp/*.apk && \
/usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib

This will install glibc on the Alpine container. Finally, run luarocks.

For reference, I have posted the docker build output on pastebin.

Docker image with Alpine Linux: an executable file is definitely there but cannot be found while trying to execute

Alpine makes use of musl which is a minimalistic libc. My guess is that your binary is using non-standard functions that do not exist under Alpine.

I see two solutions to that :

  • Try to install glibc in your docker container
  • Probably the easiest solution : try to find a Docker image that uses it as default (a minimalistic Debian/Ubuntu Docker should do it)

docker: executable file not found in $PATH

When you use the exec format for a command (e.g., CMD ["grunt"], a JSON array with double quotes), it will be executed without a shell. This means that most environment variables will not be present.

If you specify your command as a regular string (e.g. CMD grunt) then the string after CMD will be executed with /bin/sh -c.

More info on this is available in the CMD section of the Dockerfile reference.

Why can't I execute binary copied into a container?

For a binary, this most likely indicates a missing dynamic library. You can run ldd /usr/local/bin/my_bin to see all the libraries that binary uses. With alpine, the most common library to be missing from an externally compiled program is libc. Alpine is built with musl instead of libc, and therefore you'll want to compile programs specifically for Alpine.

For others that may encounter this error in docker containers, I cover various issues in my faq presentation and other questions on the site.

Go-compiled binary won't run in an alpine docker container on Ubuntu host

By default, if using the net package a build will likely produce a binary with some dynamic linking, e.g. to libc. You can inspect dynamically vs. statically link by viewing the result of ldd output.bin

There are two solutions I've come across:

  • Disable CGO, via CGO_ENABLED=0
  • Force the use of the Go implementation of net dependencies, netgo via go build -tags netgo -a -v, this is implemented for a certain platforms

From https://golang.org/doc/go1.2:

The net package requires cgo by default because the host operating system must in general mediate network call setup. On some systems, though, it is possible to use the network without cgo, and useful to do so, for instance to avoid dynamic linking. The new build tag netgo (off by default) allows the construction of a net package in pure Go on those systems where it is possible.

The above assumes that the only CGO dependency is the standard library's net package.



Related Topics



Leave a reply



Submit