opengl: Do VirtualGL preload conditioning on VGL_DISPLAY
Preloading is done by doing a binary patch to the library to add it as a dependency. The 2.3 series of glibc will let this be done with just a wrapper that calls ld.so and with the --preload option.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
# Add a setup hook to the mesa_noglu package that automatically adds
|
||||
# a libvglfaker.so dependency to executables that depend on libGL.so.
|
||||
|
||||
{ super, lib, buildEnv, makeSetupHook, file, bash }:
|
||||
{ super, lib, buildEnv, makeSetupHook, file, bash, xorg }:
|
||||
|
||||
let
|
||||
autoVirtualGLHook =
|
||||
@@ -27,7 +27,7 @@ let
|
||||
makeSetupHook {
|
||||
name = "insert-virtualgl";
|
||||
deps = [ file virtualglLib ];
|
||||
substitutions = { inherit virtualglLib; };
|
||||
substitutions = { inherit virtualglLib bash; inherit (xorg) libXext; };
|
||||
} ./insert-virtualgl.sh;
|
||||
|
||||
in {
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# Quote the given arguement
|
||||
#
|
||||
VGL_quote() {
|
||||
printf '%s' "${*@Q}"
|
||||
}
|
||||
|
||||
|
||||
# Stream producers
|
||||
#
|
||||
# These produce a stream of escaped new-line separated items
|
||||
@@ -12,16 +19,22 @@ VGL_sourceS() {
|
||||
# Stream the results of find (correctly escapes all filenames)
|
||||
#
|
||||
VGL_findS() {
|
||||
declare -a options=("$@") file
|
||||
declare options=("$@") file
|
||||
find "${options[@]}" -print0 |
|
||||
while read -d '' -r file; do
|
||||
VGL_sourceS "$file"
|
||||
done
|
||||
}
|
||||
|
||||
# Stream the closure of the list libraries from an elf file
|
||||
# Stream the list of directly required libraries from an elf file
|
||||
#
|
||||
VGL_elfLibsS() {
|
||||
patchelf --print-needed "$1"
|
||||
}
|
||||
|
||||
# Stream the resolved libraries from an elf file
|
||||
#
|
||||
VGL_elfLibsResolvedS() {
|
||||
declare file=$1 line
|
||||
ldd "$file" |
|
||||
while read line; do
|
||||
@@ -42,6 +55,16 @@ VGL_elfLibsS() {
|
||||
done
|
||||
}
|
||||
|
||||
# Stream the list of rpaths from an elf file
|
||||
#
|
||||
VGL_elfRPathS() {
|
||||
declare file=$1 entry
|
||||
patchelf --print-rpath "$file" |
|
||||
while read -d ':' -r entry; do
|
||||
VGL_sourceS "$entry"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Stream transformers
|
||||
@@ -49,30 +72,41 @@ VGL_elfLibsS() {
|
||||
# Take in a set of options and transform a stream of new-line separated items
|
||||
#
|
||||
|
||||
# Group all items onto a single line separated by given deliminator (or space)
|
||||
#
|
||||
VGL_joinS() {
|
||||
declare deliminator=${1- } next
|
||||
if read next; then
|
||||
printf '%q' "$next"
|
||||
while read next; do
|
||||
printf '%s%q' "$deliminator" "$next"
|
||||
done
|
||||
printf '\n'
|
||||
fi
|
||||
}
|
||||
|
||||
# Remove all items for which the given command returns false
|
||||
#
|
||||
VGL_filterS() {
|
||||
declare command=("$@")
|
||||
VGL_testKeepS() {
|
||||
declare command=$1 next
|
||||
while read next; do
|
||||
if eval $(printf '%q ' "${command[@]}" "$next"); then
|
||||
set -- "$next"
|
||||
if (eval "$command"); then
|
||||
VGL_sourceS "$next"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Remove all items which match one of the given values
|
||||
#
|
||||
VGL_valuesRemoveS() {
|
||||
declare -A values
|
||||
declare value
|
||||
for value in "$@"; do
|
||||
values[$value]=
|
||||
done
|
||||
while read value; do
|
||||
[[ ${values[$value]+yes} ]] \
|
||||
|| VGL_sourceS "$value"
|
||||
done
|
||||
}
|
||||
|
||||
# Add addition to end unless test succeeds on at least one
|
||||
#
|
||||
VGL_testSuffixNoneS() {
|
||||
declare command=$1 addition=$2
|
||||
eval "declare items=($(cat))"
|
||||
VGL_sourceS "${items[@]}"
|
||||
VGL_sourceS "${items[@]}" | VGL_isTestAnyS "$command" \
|
||||
|| VGL_sourceS "$addition"
|
||||
}
|
||||
|
||||
|
||||
# Stream consumers
|
||||
@@ -82,10 +116,11 @@ VGL_filterS() {
|
||||
|
||||
# Return true iff given command returns true for some item
|
||||
#
|
||||
VGL_anyS() {
|
||||
declare command=("$@")
|
||||
VGL_isTestAnyS() {
|
||||
declare command="$1" next
|
||||
while read next; do
|
||||
if eval $(printf '%q ' "${command[@]}" "$next"); then
|
||||
set -- "$next"
|
||||
if (eval "$command"); then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
@@ -93,6 +128,19 @@ VGL_anyS() {
|
||||
}
|
||||
|
||||
|
||||
# Return true iff given command returns true for all item
|
||||
#
|
||||
VGL_isTestAllS() {
|
||||
declare command="$1" next
|
||||
while read next; do
|
||||
set -- "$next"
|
||||
if ! (eval "$command"); then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# Tests for the filter transformer
|
||||
#
|
||||
@@ -123,33 +171,97 @@ VGL_isLibGL() {
|
||||
|
||||
|
||||
|
||||
# Update elf files
|
||||
#
|
||||
|
||||
# Run given stream element on rpath stream and write result to file if differs from original
|
||||
#
|
||||
VGL_elfFilterRPathS() {
|
||||
declare file=$1 command=$2
|
||||
eval "declare pathsOld=($(VGL_elfRPathS "$file"))"
|
||||
eval "declare pathsNew=($(VGL_sourceS "${pathsOld[@]}" | eval "$command"))"
|
||||
[[ ${pathsNew[@]@Q} = ${pathsOld[@]@Q} ]] \
|
||||
|| if (( ${#pathsNew[@]} > 0 ));
|
||||
then ( IFS=:; set -x; patchelf --set-rpath "${pathsNew[*]}" "$file" )
|
||||
else ( set -x; patchelf --remove-rpath "$file" )
|
||||
fi
|
||||
}
|
||||
|
||||
# Run given stream element on directly required libraries stream and write result to file if differs from original
|
||||
#
|
||||
VGL_elfFilterLibsS() {
|
||||
declare file=$1 command=$2
|
||||
eval "declare libsOld=($(VGL_elfLibsS "$file"))"
|
||||
eval "declare libsNew=($(VGL_sourceS "${libsOld[@]}" | eval "$command"))"
|
||||
eval "declare libs=($(VGL_sourceS "${libsOld[@]}" | VGL_valuesRemoveS "${libsNew[@]}"))"
|
||||
(( ${#libs[@]} < 1 )) \
|
||||
|| ( eval "set -x; patchelf$(printf ' --remove-needed %q' "${libs[@]}") ${file@Q}")
|
||||
eval "declare libs=($(VGL_sourceS "${libsNew[@]}" | VGL_valuesRemoveS "${libsOld[@]}"))"
|
||||
(( ${#libs[@]} < 1 )) \
|
||||
|| ( eval "set -x; patchelf$(printf ' --add-needed %q' "${libs[@]}") ${file@Q}")
|
||||
}
|
||||
|
||||
# Patch elf file to be able to use OpenGL properly (not a stream function so can be called in normal way)
|
||||
#
|
||||
VGL_patch() {
|
||||
declare files=("$@") file
|
||||
for file in "${files[@]}"; do
|
||||
# glibc 2.3 ld.so supports a --preload option that would make this just a simple wrapper
|
||||
#
|
||||
# mv "$file" "${file%/*}/.${file##*/}.vgl"
|
||||
# cat > "$file" <<EOF
|
||||
#! @bash@/bin/bash
|
||||
# exec -a "\$0" "$(patchelf --print-interpreter "${file%/*}/.${file##*/}.vgl")" \${VGL_DISPLAY:+--preload "@virtualglLib@/lib/libvglfaker.so"} "${file%/*}/.${file##*/}.vgl" "\$@"
|
||||
# EOF
|
||||
# chmod +x "$file"
|
||||
|
||||
# VGL version may need fixing up to ensure libglfaker.so gets loaded at the start
|
||||
|
||||
printf 'Wrapping and patching for VirtualGL: %q\n' "$file" >&2
|
||||
cp -a "$file" "${file%/*}/.${file##*/}.vgl-yes"
|
||||
VGL_elfFilterLibsS "${file%/*}/.${file##*/}.vgl-yes" 'VGL_testSuffixNoneS '"$(VGL_quote '[[ $1 =~ ^(.*/)?libvglfaker.so ]]')"' @virtualglLib@/lib/libvglfaker.so'
|
||||
# patchelf --add-needed @virtualglLib@/lib/libvglfaker.so "${file%/*}/.${file##*/}.vgl-yes"
|
||||
|
||||
# Non-VGL versions may need fixing to load libXext so NVIDIA binary libs are happy
|
||||
#
|
||||
# Ugly, but putting this here instead of with the NVIDIA libs, allows us not to to fix one version for all
|
||||
|
||||
mv "$file" "${file%/*}/.${file##*/}.vgl-no"
|
||||
VGL_elfFilterLibsS "${file%/*}/.${file##*/}.vgl-no" 'VGL_testSuffixNoneS '"$(VGL_quote '[[ $1 =~ ^(.*/)?libXext.so ]]')"' libXext.so.6'
|
||||
VGL_elfFilterRPathS "${file%/*}/.${file##*/}.vgl-no" 'VGL_testSuffixNoneS '"$(VGL_quote '[[ $1 = @libXext@/lib ]]')"' @libXext@/lib'
|
||||
|
||||
# Replace with chooser script based on whether VGL_DISPLAY is set or not
|
||||
cat > "$file" <<EOF
|
||||
#! @bash@/bin/bash
|
||||
|
||||
if [ -z "\$VGL_DISPLAY" ]; then
|
||||
exec -a "\$0" "${file%/*}/.${file##*/}.vgl-no" "\$@"
|
||||
else
|
||||
exec -a "\$0" "${file%/*}/.${file##*/}.vgl-yes" "\$@"
|
||||
fi
|
||||
EOF
|
||||
chmod +x "$file"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# Setup hook
|
||||
#
|
||||
|
||||
# Find all executables that depend on libGL and add a libvglfaker.so dependency
|
||||
# Find all executables that depend on libGL and replace with a wrapper script that either executes the original
|
||||
# executable or a copy with an added libvglfaker.so dependency depending on whether VGL_DISPLAY is set or not.
|
||||
#
|
||||
VGL_autoAddVGL() {
|
||||
declare opts=$(shopt -p) output
|
||||
set +o pipefail
|
||||
|
||||
echo "Inserting VirtualGL into OpenGL executables in $prefix..." >&2
|
||||
|
||||
set -x
|
||||
for output in $outputs; do
|
||||
VGL_findS "${!output}" -type f | VGL_filterS VGL_isElfBin | {
|
||||
declare file
|
||||
while read file; do
|
||||
VGL_elfLibsS "$file" |
|
||||
if VGL_anyS VGL_isLibGL; then
|
||||
VGL_sourceS "$file"
|
||||
fi
|
||||
done
|
||||
} | {
|
||||
declare file
|
||||
while read file; do
|
||||
printf 'patchelf --add-needed @virtualglLib@/lib/libvglfaker.so %q\n' "$file" >&2
|
||||
patchelf --add-needed @virtualglLib@/lib/libvglfaker.so "$file"
|
||||
done
|
||||
}
|
||||
VGL_findS "${!output}" -type f \
|
||||
| VGL_testKeepS 'VGL_isElfBin "$1"' \
|
||||
| VGL_testKeepS 'VGL_elfLibsResolvedS "$1" | VGL_isTestAnyS '"$(VGL_quote 'VGL_isLibGL "$1"')" \
|
||||
| VGL_isTestAllS 'VGL_patch "$1"'
|
||||
done
|
||||
|
||||
eval "$opts"
|
||||
|
||||
Reference in New Issue
Block a user