linuxdockershellalpine-linux

Docker Alpine executable binary not found even if in PATH


I have an alpine running container which contains some binaries in usr/local/bin

When I ls the content of usr/local/bin I got this output :

/usr/local/bin # ls
dwg2SVG     dwg2dxf     dwgadd      dwgbmp      dwgfilter   dwggrep     dwglayers   dwgread     dwgrewrite  dwgwrite    dxf2dwg     dxfwrite

Which is what I expected. However if I execute one of these binaries by calling it I got a not found error from the shell :

/usr/local/bin # dwg2dxf
sh: dwgread: not found
/usr/local/bin # ./dwg2dxf
sh: ./dwgread: not found

I tested my $PATH which seems to be correct :

/usr/local/bin # echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

How can I make those binaries callable or "foundable" ? Did I miss something in my Dockerfile build ? I suppose that there is something with the ldconfig command in alpine that went wrong but I'm not sure.

EDIT

As suggested in one answer here I executed the file command and here is the output :

/usr/local/bin # file dwg2dxf
dwgread: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=7835d4a42651a5fb7bdfa2bd8a76e40096bacb07, with debug_info, not stripped

Thoses binaries are from the LibreDWG Official repository as well as the first part of my Dockerfile. Here is the complete Dockerfile :

# podman/docker build -t libredwg .
############################
# STEP 1 build package from latest tar.xz
############################

FROM python:3.7.7-buster AS extracting
# libxml2-dev is broken so we need to compile it by our own
ARG LIBXML2VER=2.9.9
RUN apt-get update && \
    apt-get install -y --no-install-recommends autoconf libtool swig texinfo \
            build-essential gcc libxml2 python3-libxml2 libpcre2-dev libpcre2-32-0 curl \
            libperl-dev libxml2-dev && \
    mkdir libxmlInstall && cd libxmlInstall && \
    wget ftp://xmlsoft.org/libxml2/libxml2-$LIBXML2VER.tar.gz && \
    tar xf libxml2-$LIBXML2VER.tar.gz && \
    cd libxml2-$LIBXML2VER/ && \
    ./configure && \
    make && \
    make install && \
    cd /libxmlInstall && \
    rm -rf gg libxml2-$LIBXML2VER.tar.gz libxml2-$LIBXML2VER
WORKDIR /app
RUN tarxz=`curl --silent 'https://ftp.gnu.org/gnu/libredwg/?C=M;O=D' | grep '.tar.xz<' | \
         head -n1|sed -E 's/.*href="([^"]+)".*/\1/'`; \
    echo "latest release $tarxz"; \
    curl --silent --output "$tarxz" https://ftp.gnu.org/gnu/libredwg/$tarxz && \
    mkdir libredwg && \
    tar -C libredwg --xz --strip-components 1 -xf "$tarxz" && \
    rm "$tarxz" && \
    cd libredwg && \
    ./configure --disable-bindings --enable-release && \
    make -j `nproc` && \
    mkdir install && \
    make install DESTDIR="$PWD/install" && \
    make check DOCKER=1 DESTDIR="$PWD/install"

############################
# STEP 2 install into stable-slim
############################

# pull official base image
FROM osgeo/gdal:alpine-normal-latest

# set work directory
WORKDIR /usr/src/app



# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# copy requirements file
COPY ./requirements.txt /usr/src/app/requirements.txt

# install dependencies
RUN set -eux \
    && apk add --no-cache --virtual .build-deps build-base \
        py3-pip libressl-dev libffi-dev gcc musl-dev python3-dev postgresql-dev\
    && pip3 install --upgrade pip setuptools wheel \
    && pip3 install -r /usr/src/app/requirements.txt \
    && rm -rf /root/.cache/pip

# Install libredwg binaries
COPY --from=extracting /app/libredwg/install/usr/local/bin/* /usr/local/bin/
COPY --from=extracting /app/libredwg/install/usr/local/include/* /usr/local/include/
COPY --from=extracting /app/libredwg/install/usr/local/lib/* /usr/local/lib/
COPY --from=extracting /app/libredwg/install/usr/local/share/* /usr/local/share/
RUN ldconfig /usr/local/bin/
RUN ldconfig /usr/local/include/
RUN ldconfig /usr/local/lib/
RUN ldconfig /usr/local/share/

# copy project
COPY . /usr/src/app/

Solution

  • 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...