Skip to content

Commit 9102ff5

Browse files
committed
载荷必须带上它链接的东西 —— 而这是量出来的,不是想出来的
按方案里那条判据(deps 按实测 DT_NEEDED / otool 闭包决定,不抄兄弟描述符)去量 9.2.4 的产物,量出一个装得上、首次运行就失败的包: linux libpixman-1 / libglib-2.0 / libgio / libgobject / libgmodule / libz / libzstd —— 一个都不在载荷里,也都不是核心 glibc darwin /opt/homebrew/opt/{pixman,glib,zstd,gnutls,...}/lib/*.dylib —— **构建机 Homebrew 的绝对路径** macOS 那条更糟:Intel 机的 Homebrew 在 /usr/local,前缀都不一样。 ⇒ 改为兄弟包已有的形状:把闭包打进载荷,用相对 rpath / @loader_path 到达。 ⚠️ 系统库刻意不打包:核心 libc/libm 与 macOS 框架来自运行的系统,把第二份 libc 带进 一个进程正是本索引契约文本点名为 SIGSEGV 来源的那个形状。 ⚠️ macOS 上重写 install name 会让 ad-hoc 签名失效,必须重签,否则不是警告而是被内核 杀掉。 并加一条断言:载荷不得指向自身之外。它会在构建时响亮地失败,而不是在别人第一次运行 时安静地失败。
1 parent 1cf54e3 commit 9102ff5

1 file changed

Lines changed: 93 additions & 1 deletion

File tree

.github/workflows/build.yml

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,70 @@ jobs:
289289
# loads at run time is a question for the descriptor, and answering it
290290
# by deleting files until something breaks is how a payload ends up
291291
# missing one on somebody else's machine.
292-
mkdir -p ../out/bin ../out/share/qemu
292+
mkdir -p ../out/bin ../out/share/qemu ../out/lib
293293
cp qemu-system-x86_64 ../out/bin/
294294
cp -r "$SRC/pc-bios/." ../out/share/qemu/
295295
296+
# ⚠️ THE PAYLOAD MUST CARRY WHAT IT LINKS AGAINST, AND MEASURING THAT
297+
# IS WHAT CAUGHT THIS.
298+
#
299+
# An earlier build shipped only the emulator. Its measured closure:
300+
#
301+
# linux libpixman-1.so.0, libglib-2.0.so.0, libgio, libgobject,
302+
# libgmodule, libz.so.1, libzstd.so.1 -- none of them in
303+
# the payload, none of them core glibc
304+
# darwin /opt/homebrew/opt/{pixman,glib,zstd,gnutls,...}/lib/*.dylib
305+
# -- ABSOLUTE paths into the build machine's Homebrew
306+
#
307+
# So that payload installs and then fails on first run for anyone
308+
# without those libraries, and on macOS for anyone whose Homebrew is
309+
# not at the same prefix -- an Intel mac uses /usr/local. The index's
310+
# sibling packages avoid this by bundling: xPack's QEMU carries 51
311+
# shared objects and reaches them through its own relative rpath.
312+
#
313+
# ⚠️ SYSTEM LIBRARIES ARE DELIBERATELY NOT BUNDLED. Core libc, libm
314+
# and the macOS frameworks come from the running system by design;
315+
# carrying a second libc into a process is the two-glibcs shape this
316+
# index's own contract text names as a SIGSEGV source.
317+
if [ "$RUNNER_OS" = "Linux" ]; then
318+
ldd qemu-system-x86_64 \
319+
| awk '/=> \// {print $3}' \
320+
| grep -vE '/(libc|libm|libdl|librt|libpthread|libresolv|ld-linux[^ ]*)\.so' \
321+
| while read -r so; do cp -Ln "$so" ../out/lib/ 2>/dev/null || true; done
322+
# ⚠️ `patchelf` on the EMULATOR, not on a loader. This project's own
323+
# notes forbid patching a loader's own paths; setting a relative
324+
# rpath on an ordinary executable is the opposite case, and it is
325+
# what makes `out/lib` reachable without an environment variable.
326+
sudo apt-get install -y -qq patchelf
327+
patchelf --set-rpath '$ORIGIN/../lib' ../out/bin/qemu-system-x86_64
328+
for so in ../out/lib/*.so*; do patchelf --set-rpath '$ORIGIN' "$so" 2>/dev/null || true; done
329+
elif [ "$RUNNER_OS" = "macOS" ]; then
330+
# Mach-O records each dependency by the install name the LIBRARY
331+
# carries, so copying is not enough: every reference has to be
332+
# rewritten, and so does each bundled library's own id.
333+
otool -L qemu-system-x86_64 | tail -n +2 | awk '{print $1}' \
334+
| grep -E '^/(opt|usr/local)/' \
335+
| while read -r dy; do
336+
cp -Ln "$dy" ../out/lib/ 2>/dev/null || true
337+
install_name_tool -change "$dy" "@loader_path/../lib/$(basename "$dy")" \
338+
../out/bin/qemu-system-x86_64 2>/dev/null || true
339+
done
340+
for dy in ../out/lib/*.dylib; do
341+
[ -e "$dy" ] || continue
342+
install_name_tool -id "@loader_path/$(basename "$dy")" "$dy" 2>/dev/null || true
343+
otool -L "$dy" | tail -n +2 | awk '{print $1}' | grep -E '^/(opt|usr/local)/' \
344+
| while read -r dep; do
345+
cp -Ln "$dep" ../out/lib/ 2>/dev/null || true
346+
install_name_tool -change "$dep" "@loader_path/$(basename "$dep")" "$dy" 2>/dev/null || true
347+
done
348+
done
349+
# ⚠️ Rewriting invalidates the ad-hoc signature; re-sign, or the
350+
# binary is killed by the kernel rather than merely warned about.
351+
codesign --force -s - ../out/bin/qemu-system-x86_64 2>/dev/null || true
352+
for dy in ../out/lib/*.dylib; do codesign --force -s - "$dy" 2>/dev/null || true; done
353+
fi
354+
rmdir ../out/lib 2>/dev/null || true
355+
296356
- name: Configure and build (Windows / MSYS2)
297357
if: runner.os == 'Windows'
298358
shell: msys2 {0}
@@ -523,6 +583,38 @@ jobs:
523583
# at run time is a question for the descriptor, and answering it by
524584
# deleting directories until something breaks is how a payload ends up
525585
# missing a firmware blob on somebody else's machine.
586+
# ⭐ THE PAYLOAD MUST NOT REACH OUTSIDE ITSELF, ASSERTED RATHER THAN
587+
# ASSUMED. This is the check that would have failed on the first
588+
# build, and it fails loudly instead of at somebody's first run.
589+
echo "── every library the emulator names ──"
590+
case "$RUNNER_OS" in
591+
Linux)
592+
readelf -d out/bin/qemu-system-x86_64 | grep -E 'NEEDED|RUNPATH|RPATH' || true
593+
# Resolve the closure the way the loader will, from the payload.
594+
bad=$(ldd out/bin/qemu-system-x86_64 2>/dev/null \
595+
| awk '/=> \// {print $3}' \
596+
| grep -vE "^$PWD/out/" \
597+
| grep -vE '/(libc|libm|libdl|librt|libpthread|libresolv|ld-linux[^ ]*)\.so' || true)
598+
if [ -n "$bad" ]; then
599+
echo "$bad"
600+
echo "::error::the payload links against libraries it does not carry; installing it would succeed and running it would not"
601+
exit 1
602+
fi
603+
echo "resolved entirely inside the payload, plus core glibc" ;;
604+
macOS)
605+
otool -L out/bin/qemu-system-x86_64 | tail -n +2 | awk '{print $1}'
606+
bad=$(otool -L out/bin/qemu-system-x86_64 | tail -n +2 | awk '{print $1}' \
607+
| grep -E '^/(opt|usr/local)/' || true)
608+
if [ -n "$bad" ]; then
609+
echo "$bad"
610+
echo "::error::the payload names absolute paths into this build machine's package manager; it would fail on any host without the same prefix"
611+
exit 1
612+
fi
613+
echo "no build-machine paths remain" ;;
614+
Windows)
615+
echo "(the DLL closure sits beside the exe; counted above)" ;;
616+
esac
617+
526618
echo "── the ten largest things under the prefix ──"
527619
du -sh out/* 2>/dev/null | sort -rh | head -10
528620
du -sh out/share/* 2>/dev/null | sort -rh | head -10

0 commit comments

Comments
 (0)