0

我试图让 Rust 在 Docker 中运行以将其用于 32 位 musl 构建。自从我更新它以使用新的 URL 来拉取 rustup 后,我在使用 bash 以交互方式运行容器时遇到了这个问题:

root@2c3549fe3169:/sample# cargo
error: command failed: 'cargo'
info: caused by: No such file or directory (os error 2)

奇怪的是,我可以看到可执行文件

root@2c3549fe3169:/sample# ls -l /root/.cargo/bin/
total 101440
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 cargo
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 cargo-clippy
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 cargo-fmt
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 rls
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 rust-gdb
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 rust-lldb
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 rustc
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 rustdoc
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 rustfmt
-rwxr-xr-x 10 root root 10383380 Feb 17 21:34 rustup
root@2c3549fe3169:/sample# date
Sun Feb 17 21:34:21 UTC 2019
root@2c3549fe3169:/sample# file /root/.cargo/bin/cargo
/root/.cargo/bin/cargo: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.9, with debug_info, not stripped
root@2c3549fe3169:/sample# cargo
error: command failed: 'cargo'
info: caused by: No such file or directory (os error 2)

它通过以下方式安装:

RUN curl https://sh.rustup.rs -sSf | sh -s -- \
--default-toolchain 1.32.0 \
-y && \
~/.cargo/bin/rustup target add i686-unknown-linux-musl && \
echo "[build]\ntarget = \"i686-unknown-linux-musl\"" > ~/.cargo/config

我可以看到该文件,但似乎无法运行它,即使我切换到该目录:

root@2c3549fe3169:~/.cargo/bin# ./cargo
error: command failed: 'cargo'
info: caused by: No such file or directory (os error 2)

这是我在运行时看到的ldd

root@4e21c8d37266:/volume# ldd /root/.cargo/bin/cargo
linux-gate.so.1 (0xf7f41000)
libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xf774c000)
librt.so.1 => /lib/i386-linux-gnu/librt.so.1 (0xf7742000)
libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xf7723000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xf7705000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7529000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xf7427000)
/lib/ld-linux.so.2 (0xf7f43000)

这是我完整的 Dockerfile

FROM i386/ubuntu

RUN apt-get update && apt-get install -y \
  cmake \
  curl \
  file \
  git \
  g++ \
  python \
  make \
  nano \
  ca-certificates \
  xz-utils \
  musl-tools \
  pkg-config \
  apt-file \
  xutils-dev \
  --no-install-recommends && \
  rm -rf /var/lib/apt/lists/*


RUN curl https://sh.rustup.rs -sSf | sh -s -- \
  --default-toolchain 1.32.0 \
  -y && \
  ~/.cargo/bin/rustup target add i686-unknown-linux-musl && \
  echo "[build]\ntarget = \"i686-unknown-linux-musl\"" > ~/.cargo/config


# Compile C libraries with musl-gcc
ENV SSL_VER=1.0.2j \
    CURL_VER=7.52.1 \
    CC=musl-gcc \
    PREFIX=/usr/local \
    PATH=/usr/local/bin:$PATH \
    PKG_CONFIG_PATH=/usr/local/lib/pkgconfig

RUN curl -sL http://www.openssl.org/source/openssl-$SSL_VER.tar.gz | tar xz && \
    cd openssl-$SSL_VER && \
    ./Configure no-shared --prefix=$PREFIX --openssldir=$PREFIX/ssl no-zlib -m32 linux-elf -fPIC -fno-stack-protector && \
    make depend 2> /dev/null && make -j$(nproc) && make install && \
    cd .. && rm -rf openssl-$SSL_VER

RUN curl https://curl.haxx.se/download/curl-$CURL_VER.tar.gz | tar xz && \
    cd curl-$CURL_VER && \
    ./configure --enable-shared=no --enable-static=ssl --enable-optimize --prefix=$PREFIX --host=i686-pc-linux-gnu CFLAGS=-m32 \
      --with-ca-path=/etc/ssl/certs/ --with-ca-bundle=/etc/ssl/certs/ca-certificates.crt --without-ca-fallback && \
    make -j$(nproc) && make install && \
    cd .. && rm -rf curl-$CURL_VER

ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
    SSL_CERT_DIR=/etc/ssl/certs \
    OPENSSL_LIB_DIR=$PREFIX/lib \
    OPENSSL_INCLUDE_DIR=$PREFIX/include \
    OPENSSL_DIR=$PREFIX \
    OPENSSL_STATIC=1 \
    PATH=/usr/local/bin:/root/.cargo/bin:$PATH

RUN echo $PATH

cargo根据评论对二进制文件进行跟踪:

root@156da6108ff8:~/.cargo/bin# strace -f -e trace=execve cargo               
execve("/root/.cargo/bin/cargo", ["cargo"], 0xfffdd8fc /* 20 vars */) = 0
execve("/root/.rustup/toolchains/1.32.0-x86_64-unknown-linux-gnu/bin/cargo", ["/root/.rustup/toolchains/1.32.0-"...], 0x57d95620 /* 25 vars */) = -1 ENOENT (No such file or directory)
error: command failed: 'cargo'
info: caused by: No such file or directory (os error 2)
+++ exited with 1 +++
4

1 回答 1

3

所以,这里是我们调查的总结。

用于构建的基本映像i386/ubuntu内部具有 32 位环境,但是,该映像无法适当地掩盖uname(2)调用结果(通过具有类似setarch linux32入口点的东西),因此,在 64 位系统上运行时(您的情况),构建容器内的任何进程调用uname(2)uname(1)看到x86_64而不是i686. 这是问题的根源。

安装时cargo,您下载并运行安装脚本,该脚本会检测它运行的平台并下载适当版本的rustup-init. 此脚本中的平台检测正确识别它运行在 32 位环境但在 64 位内核上,因此脚本下载 32 位版本的rustup-init. 但是,rustup-init决定它运行x86_64(可能它看到x86_64返回uname(2),但不执行检查“64 位内核上的 32 位环境”情况,就像安装程序脚本一样)。您可以在安装过程中看到它,而无需-y

Current installation options:

    default host triple: x86_64-unknown-linux-gnu
    default toolchain: stable
    modify PATH variable: yes

因此,rustup安装 64 位工具链,您最终会遇到调用cargo结果在 32 位环境中运行64 位二进制文​​件的情况,因此您会看到错误。

我在这里仍然感到某种不一致的行为,因为安装脚本和rustup-init都是同一个项目的一部分,我真的不明白为什么他们应该在同一环境中以不同的方式检测平台(为什么不能rustup-init只是像安装脚本一样聪明?)。

正如@Shepmaster 所注意到的,这是一个已知问题(Rustup 在 32 位 Docker 实例上安装 64 位编译器)。有两种可能的解决方法:

  • --default-host i686-unknown-linux-gnu通过传递给安装程序来强制使用默认工具链的平台;
  • 通过运行它来欺骗安装程序,setarch linux32以便它的调用uname(2)将看到i686而不是x86_64.

就个人而言,我会选择第一个选项,因为它似乎不那么 hacky。

于 2019-02-19T10:29:07.660 回答