工作上常用 Docker container 編譯 SDK.
最近發現只要 host 作業系統一更新, SDK 裡部份 libs/apps 在編譯過程就會報 Ivalid kernel source directory ...
錯誤.
錯誤的原因是這些 libs/apps 的 code 有 include kernel header, 因此在 ./configure
過程中要透過 uname -r
去取得 kernel version, 組合出 kernel source 路徑.
但因 container 和 host 共用 kernel, 且 uname -r
透過 uname()
system call 取得 kernel version, 在 container 裡執行 uname -r
實際上拿到的是 host
的 kernel version.
當系統更新導致 host 的 kernel version 和 Docker image 在建立時的 kernel version 不一致時, ./configure
就會找不到正確的 kernel source 路徑.
解決此問題最簡單的方法就是在呼叫 uname -r
時, 攔截這個指令. 並丟 Docker/Singularity image 建立時的 kernel version 回去.
所以我寫了一個 fakeuname project 來瞞天過海 (XD).
概念如下:
uname
時執行一個假造的 uname
sciptuname
script 回傳 image 建立時的 kernel version$ mkdir ~/.local/fakeuname
$ vim ~/.local/fakeuname/uname
$ chmod u+x ~/.local/fakeuname/uname
以下是一個簡單的 uname
script 範例:
#!/usr/bin/env bash
# Set the kernel release string you attempt to present here.
FAKE_KERNEL_RELEASE="4.15.0-112-generic"
# Reset in case getopts has been used previously in the shell.
OPTIND=1
# Substitute kernel release string
OUTPUT=`/bin/uname $@`
while getopts "asnrvmpio" opt; do
case "${opt}" in
r)
if [[ ! -z ${FAKE_KERNEL_RELEASE+x} ]]; then
ORG_KERNEL_RELEASE=`/bin/uname -r`
OUTPUT=${OUTPUT/${ORG_KERNEL_RELEASE}/${FAKE_KERNEL_RELEASE}}
fi
;;
*)
;;
esac
done
echo ${OUTPUT}
在 ~/.bashrc
透過新增 PATH env 來修改 command search 的順序, 讓我們假造的 uname
script 先被執行.
此外, 為了不影響在 host 環境下的 uanme
操作, PATH env 應該在 container 環境下才被 updated.
# Update PATH env if we are in a Docker container or Singularity container
if test -f "/.dockerenv" || [[ ! -z "${SINGULARITY_NAME+x}" ]]; then
export PATH=${HOME}/.local/fakeuname:${PATH}
fi
執行 Docker container 時, 需要額外 mount ~/.bashrc
和 ~/.local/fakeuname
.
$ docker run -v ${HOME}/.bashrc:/root/.bashrc:ro -v ${HOME}/.local/fakeuname/uname:/root/.local/fakeuname/uname --rm -it DOCKER_IMAGE
執行 Singularity container 時預設會 mount 家目錄, 所以不需額外指令.
$ SINGULARITY_SHELL=/bin/bash singularity shell SINGULARITY_IMAGE
另一個假造 kernel version 的作法是透過 LD_PRELOAD
去 override uname()
system call.
詳細作法可以參考 rpm-software-management/fakeuname.
但使用 LD_PRELOAD
的方法在 cross compiling 時會因為 host 和 target ELF 格式不同, 導致在 load LD_PRELOAD
的 .so
檔時報錯.
ERROR: ld.so: object '/home/borting/.local/fakeuname/fakeuname.so' from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS64): ignored.
雖然 LD_PRELOAD
的 .so
檔最終不會編進 binary, 但在 make log 看到噴 error 就不大爽, 所以最後就不採用這種方法了.
container
Docker
Singularity
Written on
February
25th,
2021
by
Borting