summaryrefslogtreecommitdiff
path: root/tests/tst-hll-crt-runner.sh
blob: ca036418436aca7ac783fc7b3cf0cca6bbb6bbf2 (plain)
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