1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
|
#!/bin/sh
set -eu
: "${CPU_AS:?}" "${CPU_LD:?}" "${CPU_RUN:?}" "${CPU_READELF:?}"
: "${MCPU_TEST_ROOT:?}" "${MCPU_HLL_FIXTURE_SRCDIR:?}"
TMP=${TMPDIR:-/tmp}/libmcpu-hll-crt-$$
mkdir -p "$TMP"
trap 'rm -rf "$TMP"' EXIT HUP INT TERM
as()
{
"$CPU_AS" -o "$2" "$1" >/dev/null
}
must_fail_link()
{
if "$CPU_LD" "$@" >/dev/null 2>&1; then
echo "mcpu-ld unexpectedly accepted invalid HLL input: $*" >&2
exit 1
fi
}
# ---------------------------------------------------------------------------
# Regression fixtures for compiler-normalized HLL objects and libc hooks.
# ---------------------------------------------------------------------------
as "$MCPU_HLL_FIXTURE_SRCDIR/program-main.S" "$TMP/program.o"
as "$MCPU_HLL_FIXTURE_SRCDIR/model-pendulum-life.S" "$TMP/model.o"
as "$MCPU_HLL_FIXTURE_SRCDIR/libc-stub.S" "$TMP/libc-stub.o"
# The future compiler output owns HLL functions, never MELF lifecycle symbols.
"$CPU_READELF" -s "$TMP/program.o" > "$TMP/program.syms"
grep -q ' main$' "$TMP/program.syms"
grep -q ' fini$' "$TMP/program.syms"
if grep -Eq ' (_start|_life|_fini)$' "$TMP/program.syms"; then
echo "PROGRAM compiler-output emulator defines lifecycle symbols" >&2
exit 1
fi
"$CPU_READELF" -s "$TMP/model.o" > "$TMP/model.syms"
grep -q ' init$' "$TMP/model.syms"
grep -q ' life$' "$TMP/model.syms"
grep -q ' fini$' "$TMP/model.syms"
if grep -Eq ' (_start|_life|_fini)$' "$TMP/model.syms"; then
echo "MODEL compiler-output emulator defines lifecycle symbols" >&2
exit 1
fi
# ---------------------------------------------------------------------------
# PROGRAM: automatic classification, CRT insertion, argc/argv bridge and fini.
# ---------------------------------------------------------------------------
"$CPU_LD" --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \
-o "$TMP/program" "$TMP/program.o" "$TMP/libc-stub.o" >/dev/null
"$CPU_READELF" -h "$TMP/program" > "$TMP/program.hdr"
entry=`sed -n 's/^ Entry point address:[[:space:]]*//p' "$TMP/program.hdr"`
init=`sed -n 's/^ INIT entry address:[[:space:]]*//p' "$TMP/program.hdr"`
life=`sed -n 's/^ LIFE entry address:[[:space:]]*//p' "$TMP/program.hdr"`
fini=`sed -n 's/^ FINI entry address:[[:space:]]*//p' "$TMP/program.hdr"`
test "x$entry" = "x$life"
test "x$init" = "x0x0000000000000000"
test "x$fini" != "x0x0000000000000000"
set +e
out=`"$CPU_RUN" --after-run=print-int-regs:r64@0x70000 \
-- "$TMP/program" alpha beta`
status=$?
set -e
test "$status" -eq 42
test "x$out" = "x6"
# ABI-v3 PROGRAM uses the same HLL interface with the runtime hooks supplied
# by a normal shared MCPU Libc dependency. The four hook names must therefore
# be exported from ET_DYN, while explicit preloads still may not interpose them.
"$CPU_LD" --shared --soname=libhllc.so.1 \
-o "$TMP/libhllc.so.1" "$TMP/libc-stub.o" >/dev/null
ln -s libhllc.so.1 "$TMP/libhllc.so"
"$CPU_READELF" -s "$TMP/libhllc.so.1" > "$TMP/libhllc.syms"
grep -q ' __mcpu_libc_init$' "$TMP/libhllc.syms"
grep -q ' __mcpu_init_array$' "$TMP/libhllc.syms"
grep -q ' __mcpu_fini_array$' "$TMP/libhllc.syms"
grep -q ' __mcpu_libc_fini$' "$TMP/libhllc.syms"
"$CPU_LD" --dynamic --sys-root="$MCPU_TEST_ROOT" \
--stack-size=4K --no-heap -L"$TMP" -lhllc \
-o "$TMP/program-dyn" "$TMP/program.o" >/dev/null
set +e
out=`MCPU_LD_PRELOAD= "$CPU_RUN" --library-path="$TMP" \
--after-run=print-int-regs:r64@0x70000 \
-- "$TMP/program-dyn" alpha beta`
status=$?
set -e
test "$status" -eq 42
test "x$out" = "x6"
# HLL complete-program functions remain bound to the main executable. Runtime
# hooks come from the normal libc dependency. A preload may export all of the
# same names but cannot interpose either class of protected HLL symbol.
cat > "$TMP/override.s" <<'EOS'
.global main
.global fini
.global __mcpu_libc_init
.global __mcpu_init_array
.global __mcpu_fini_array
.global __mcpu_libc_fini
.text
main:
LDI r64@0x70000, 700
LDI r32@0x7fffc, 70
RET
fini:
LDI r64@0x70000, 701
RET
__mcpu_libc_init:
LDI r64@0x70000, 702
RET
__mcpu_init_array:
LDI r64@0x70000, 703
RET
__mcpu_fini_array:
LDI r64@0x70000, 704
RET
__mcpu_libc_fini:
LDI r64@0x70000, 705
RET
EOS
as "$TMP/override.s" "$TMP/override.o"
"$CPU_LD" --shared --soname=libhll-override.so.1 \
-o "$TMP/libhll-override.so" "$TMP/override.o" >/dev/null
set +e
out=`MCPU_LD_PRELOAD= "$CPU_RUN" --library-path="$TMP" \
--ldpreload="$TMP/libhll-override.so" \
--after-run=print-int-regs:r64@0x70000 \
-- "$TMP/program-dyn" alpha beta`
status=$?
set -e
test "$status" -eq 42
test "x$out" = "x6"
# ---------------------------------------------------------------------------
# MODEL: compact pendulum-shaped compiler-output regression fixture.
# ---------------------------------------------------------------------------
"$CPU_LD" --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \
-o "$TMP/model" "$TMP/model.o" "$TMP/libc-stub.o" >/dev/null
"$CPU_READELF" -h "$TMP/model" > "$TMP/model.hdr"
entry=`sed -n 's/^ Entry point address:[[:space:]]*//p' "$TMP/model.hdr"`
init=`sed -n 's/^ INIT entry address:[[:space:]]*//p' "$TMP/model.hdr"`
life=`sed -n 's/^ LIFE entry address:[[:space:]]*//p' "$TMP/model.hdr"`
fini=`sed -n 's/^ FINI entry address:[[:space:]]*//p' "$TMP/model.hdr"`
test "x$entry" = "x$init"
test "x$life" != "x$entry"
test "x$fini" != "x0x0000000000000000"
# Natural MODEL run: three LIFE_CONTINUE boundaries, then LIFE_END.
set +e
out=`"$CPU_RUN" --steps=10 \
--on-step=print-int-trace:64@0 \
--after-run=print-int-regs:r64@0x70000,r64@0x70010 \
-- "$TMP/model"`
status=$?
set -e
test "$status" -eq 0
expected='1
2
3
6 1'
test "x$out" = "x$expected"
# init() failure is already RUNNING: normal HLL finalization is mandatory.
set +e
out=`"$CPU_RUN" --before-run=set-int-control:64@0=-1 \
--after-run=print-int-regs:r64@0x70000,r64@0x70010 \
-- "$TMP/model"`
status=$?
set -e
test "$status" -eq 17
test "x$out" = "x6 1"
# Positive life() status is a MODEL error and performs normal finalization.
set +e
out=`"$CPU_RUN" --steps=10 \
--before-run=set-int-control:64@8=2 \
--on-step=print-int-trace:64@0 \
--after-run=print-int-regs:r64@0x70000,r64@0x70010 \
-- "$TMP/model"`
status=$?
set -e
test "$status" -eq 9
expected='1
6 1'
test "x$out" = "x$expected"
# Diagnostic --steps stop does not synthesize FINI.
out=`"$CPU_RUN" --steps=1 \
--on-step=print-int-trace:64@0 \
--after-run=print-int-regs:r64@0x70000,r64@0x70010 \
-- "$TMP/model"`
expected='1
3 0'
test "x$out" = "x$expected"
# ---------------------------------------------------------------------------
# PRE_RUNTIME failure: exercise the real common CRT startup/finalization code.
# A test-only PROGRAM adapter corrupts McpuStartup.version before entering the
# real __mcpu_crt_startup. The normal user object and libc stub are unchanged.
# ---------------------------------------------------------------------------
mkdir -p "$TMP/bad-root/lib/mcpu"
cp "$MCPU_TEST_ROOT/lib/mcpu/crti.o" "$TMP/bad-root/lib/mcpu/"
cp "$MCPU_TEST_ROOT/lib/mcpu/crtn.o" "$TMP/bad-root/lib/mcpu/"
cp "$MCPU_TEST_ROOT/lib/mcpu/crt1.o" "$TMP/bad-root/lib/mcpu/"
cat > "$TMP/bad-crt0.s" <<'EOS'
.global __mcpu_crt_startup
.global main
.text
__mcpu_crt_hll_program:
_start_life:
LDI r32@0x7fe00, 99
STSP 0, r32@0x7fe00
CALL __mcpu_crt_startup
LDI r32@0x7fe08, 88
EXIT r32@0x7fe08
EOS
as "$TMP/bad-crt0.s" "$TMP/bad-root/lib/mcpu/crt0.o"
"$CPU_LD" --sys-root="$TMP/bad-root" --stack-size=4K --no-heap \
-o "$TMP/bad-startup" "$TMP/program.o" "$TMP/libc-stub.o" >/dev/null
set +e
out=`"$CPU_RUN" --after-run=print-int-regs:r64@0x70000 \
-- "$TMP/bad-startup" alpha beta`
status=$?
set -e
test "$status" -ne 0
test "x$out" = "x0"
# ---------------------------------------------------------------------------
# HLL complete-program classification errors.
# ---------------------------------------------------------------------------
cat > "$TMP/main-only.s" <<'EOS'
.global main
.text
main:
CLR r32@0x7fffc
RET
EOS
as "$TMP/main-only.s" "$TMP/main-only.o"
must_fail_link --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \
-o "$TMP/bad-main-only" "$TMP/main-only.o" "$TMP/libc-stub.o"
cat > "$TMP/model-init.s" <<'EOS'
.global init
.global fini
.text
init:
CLR r32@0x7fffc
RET
fini:
RET
EOS
cat > "$TMP/model-life.s" <<'EOS'
.global life
.text
life:
CLR r32@0x7fffc
RET
EOS
as "$TMP/model-init.s" "$TMP/model-init.o"
as "$TMP/model-life.s" "$TMP/model-life.o"
must_fail_link --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \
-o "$TMP/bad-split-model" "$TMP/model-init.o" "$TMP/model-life.o" \
"$TMP/libc-stub.o"
cat > "$TMP/mixed.s" <<'EOS'
.global main
.global fini
.text
_start_life:
main:
CLR r32@0x7fffc
RET
fini:
RET
EOS
as "$TMP/mixed.s" "$TMP/mixed.o"
must_fail_link --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \
-o "$TMP/bad-mixed" "$TMP/mixed.o" "$TMP/libc-stub.o"
cat > "$TMP/reserved.s" <<'EOS'
.global __mcpu_crt_user_collision
.text
__mcpu_crt_user_collision:
RET
EOS
as "$TMP/reserved.s" "$TMP/reserved.o"
must_fail_link --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \
-o "$TMP/bad-reserved" "$TMP/reserved.o"
# ---------------------------------------------------------------------------
# Frozen ET_DYN ownership rule: model/lifecycle sections belong to ET_EXEC.
# ---------------------------------------------------------------------------
for sec in init fini control input output trace; do
case "$sec" in
init|fini)
cat > "$TMP/shared-$sec.s" <<EOS
.$sec
shared_${sec}_object:
RET
EOS
;;
*)
cat > "$TMP/shared-$sec.s" <<EOS
.$sec
shared_${sec}_object:
.integer 64, 0
EOS
;;
esac
as "$TMP/shared-$sec.s" "$TMP/shared-$sec.o"
if "$CPU_LD" --shared --soname="libbad-$sec.so.1" \
-o "$TMP/libbad-$sec.so" "$TMP/shared-$sec.o" >/dev/null 2>&1; then
echo "ET_DYN unexpectedly accepted .$sec" >&2
exit 1
fi
done
cat > "$TMP/shared-lifecycle.s" <<'EOS'
.text
_start_life:
END
EOS
as "$TMP/shared-lifecycle.s" "$TMP/shared-lifecycle.o"
if "$CPU_LD" --shared --soname=libbad-lifecycle.so.1 \
-o "$TMP/libbad-lifecycle.so" "$TMP/shared-lifecycle.o" >/dev/null 2>&1; then
echo "ET_DYN unexpectedly accepted lifecycle symbols" >&2
exit 1
fi
exit 0
|