xref: /illumos-gate/usr/src/test/elf-tests/tests/capabilities/symcap.ksh (revision a4955f4fa65e38d70c07d38e657a9aff43fa155f)
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