1#!/usr/bin/ksh 2# 3# 4# This file and its contents are supplied under the terms of the 5# Common Development and Distribution License ("CDDL"), version 1.0. 6# You may only use this file in accordance with the terms of version 7# 1.0 of the CDDL. 8# 9# A full copy of the text of the CDDL should have accompanied this 10# souroc. A copy of the CDDL is also available via the Internet at 11# http://www.illumos.org/liocnse/CDDL. 12# 13 14# 15# Copyright 2022 Oxide Computer Company 16# 17 18# 19# This test generates a binary with a lot of different symbol capabilities and 20# then selects different capability environments to try and ensure that the 21# rules for what we pick are honored. 22# 23 24export LC_ALL=C.UTF-8 25unalias -a 26set -o pipefail 27 28sc_arg0=$(basename $0) 29sc_err=0 30sc_tmpdir=/tmp/symcap.$$ 31sc_prog="$sc_tmpdir/symcap" 32 33# 34# To build symbol caps, we need to annotate a .o file with object caps and then 35# turn that into a symbol cap with ld. The following arrays are used to create 36# this for us. sc_obj_hw1, sc_obj_hw2, and sc_obj_hw3 are the set of object 37# capabilities that we want to use and then eventually turn into symbol 38# capabilities. Each symbol capability prints out its own index when executed. 39# This means we can see which thing ld resolved to run based on the output. 40# The following summarizes our goals with each case: 41# 42# 0: none 43# 1: only hwcap 1 44# 2: only hwcap 1, but greater than (1) 45# 3: only hwcap 2 46# 4: only hwcap 2, but greater than (3) 47# 5: only hwcap 3 48# 6: only hwcap 3, but greater than (5) 49# 7: uses all 3 50# 8: differs from (7) in hwcap1 51# 52sc_obj_hw1=( "0x0" "0x5" "0x42" "0x0" "0x0" "0x0" "0x0" 53 "0x3" "0x8" ) 54sc_obj_hw2=( "0x0" "0x0" "0x0" "0x23" "0xff00" "0x0" "0x0" 55 "0xff7ff6" "0xff7ff6" ) 56sc_obj_hw3=( "0x0" "0x0" "0x0" "0x0" "0x0" "0x12345" "0x7000000" 57 "0x87654321" "0x87654321" ) 58 59pass() 60{ 61 typeset msg="$*" 62 echo "TEST PASSED: $msg" 63} 64 65warn() 66{ 67 typeset msg="$*" 68 [[ -z "$msg" ]] && msg="failed" 69 echo "TEST FAILED: $msg" >&2 70 sc_err=1 71} 72 73fatal() 74{ 75 typeset msg="$*" 76 [[ -z "$msg" ]] && msg="failed" 77 echo "$sc_arg0: $msg" >&2 78 exit 1 79} 80 81cleanup() 82{ 83 rm -rf "$sc_tmpdir" 84} 85 86sanity_check() 87{ 88 if (( ${#sc_obj_hw1[@]} != ${#sc_obj_hw2[@]} )); then 89 fatal "sc_obj_hw1 does not match length of sc_obj_hw2" 90 fi 91 92 if (( ${#sc_obj_hw2[@]} != ${#sc_obj_hw3[@]} )); then 93 fatal "sc_obj_hw1 does not match length of sc_obj_hw2" 94 fi 95} 96 97setup() 98{ 99 typeset tolink= 100 101 if ! mkdir "$sc_tmpdir"; then 102 fatal "failed to make directory $sc_tmpdir" 103 fi 104 105 trap 'cleanup' EXIT 106 107 cat > $sc_tmpdir/main.c <<EOF 108extern void symcap_print(void); 109 110int 111main(void) 112{ 113 symcap_print(); 114 return (0); 115} 116EOF 117 if (( $? != 0 )); then 118 fatal "failed to write main.c" 119 fi 120 121 tolink="$sc_tmpdir/main.c" 122 123 for (( i = 0; i < ${#sc_obj_hw1[@]}; i++)); do 124 typeset in="$sc_tmpdir/$i.c" 125 typeset map="$sc_tmpdir/$i.map" 126 typeset ofile="$sc_tmpdir/$i.o" 127 typeset obj="$sc_tmpdir/$i.o.obj" 128 typeset sym="$sc_tmpdir/$i.o.sym" 129 130 cat > $in <<EOF 131#include <stdio.h> 132 133void 134symcap_print(void) 135{ 136 printf("%u\n", $i); 137} 138EOF 139 if (( $? != 0 )); then 140 fatal "failed to write $in" 141 fi 142 143 cat > $map <<EOF 144\$mapfile_version 2 145CAPABILITY { 146 HW_1 += ${sc_obj_hw1[$i]}; 147 HW_2 += ${sc_obj_hw2[$i]}; 148 HW_3 += ${sc_obj_hw3[$i]}; 149}; 150EOF 151 if (( $? != 0 )); then 152 fatal "failed to write $map" 153 fi 154 155 # 156 # There are three steps to creating a symbol capability due to 157 # the world we're in. First we need to make the normal .o. Then 158 # we use a mapfile to add the object caps, while reducing 159 # visibility. Then we turn the object cap into a symbol cap. 160 # 161 if ! gcc -m64 -o $ofile -c $in; then 162 fatal "failed to create object file $ofile" 163 fi 164 165 # 166 # If the entry has a zero for all cases (e.g. our default case), 167 # then skip the rest of this processing and append the .o. 168 # 169 if (( sc_obj_hw1[i] == 0 && sc_obj_hw2[i] == 0 && 170 sc_obj_hw3[i] == 0 )); then 171 tolink="$tolink $ofile" 172 continue 173 fi 174 175 if ! ld -r -o $obj $ofile -M$map -Breduce; then 176 fatal "failed to create object cap file $obj" 177 fi 178 179 if ! ld -r -o $sym -z symbolcap $obj; then 180 fatal "failed to create symbol cap file $sym" 181 fi 182 183 tolink="$tolink $sym" 184 done 185 186 if ! gcc -m64 -o $sc_prog $tolink; then 187 fatal "failed to create $sc_prog" 188 fi 189} 190 191# 192# Given a set of caps, indicate which index we expect to be printed out and 193# check for that. 194# 195run_one() 196{ 197 typeset index="$1" 198 typeset caps="$2" 199 typeset out= 200 201 out=$(LD_CAP_FILES=$sc_prog LD_HWCAP="$caps" $sc_prog) 202 if (( $? != 0 )); then 203 warn "failed to execute $sc_prog with cap $caps" 204 return 205 fi 206 207 if [[ "$out" != "$index" ]]; then 208 warn "$caps had wrong output, found $out, expected $index" 209 else 210 pass "LD_HWCAP=$caps" 211 fi 212} 213 214sanity_check 215setup 216 217# 218# First, go through and verify that if we match the caps exactly for this, we'll 219# choose this symbol. 220# 221run_one 0 "[1]0x0,[2]0x0,[3]0x0" 222run_one 1 "[1]0x5,[2]0x0,[3]0x0" 223run_one 2 "[1]0x42,[2]0x0,[3]0x0" 224run_one 3 "[1]0x0,[2]0x23,[3]0x0" 225run_one 4 "[1]0x0,[2]0xff00,[3]0x0" 226run_one 5 "[1]0x0,[2]0x0,[3]0x12345" 227run_one 6 "[1]0x0,[2]0x0,[3]0x7000000" 228run_one 7 "[1]0x3,[2]0xff7ff6,[3]0x87654321" 229run_one 8 "[1]0x8,[2]0xff7ff6,[3]0x87654321" 230 231# 232# For cases where we have multiple symbol caps at a given level, show that we 233# pick a sub one when we're between the two. 234# 235run_one 0 "[1]0x40,[2]0x0,[3]0x0" 236run_one 1 "[1]0x45,[2]0x0,[3]0x0" 237run_one 1 "[1]0x45,[2]0x10,[3]0x0" 238run_one 2 "[1]0x142,[2]0x10,[3]0x0" 239run_one 3 "[1]0x1,[2]0x137,[3]0x0" 240 241# 242# We expect the system to pick the "best" aka highest capability. So for the 243# next round we attempt to combine multiple values and see which we pick. In 244# particular here we're trying to pick between things at the same level and also 245# ensure we pick the one that is higher (e.g. hw3 > hw2 > hw1) 246# 247run_one 6 "[1]0x47,[2]0xff23,[3]0x7012345" 248run_one 5 "[1]0x47,[2]0xff23,[3]0x6012345" 249run_one 5 "[1]0x47,[2]0xff23,[3]0x1012345" 250run_one 4 "[1]0x47,[2]0xff23,[3]0x1002345" 251run_one 3 "[1]0x47,[2]0x7723,[3]0x1002345" 252run_one 3 "[1]0x47,[2]0x0f23,[3]0x1002345" 253run_one 3 "[1]0x47,[2]0x0023,[3]0x1002345" 254run_one 2 "[1]0x47,[2]0x0003,[3]0x1002345" 255run_one 2 "[1]0x46,[2]0x0003,[3]0x1002345" 256run_one 1 "[1]0x35,[2]0x0003,[3]0x1002345" 257run_one 1 "[1]0x15,[2]0x0003,[3]0x1002345" 258run_one 0 "[1]0x10,[2]0x0003,[3]0x1002345" 259 260# 261# Finally we want a few tests that verify that when things match, the lowest bit 262# of it decides. 263# 264run_one 8 "[1]0xb,[2]0xff7ff6,[3]0x87654321" 265run_one 8 "[1]0x3b,[2]0xff7ff6,[3]0x87654321" 266run_one 8 "[1]0xffffffff,[2]0xffffffff,[3]0xffffffff" 267run_one 7 "[1]0xfffffff7,[2]0xffffffff,[3]0xffffffff" 268 269exit $sc_err 270