#!/bin/bash # test-wrapper script for NaCl. # Copyright (C) 2015-2016 Free Software Foundation, Inc. # This file is part of the GNU C Library. # The GNU C Library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # The GNU C Library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # You should have received a copy of the GNU Lesser General Public # License along with the GNU C Library; if not, see # . progname="$(basename "$0")" usage="usage: ${progname} --arch=ARCH [VAR=VAL...] COMMAND ..." help=" " use_bootstrap=true arch= env=() envi=0 while [ $# -gt 0 ]; do case "$1" in --help) echo "$usage" echo "$help" exit 0 ;; --arch=*) arch="${1#--arch=}" shift ;; *=*) env[envi++]='-E' env[envi++]="$1" shift ;; --) shift break ;; *) break ;; esac done if [ $# -lt 1 -o -z "$arch" ]; then echo "$usage" >&2 echo "Type '${progname} --help' for more detailed help." >&2 exit 1 fi test_args=("$@") if [ -z "$NACL_SDK_ROOT" ]; then echo >&2 "$0: NACL_SDK_ROOT must be set in the environment" exit 77 fi # We use a handful of things from the NaCl SDK, or at least # from a directory matching the layout of the NaCl SDK. sdk_tools="${NACL_SDK_ROOT}/tools" NACL_BOOTSTRAP="${sdk_tools}/nacl_helper_bootstrap_${arch}" NACL_SEL_LDR="${sdk_tools}/sel_ldr_${arch}" NACL_IRT="${sdk_tools}/irt_core_${arch}.nexe" NACL_LOADER="${sdk_tools}/elf_loader_${arch}.nexe" if [ ! -x "$NACL_BOOTSTRAP" -o ! -x "$NACL_SEL_LDR" ]; then echo >&2 "$0: sel_ldr_${arch} and/or nacl_helper_bootstrap_${arch} missing" echo >&2 "$0: from directory $sdk_tools" exit 77 fi if [ ! -r "$NACL_IRT" -o ! -r "$NACL_LOADER" ]; then echo >&2 "$0: irt_core_${arch}.nexe and/or loader_${arch}.nexe missing" echo >&2 "$0: from directory $sdk_tools" exit 77 fi # Figure out if we are building for the native machine or not. # If not, we'll run sel_ldr under qemu. decide_use_emulator() { local arg for arg; do if [[ "$(uname -m)" = "$1" ]]; then return fi done use_emulator=true } use_emulator=false case "$arch" in arm) decide_use_emulator 'arm*' emulator=(qemu-arm -cpu cortex-a15 -L "${sdk_tools}/arm_trusted") ;; x86_32) decide_use_emulator 'i?86' 'x86_64*' emulator=(qemu-i386) ;; x86_64) decide_use_emulator 'x86_64*' emulator=(qemu-x86_64) ;; esac if $use_emulator; then ldr_args=('-Q') emulator_factor=10 else emulator=() ldr_args=() emulator_factor=1 fi if $use_bootstrap; then ldr=( "${NACL_BOOTSTRAP}" "${NACL_SEL_LDR}" '--r_debug=0xXXXXXXXXXXXXXXXX' '--reserved_at_zero=0xXXXXXXXXXXXXXXXX' ) else ldr=("${NACL_SEL_LDR}") fi static=true case "$1" in */ld-nacl*) static=false ;; esac if $static; then loader=() else loader=(-f "${NACL_LOADER}") fi run_test() { local test_fifo="$1" local cmd=( "${emulator[@]}" "${ldr[@]}" -q -S -a "${ldr_args[@]}" -B "${NACL_IRT}" "${loader[@]}" "${env[@]}" -E TEST_DIRECT="$test_fifo" -- "${test_args[@]}" ) if [ "${NACLVERBOSITY:+set}" = set ]; then "${cmd[@]}" else NACLLOG=/dev/null "${cmd[@]}" fi } temp_files=() test_fifo= do_cleanup() { rm -rf "$test_fifo" "${temp_files[@]}" } trap do_cleanup EXIT HUP INT TERM # Create a named pipe to receive the TEST_DIRECT information from the test # program. test_fifo=${TMPDIR:-/tmp}/libc-test-fifo.$$ rm -f "$test_fifo" mkfifo "$test_fifo" || { echo "Cannot create test FIFO '$test_fifo'" exit 1 } # Run the test in the background, so we can implement a timeout. # The no-op redirection defeats the default behavior of "< /dev/null" # for a background command. run_test "$test_fifo" <&0 & test_pid=$! # Set up a short timeout before we read from the FIFO, in case # the program doesn't actually write to the FIFO at all (it is # not a test-skeleton.c program, or it dies very early). no_skeleton=false script_pid=$$ trap 'no_skeleton=true' USR1 (sleep 2; kill -USR1 $script_pid) 2> /dev/null & # The test should first write into the FIFO to describe its expectations. # Our open-for-reading of the FIFO will block until the test starts up and # opens it for writing. Then our reads will block until the test finishes # writing out info and closes the FIFO. At that point we will have # collected (and evaluated) what it emitted. It sets these variables: # timeout=%u # timeoutfactor=%u # exit=%u # signal=%s unset exit signal . "$test_fifo" 2> /dev/null # If we got this far, either the 'no_skeleton=true' watchdog already # fired, or else we don't want it to. trap '' USR1 if $no_skeleton; then # We hit the timeout, so we didn't get full information about test # expectations. Reset any partial results we may have gotten. unset exit signal else # Now we know the expected timeout, so we can start the timer running. ((sleep_time = timeout * timeoutfactor * emulator_factor)) # Now start a background subshell to enforce the timeout. (sleep "$sleep_time"; kill -ALRM $test_pid) 2> /dev/null & fi # This corresponds to '#ifdef EXPECTED_STATUS' in test-skeleton.c. expected_status() { test "${exit+yes}" = yes } # This corresponds to '#ifdef EXPECTED_SIGNAL' in test-skeleton.c. expected_signal() { test "${signal+yes}" = yes } # This corresponds to 'if (WIFEXITED (status))' in test-skeleton.c. wifexited() { test $test_rc -lt 128 } # Now wait for the test process to finish. wait $test_pid test_rc=$? # This exactly duplicates the logic in test-skeleton.c. if wifexited; then if ! expected_status; then if ! expected_signal; then # Simply exit with the return value of the test. */ exit $test_rc else echo "Expected signal '${signal}' from child, got none" exit 1 fi else if [ $test_rc -ne $exit ]; then echo "Expected status $exit, got $test_rc" exit 1 fi exit 0 fi else # Process was killed by timer or other signal. ((test_signal = test_rc > 192 ? 256 - test_rc : test_rc - 128 )) test_signame=$(kill -l "$test_signal") if ! expected_signal; then echo "Didn't expect signal from child; got '${test_signame}'" exit 1 else if [ "$test_signame" != "$signal" ]; then echo "\ Incorrect signal from child: got '${test_signame}', need '${signal}'" exit 1 fi exit 0 fi fi