xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame^] | 1 | #!/bin/bash |
| 2 | # test-wrapper script for NaCl. |
| 3 | |
| 4 | # Copyright (C) 2015-2016 Free Software Foundation, Inc. |
| 5 | # This file is part of the GNU C Library. |
| 6 | |
| 7 | # The GNU C Library is free software; you can redistribute it and/or |
| 8 | # modify it under the terms of the GNU Lesser General Public |
| 9 | # License as published by the Free Software Foundation; either |
| 10 | # version 2.1 of the License, or (at your option) any later version. |
| 11 | |
| 12 | # The GNU C Library is distributed in the hope that it will be useful, |
| 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | # Lesser General Public License for more details. |
| 16 | |
| 17 | # You should have received a copy of the GNU Lesser General Public |
| 18 | # License along with the GNU C Library; if not, see |
| 19 | # <http://www.gnu.org/licenses/>. |
| 20 | |
| 21 | progname="$(basename "$0")" |
| 22 | |
| 23 | usage="usage: ${progname} --arch=ARCH [VAR=VAL...] COMMAND ..." |
| 24 | help=" |
| 25 | " |
| 26 | |
| 27 | use_bootstrap=true |
| 28 | arch= |
| 29 | env=() |
| 30 | envi=0 |
| 31 | while [ $# -gt 0 ]; do |
| 32 | case "$1" in |
| 33 | |
| 34 | --help) |
| 35 | echo "$usage" |
| 36 | echo "$help" |
| 37 | exit 0 |
| 38 | ;; |
| 39 | |
| 40 | --arch=*) |
| 41 | arch="${1#--arch=}" |
| 42 | shift |
| 43 | ;; |
| 44 | |
| 45 | *=*) |
| 46 | env[envi++]='-E' |
| 47 | env[envi++]="$1" |
| 48 | shift |
| 49 | ;; |
| 50 | |
| 51 | --) |
| 52 | shift |
| 53 | break |
| 54 | ;; |
| 55 | |
| 56 | *) |
| 57 | break |
| 58 | ;; |
| 59 | esac |
| 60 | done |
| 61 | |
| 62 | if [ $# -lt 1 -o -z "$arch" ]; then |
| 63 | echo "$usage" >&2 |
| 64 | echo "Type '${progname} --help' for more detailed help." >&2 |
| 65 | exit 1 |
| 66 | fi |
| 67 | |
| 68 | test_args=("$@") |
| 69 | |
| 70 | if [ -z "$NACL_SDK_ROOT" ]; then |
| 71 | echo >&2 "$0: NACL_SDK_ROOT must be set in the environment" |
| 72 | exit 77 |
| 73 | fi |
| 74 | |
| 75 | # We use a handful of things from the NaCl SDK, or at least |
| 76 | # from a directory matching the layout of the NaCl SDK. |
| 77 | sdk_tools="${NACL_SDK_ROOT}/tools" |
| 78 | |
| 79 | NACL_BOOTSTRAP="${sdk_tools}/nacl_helper_bootstrap_${arch}" |
| 80 | NACL_SEL_LDR="${sdk_tools}/sel_ldr_${arch}" |
| 81 | NACL_IRT="${sdk_tools}/irt_core_${arch}.nexe" |
| 82 | NACL_LOADER="${sdk_tools}/elf_loader_${arch}.nexe" |
| 83 | |
| 84 | if [ ! -x "$NACL_BOOTSTRAP" -o ! -x "$NACL_SEL_LDR" ]; then |
| 85 | echo >&2 "$0: sel_ldr_${arch} and/or nacl_helper_bootstrap_${arch} missing" |
| 86 | echo >&2 "$0: from directory $sdk_tools" |
| 87 | exit 77 |
| 88 | fi |
| 89 | |
| 90 | if [ ! -r "$NACL_IRT" -o ! -r "$NACL_LOADER" ]; then |
| 91 | echo >&2 "$0: irt_core_${arch}.nexe and/or loader_${arch}.nexe missing" |
| 92 | echo >&2 "$0: from directory $sdk_tools" |
| 93 | exit 77 |
| 94 | fi |
| 95 | |
| 96 | # Figure out if we are building for the native machine or not. |
| 97 | # If not, we'll run sel_ldr under qemu. |
| 98 | decide_use_emulator() |
| 99 | { |
| 100 | local arg |
| 101 | for arg; do |
| 102 | if [[ "$(uname -m)" = "$1" ]]; then |
| 103 | return |
| 104 | fi |
| 105 | done |
| 106 | use_emulator=true |
| 107 | } |
| 108 | |
| 109 | use_emulator=false |
| 110 | case "$arch" in |
| 111 | arm) |
| 112 | decide_use_emulator 'arm*' |
| 113 | emulator=(qemu-arm -cpu cortex-a15 -L "${sdk_tools}/arm_trusted") |
| 114 | ;; |
| 115 | x86_32) |
| 116 | decide_use_emulator 'i?86' 'x86_64*' |
| 117 | emulator=(qemu-i386) |
| 118 | ;; |
| 119 | x86_64) |
| 120 | decide_use_emulator 'x86_64*' |
| 121 | emulator=(qemu-x86_64) |
| 122 | ;; |
| 123 | esac |
| 124 | |
| 125 | if $use_emulator; then |
| 126 | ldr_args=('-Q') |
| 127 | emulator_factor=10 |
| 128 | else |
| 129 | emulator=() |
| 130 | ldr_args=() |
| 131 | emulator_factor=1 |
| 132 | fi |
| 133 | |
| 134 | if $use_bootstrap; then |
| 135 | ldr=( |
| 136 | "${NACL_BOOTSTRAP}" |
| 137 | "${NACL_SEL_LDR}" |
| 138 | '--r_debug=0xXXXXXXXXXXXXXXXX' |
| 139 | '--reserved_at_zero=0xXXXXXXXXXXXXXXXX' |
| 140 | ) |
| 141 | else |
| 142 | ldr=("${NACL_SEL_LDR}") |
| 143 | fi |
| 144 | |
| 145 | static=true |
| 146 | case "$1" in |
| 147 | */ld-nacl*) static=false ;; |
| 148 | esac |
| 149 | |
| 150 | if $static; then |
| 151 | loader=() |
| 152 | else |
| 153 | loader=(-f "${NACL_LOADER}") |
| 154 | fi |
| 155 | |
| 156 | run_test() |
| 157 | { |
| 158 | local test_fifo="$1" |
| 159 | local cmd=( |
| 160 | "${emulator[@]}" "${ldr[@]}" -q -S -a "${ldr_args[@]}" -B "${NACL_IRT}" |
| 161 | "${loader[@]}" "${env[@]}" -E TEST_DIRECT="$test_fifo" -- "${test_args[@]}" |
| 162 | ) |
| 163 | if [ "${NACLVERBOSITY:+set}" = set ]; then |
| 164 | "${cmd[@]}" |
| 165 | else |
| 166 | NACLLOG=/dev/null "${cmd[@]}" |
| 167 | fi |
| 168 | } |
| 169 | |
| 170 | temp_files=() |
| 171 | test_fifo= |
| 172 | do_cleanup() |
| 173 | { |
| 174 | rm -rf "$test_fifo" "${temp_files[@]}" |
| 175 | } |
| 176 | trap do_cleanup EXIT HUP INT TERM |
| 177 | |
| 178 | # Create a named pipe to receive the TEST_DIRECT information from the test |
| 179 | # program. |
| 180 | test_fifo=${TMPDIR:-/tmp}/libc-test-fifo.$$ |
| 181 | rm -f "$test_fifo" |
| 182 | mkfifo "$test_fifo" || { |
| 183 | echo "Cannot create test FIFO '$test_fifo'" |
| 184 | exit 1 |
| 185 | } |
| 186 | |
| 187 | # Run the test in the background, so we can implement a timeout. |
| 188 | # The no-op redirection defeats the default behavior of "< /dev/null" |
| 189 | # for a background command. |
| 190 | run_test "$test_fifo" <&0 & test_pid=$! |
| 191 | |
| 192 | # Set up a short timeout before we read from the FIFO, in case |
| 193 | # the program doesn't actually write to the FIFO at all (it is |
| 194 | # not a test-skeleton.c program, or it dies very early). |
| 195 | no_skeleton=false |
| 196 | script_pid=$$ |
| 197 | trap 'no_skeleton=true' USR1 |
| 198 | (sleep 2; kill -USR1 $script_pid) 2> /dev/null & |
| 199 | |
| 200 | # The test should first write into the FIFO to describe its expectations. |
| 201 | # Our open-for-reading of the FIFO will block until the test starts up and |
| 202 | # opens it for writing. Then our reads will block until the test finishes |
| 203 | # writing out info and closes the FIFO. At that point we will have |
| 204 | # collected (and evaluated) what it emitted. It sets these variables: |
| 205 | # timeout=%u |
| 206 | # timeoutfactor=%u |
| 207 | # exit=%u |
| 208 | # signal=%s |
| 209 | unset exit signal |
| 210 | . "$test_fifo" 2> /dev/null |
| 211 | |
| 212 | # If we got this far, either the 'no_skeleton=true' watchdog already |
| 213 | # fired, or else we don't want it to. |
| 214 | trap '' USR1 |
| 215 | |
| 216 | if $no_skeleton; then |
| 217 | # We hit the timeout, so we didn't get full information about test |
| 218 | # expectations. Reset any partial results we may have gotten. |
| 219 | unset exit signal |
| 220 | else |
| 221 | # Now we know the expected timeout, so we can start the timer running. |
| 222 | ((sleep_time = timeout * timeoutfactor * emulator_factor)) |
| 223 | |
| 224 | # Now start a background subshell to enforce the timeout. |
| 225 | (sleep "$sleep_time"; kill -ALRM $test_pid) 2> /dev/null & |
| 226 | fi |
| 227 | |
| 228 | # This corresponds to '#ifdef EXPECTED_STATUS' in test-skeleton.c. |
| 229 | expected_status() |
| 230 | { |
| 231 | test "${exit+yes}" = yes |
| 232 | } |
| 233 | # This corresponds to '#ifdef EXPECTED_SIGNAL' in test-skeleton.c. |
| 234 | expected_signal() |
| 235 | { |
| 236 | test "${signal+yes}" = yes |
| 237 | } |
| 238 | # This corresponds to 'if (WIFEXITED (status))' in test-skeleton.c. |
| 239 | wifexited() |
| 240 | { |
| 241 | test $test_rc -lt 128 |
| 242 | } |
| 243 | |
| 244 | # Now wait for the test process to finish. |
| 245 | wait $test_pid |
| 246 | test_rc=$? |
| 247 | |
| 248 | # This exactly duplicates the logic in test-skeleton.c. |
| 249 | if wifexited; then |
| 250 | if ! expected_status; then |
| 251 | if ! expected_signal; then |
| 252 | # Simply exit with the return value of the test. */ |
| 253 | exit $test_rc |
| 254 | else |
| 255 | echo "Expected signal '${signal}' from child, got none" |
| 256 | exit 1 |
| 257 | fi |
| 258 | else |
| 259 | if [ $test_rc -ne $exit ]; then |
| 260 | echo "Expected status $exit, got $test_rc" |
| 261 | exit 1 |
| 262 | fi |
| 263 | exit 0 |
| 264 | fi |
| 265 | else |
| 266 | # Process was killed by timer or other signal. |
| 267 | ((test_signal = test_rc > 192 ? 256 - test_rc : test_rc - 128 )) |
| 268 | test_signame=$(kill -l "$test_signal") |
| 269 | if ! expected_signal; then |
| 270 | echo "Didn't expect signal from child; got '${test_signame}'" |
| 271 | exit 1 |
| 272 | else |
| 273 | if [ "$test_signame" != "$signal" ]; then |
| 274 | echo "\ |
| 275 | Incorrect signal from child: got '${test_signame}', need '${signal}'" |
| 276 | exit 1 |
| 277 | fi |
| 278 | exit 0 |
| 279 | fi |
| 280 | fi |