Docker

如何替換 Linux 容器內 /bin 中的二進製文件

  • January 29, 2019

我想做一些奇怪的容器,我的任務之一是替換uname二進製文件。

我編碼它:

#include <stdio.h>
int main() {
  printf("This is a fake uname\n");
  return 0;
}  

編譯它:

gcc uname.c -o uname

當我在我的 ubuntu 上執行它時它工作正常。

我創建了一個Dockerfile並將其複製到圖像中:

cat > Dockerfile <<EOF
FROM alpine:latest
RUN rm /bin/uname
COPY uname /bin/
ENTRYPOINT sh;
WORKDIR /home
EOF  

並建造它docker build -t myimage -f Dockerfile .

當我執行圖像時:

docker run -it --rm myimage  

該文件存在,但是當我嘗試執行它時,它寫道它不存在:

/home # uname
sh: uname: not found
/home # ls -lia uname
ls: uname: No such file or directory
/home # ls -lia /bin/uname
    35 -rwxr-xr-x    1 root     root          8600 Jan 29 13:27 /bin/uname

問題是你建構你的二進製文件glibc(因為你使用的是 Ubuntu),然後在 Alpine 上執行它(而musl不是使用它)。

當您建構二進製文件時,一些元數據被硬編碼到其中,特別是解釋器(動態連結器)位置。在二進制執行期間,核心使用此解釋器來載入二進制及其所有執行時依賴項。當您針對 建構時glibc,二進製文件會請求 的glibc解釋器(類似於/lib64/ld-linux-x86-64.so.2):

$ readelf -l /bin/uname
<...>
     [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

但是Alpine下沒有這樣的解釋器。相反, Alpine 有/lib/ld-musl-x86_64.so.1,並且它的所有二進製文件都被建構,以便它們完全請求這個解釋器:

# readelf -l /bin/busybox
<...>
     [Requesting program interpreter: /lib/ld-musl-x86_64.so.1]

因此,當您uname在 Ubuntu 中建構然後在 Alpine 中執行它時,核心無法找到解釋器,因此它返回“未找到”。

有趣的一點是,您可以手動呼叫解釋器,如果問題在解釋器中,這可能會起作用:

# /lib/ld-musl-x86_64.so.1 /bin/uname 
This is a fake uname

但這當然僅用於故障排除。

那麼該怎麼辦?

您的選擇是靜態連結您的二進製文件(因此不需要解釋器和所有共享庫,以便核心可以自己載入和執行二進製文件):

gcc uname.c -static -o uname

或在 Alpine 下建構它(也許嘗試 Docker 的多階段建構?我會推薦這種方法,因為在這種情況下,您明確聲明您的意圖並表明您在 Alpine 中建構自定義二進製文件以替換原始 Alpine 的二進製文件)。

引用自:https://unix.stackexchange.com/questions/497455