1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
|
# Copyright 2025-2026 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# @ECLASS: sysroot.eclass
# @MAINTAINER:
# cross@gentoo.org
# @AUTHOR:
# James Le Cuirot <chewi@gentoo.org>
# @SUPPORTED_EAPIS: 7 8 9
# @BLURB: Common functions for using a different (sys)root
# @DESCRIPTION:
# This eclass provides common functions to run executables within a different
# root or sysroot, with or without emulation by QEMU. Despite the name, these
# functions can be used in src_* or pkg_* phase functions.
case ${EAPI} in
7|8|9) ;;
*) die "${ECLASS}: EAPI ${EAPI:-0} not supported" ;;
esac
# @FUNCTION: qemu_arch
# @DESCRIPTION:
# Return the QEMU architecture name for the given target or CHOST. This name is
# used in qemu-user binary filenames, e.g. qemu-ppc64le.
qemu_arch() {
local target=${1:-${CHOST}}
case ${target} in
armeb*) echo armeb ;;
arm*) echo arm ;;
hppa*) echo hppa ;;
i?86*) echo i386 ;;
m68*) echo m68k ;;
mips64el*-gnuabi64) echo mips64el ;;
mips64el*-gnuabin32) echo mipsn32el ;;
mips64*-gnuabi64) echo mips64 ;;
mips64*-gnuabin32) echo mipsn32 ;;
powerpc64le*) echo ppc64le ;;
powerpc64*) echo ppc64 ;;
powerpc*) echo ppc ;;
*) echo "${target%%-*}" ;;
esac
}
# @FUNCTION: qemu_arch_if_needed
# @DESCRIPTION:
# If QEMU is needed to run binaries for the given target or CHOST on the build
# system, return the QEMU architecture, otherwise return status code 1.
qemu_arch_if_needed() {
local target=${1:-${CHOST}}
local qemu_arch=$(qemu_arch "${target}")
# We ideally compare CHOST against CBUILD, but binary packages cache the
# CBUILD value from the system that originally built them.
if [[ ${MERGE_TYPE} != binary ]]; then
if [[ ${qemu_arch} == $(qemu_arch "${CBUILD}") ]]; then
return 1
else
echo "${qemu_arch}"
return 0
fi
fi
# So for binary packages, compare against the machine hardware name instead.
# Don't use uname because that may lie. /proc knows the real value.
case "${qemu_arch}/$(< /proc/sys/kernel/arch)" in
"${qemu_arch}/${qemu_arch}") return 1 ;;
arm/armv*) return 1 ;;
hppa/parisc*) return 1 ;;
i386/i?86) return 1 ;;
mips64*/mips64) return 1 ;;
mipsn32*/mips64) return 1 ;;
mips*/mips) return 1 ;;
esac
echo "${qemu_arch}"
return 0
}
# @FUNCTION: _sysroot_compile_and_link_test_exe
# @USAGE: <output-path-name>
# @INTERNAL
# @DESCRIPTION:
# Compile and link a test executable that does nothing except to return success.
# The executable is built for the *host* machine using $(tc-getCC), *not* for
# the build machine using $(tc-getBUILD_CC).
_sysroot_compile_and_link_test_exe() {
[[ -n "${1}" ]] || die 'Must specify test executable path name.'
local test="${1}"
echo 'int main(void) { return 0; }' > "${test}.c" || die "failed to write ${test##*/}.c"
$(tc-getCC) ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o "${test}" "${test}.c" || die "failed to build ${test##*/}"
}
# @FUNCTION: sysroot_make_run_prefixed
# @DESCRIPTION:
# Create a wrapper script for directly running executables within a (sys)root
# without changing the root directory. The path to that script is returned. If
# no (sys)root has been set, then return status code 1. If the wrapper cannot be
# created for a permissible reason like QEMU being missing or broken, then
# return status code 2.
#
# The script explicitly uses QEMU if this is necessary and it is available in
# this environment. It may otherwise implicitly use a QEMU outside this
# environment if binfmt_misc has been used with the F flag. It is not feasible
# to add a conditional dependency on QEMU.
sysroot_make_run_prefixed() {
local QEMU_ARCH SCRIPT MYROOT MYEROOT LIBGCC
if [[ ${EBUILD_PHASE_FUNC} == src_* ]]; then
[[ -z ${SYSROOT} ]] && return 1
SCRIPT="${T}"/sysroot-run-prefixed
MYROOT=${SYSROOT}
MYEROOT=${ESYSROOT}
# Both methods below might need help to find GCC's libs. GCC might not
# be installed in the SYSROOT. Note that Clang supports this flag too.
LIBGCC=$($(tc-getCC) ${CPPFLAGS} ${CFLAGS} ${LDFLAGS} -print-libgcc-file-name)
LIBGCC=${LIBGCC%/*}
else
[[ -z ${ROOT} ]] && return 1
SCRIPT="${T}"/root-run-prefixed
MYROOT=${ROOT}
MYEROOT=${EROOT}
# Both methods below might need help to find GCC's libs. libc++ systems
# won't have this file, but it's not needed in that case.
if [[ -f ${EROOT}/etc/ld.so.conf.d/05gcc-${CHOST}.conf ]]; then
local LIBGCC_A
mapfile -t LIBGCC_A < "${EROOT}/etc/ld.so.conf.d/05gcc-${CHOST}.conf"
LIBGCC=$(printf "%s:" "${LIBGCC_A[@]/#/${ROOT}}")
LIBGCC=${LIBGCC%:}
fi
fi
if [[ ${CHOST} = *-mingw32 || ${CHOST} = *-cygwin ]]; then
if ! type -P wine >/dev/null; then
einfo "Wine not found. Continuing without ${SCRIPT##*/} wrapper."
return 2
fi
# UNIX paths can work, but programs will not expect this in %PATH%.
local winepath="Z:${LIBGCC};Z:${MYEROOT}/bin;Z:${MYEROOT}/usr/bin;Z:${MYEROOT}/$(get_libdir);Z:${MYEROOT}/usr/$(get_libdir)"
# Assume that Wine can do its own CPU emulation.
install -m0755 /dev/stdin "${SCRIPT}" <<-EOF || die
#!/bin/sh
SANDBOX_ON=0 LD_PRELOAD= WINEPATH="\${WINEPATH}\${WINEPATH+;};${winepath//\//\\}" exec wine "\${@}"
EOF
elif [[ ${CHOST} != *-linux-* ]]; then
einfo "Target is not Linux. Continuing without ${SCRIPT##*/} wrapper."
return 2
elif ! QEMU_ARCH=$(qemu_arch_if_needed); then
local DLINKER
if [[ "${ABI-${DEFAULT_ABI}}" == "${DEFAULT_ABI}" ]]; then
# glibc: ld.so is a symlink, ldd is a binary.
# musl: ld.so doesn't exist, ldd is a symlink.
local candidate
for candidate in "${MYEROOT}"/usr/bin/{ld.so,ldd}; do
if [[ -L ${candidate} ]]; then
DLINKER=${candidate}
break
fi
done
else
# non-default ABI needs a non-default dynamic linker
SCRIPT+="-${ABI}"
local test="${SCRIPT}-test"
_sysroot_compile_and_link_test_exe "${test}"
read -d '' -r DLINKER < <($(tc-getOBJCOPY) -O binary -j .interp -- "${test}" /dev/stdout)
DLINKER="${DLINKER:+${MYEROOT}${DLINKER}}"
[[ -f ${DLINKER} && -x ${DLINKER} ]] || DLINKER=
fi
[[ -n ${DLINKER} ]] || die "failed to find dynamic linker"
# musl symlinks ldd to ld-musl.so to libc.so. We want the ld-musl.so
# path, not the libc.so path, so don't resolve the symlinks entirely.
DLINKER=$(readlink -ev "${DLINKER}" || die "failed to find dynamic linker")
# Using LD_LIBRARY_PATH to set the prefix is not perfect, as it doesn't
# adjust RUNPATHs, but it is probably good enough.
install -m0755 /dev/stdin "${SCRIPT}" <<-EOF || die
#!/bin/sh
LD_LIBRARY_PATH="\${LD_LIBRARY_PATH}\${LD_LIBRARY_PATH+:}${LIBGCC}:${MYEROOT}/$(get_libdir):${MYEROOT}/usr/$(get_libdir)" exec "${DLINKER}" "\${@}"
EOF
else
# Use QEMU's environment variables rather than its command line
# arguments to cover both explicit and implicit QEMU usage.
install -m0755 /dev/stdin "${SCRIPT}" <<-EOF || die
#!/bin/sh
QEMU_SET_ENV="\${QEMU_SET_ENV}\${QEMU_SET_ENV+,}LD_LIBRARY_PATH=\${LD_LIBRARY_PATH}\${LD_LIBRARY_PATH+:}${LIBGCC}" QEMU_LD_PREFIX="${MYROOT}" exec $(type -P "qemu-${QEMU_ARCH}") "\${@}"
EOF
# Meson will fail if the given exe_wrapper does not work, regardless of
# whether one is actually needed. This is bad if QEMU is not installed
# and worse if QEMU does not support the architecture. We therefore need
# to perform our own test up front.
local test="${SCRIPT}-test"
_sysroot_compile_and_link_test_exe "${test}"
if ! "${SCRIPT}" "${test}" &>/dev/null; then
einfo "Failed to run ${test##*/}. Continuing without ${SCRIPT##*/} wrapper."
return 2
fi
fi
echo "${SCRIPT}"
}
# @FUNCTION: sysroot_run_prefixed
# @DESCRIPTION:
# Create a wrapper script with sysroot_make_run_prefixed if necessary, and use
# it to execute the given command, otherwise just execute the command directly.
# Return unsuccessfully if the wrapper cannot be created.
sysroot_run_prefixed() {
local script
script=$(sysroot_make_run_prefixed)
case $? in
0) "${script}" "${@}" ;;
1) "${@}" ;;
*) return $? ;;
esac
}
# @FUNCTION: sysroot_try_run_prefixed
# @DESCRIPTION:
# Create a wrapper script with sysroot_make_run_prefixed if necessary, and use
# it to execute the given command, otherwise just execute the command directly.
# Print a warning and return successfully if the wrapper cannot be created.
sysroot_try_run_prefixed() {
local script
script=$(sysroot_make_run_prefixed)
case $? in
0) "${script}" "${@}" ;;
1) "${@}" ;;
*) ewarn "Unable to run command under prefix: $*" ;;
esac
}
|