Hi @Finagolfin I'm having another try at cross-compiling a dynamically linked binary for grpc-swift under Ubuntu Linux 24.04, and then running patchelf
to patch the binaries to run under Alpine Linux. My JSON file currently looks like this:
{
"version": 1,
"target": "aarch64-unknown-linux-gnu",
"toolchain-bin-dir": "/usr/aarch64-linux-gnu/bin",
"sdk": "/root/aarch64/usr/lib/swift",
"extra-cc-flags": [
"-fPIC"
],
"extra-cpp-flags": [
"-lstdc++",
"-I",
"/usr/aarch64-linux-gnu/include/c++/13",
"-I",
"/usr/aarch64-linux-gnu/include/c++/13/aarch64-linux-gnu/"
],
"extra-swiftc-flags": [
"-resource-dir",
"/root/aarch64/usr/lib/swift"
]
}
Build succeeds with no errors. When I try to run the resulting binary, I get this error:
protoc-gen-swift: error while loading shared libraries: libswiftCore.so: cannot open shared object file: No such file or directory
If I add a step to my build Dockerfile to run ldd
on the binary to see what patchelf is trying to patch in, I get the following result showing libswiftCore.so is indeed not found:
#23 [grpc_swift 5/5] RUN ldd /protoc-gen-swift/protoc-gen-swift
#23 0.389 linux-vdso.so.1 (0x00007f2d30710000)
#23 0.390 libswiftCore.so => not found
#23 0.390 libswift_Concurrency.so => not found
#23 0.390 libswift_StringProcessing.so => not found
#23 0.390 libswift_RegexParser.so => not found
#23 0.390 libswiftGlibc.so => not found
#23 0.390 libBlocksRuntime.so => not found
#23 0.390 libdispatch.so => not found
#23 0.390 libswiftDispatch.so => not found
#23 0.390 libFoundation.so => not found
#23 0.390 libFoundationEssentials.so => not found
#23 0.390 libFoundationInternationalization.so => not found
#23 0.390 libm.so.6 => /lib/aarch64-linux-gnu/libm.so.6 (0x00007f2d27af0000)
#23 0.390 libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x00007f2d27930000)
#23 0.390 /lib/ld-linux-aarch64.so.1 (0x00007f2d306b0000)
#23 DONE 0.5s
Why doesn't the compiler error instead of building a binary that links to non-existent files? Stepping inside the build container, I have unpacked the swift-6.1-RELEASE-ubuntu24.04-aarch64.tar.gz
to /root/aarch64
. I can see all of the "not found" .so files located in /root/aarch64/usr/lib/swift/linux
. No matter how I mess around with the toolchain-bin-dir
, sdk
and resource-dir
variables, I cannot build a binary that correctly links to these files. In particular, I have tried setting /
, /root/aarch64
and every intermediate directory up to /root/aarch64/usr/lib/swift/linux
.
Ubuntu installs the aarch64 toolchain under /usr/aarch64-linux-gnu
, and I believe I'm pointing to it correctly. Any idea what I might be doing wrong? My full Dockerfile is below.
FROM --platform=$BUILDPLATFORM swift:${SWIFT_IMAGE_VERSION}-noble AS grpc_swift_builder
ARG PROTOC_GEN_SWIFT_VERSION
ARG SWIFT_IMAGE_VERSION
ARG SWIFT_SDK_CHECKSUM
RUN apt-get update && \
apt-get install -y build-essential curl file
RUN mkdir -p /grpc-swift
RUN curl -sSL https://api.github.com/repos/grpc/grpc-swift/tarball/${PROTOC_GEN_SWIFT_VERSION} | tar xz --strip 1 -C /grpc-swift
WORKDIR /grpc-swift
ARG TARGETOS TARGETARCH
RUN <<EOF
if [ "${TARGETARCH}" = "arm64" ]; then
SWIFT_VERSION=$(echo ${SWIFT_IMAGE_VERSION} | sed -E 's/([0-9]+\.[0-9]+)\.0/\1/')
mkdir -p /root/aarch64
curl -sSL https://download.swift.org/swift-$SWIFT_VERSION-release/ubuntu2404-aarch64/swift-$SWIFT_VERSION-RELEASE/swift-$SWIFT_VERSION-RELEASE-ubuntu24.04-aarch64.tar.gz | tar xz --strip 1 -C /root/aarch64
apt-get install -y \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu
ln -sf /usr/bin/aarch64-linux-gnu-ld.gold /usr/bin/ld.gold
fi
EOF
COPY ubuntu-24.04-aarch64.json /root/ubuntu-24.04-aarch64.json
RUN <<EOF
if [ "${TARGETARCH}" = "arm64" ]; then
SWIFTBUILD="swift build --destination /root/ubuntu-24.04-aarch64.json"
else
SWIFTBUILD="swift build"
fi
$SWIFTBUILD -c release --product protoc-gen-swift
EOF
RUN mkdir -p /protoc-gen-swift
RUN install -D /grpc-swift/.build/release/protoc-gen-swift /protoc-gen-swift/protoc-gen-swift
FROM swift:${SWIFT_IMAGE_VERSION}-noble AS grpc_swift
ARG TARGETARCH
RUN apt-get update && \
apt-get install -y patchelf file
COPY --from=grpc_swift_builder /protoc-gen-swift/ /protoc-gen-swift/
# Debug to check if linking worked before we try to run patchelf
RUN ldd /protoc-gen-swift/protoc-gen-swift
RUN <<EOF
case ${TARGETARCH} in
"amd64") SWIFT_LIB_DIR=/lib64 && SWIFT_LINKER=ld-linux-x86-64.so.2 ;;
"arm64") SWIFT_LIB_DIR=/lib && SWIFT_LINKER=ld-linux-aarch64.so.1 ;;
*) echo "ERROR: Machine arch ${TARGETARCH} not supported." ;;
esac
cp ${SWIFT_LIB_DIR}/${SWIFT_LINKER} \
$(ldd /protoc-gen-swift/protoc-gen-swift | awk '{print $3}' | grep /lib | sort | uniq) /protoc-gen-swift/
find /protoc-gen-swift/ -name 'lib*.so*' -exec patchelf --set-rpath /protoc-gen-swift {} \;
for p in protoc-gen-swift; do
patchelf --set-interpreter /protoc-gen-swift/${SWIFT_LINKER} /protoc-gen-swift/${p}
done
EOF