kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

exec_bare.sh (25211B)


      1 # test/lib/exec_bare.sh — bare-metal (freestanding) execution backend for
      2 # test/lib/exec_target.sh, and the single owner of kit's per-arch boot
      3 # scaffolding (reset stub + linker script + exit-code oracle + qemu-system
      4 # invocation) for aarch64 / x86_64 / riscv64 / riscv32 / arm32 (Cortex-M).
      5 #
      6 # This consolidates what used to be three scripts: the rv32 corpus runner
      7 # (exec_rv32_bare.sh), the aa64/rv64 ready-image runner (exec_kernel.sh, now a
      8 # thin shim over this file), and the per-arch stubs inlined in
      9 # test/smoke/freestanding_system.sh.
     10 #
     11 # Two contracts:
     12 #
     13 #   exec_bare_run_image <arch> <kernel.elf> <out> <err>   → sets RUN_RC
     14 #       Run a ready bootable image (the image owns its own _start + exit
     15 #       convention). What test/link's kernel_image cases need.
     16 #
     17 #   exec_bare_setup <arch> <work> [entry]
     18 #   exec_bare_run   <arch> <obj> <work> <rcfile>          → writes <rcfile>
     19 #       Link a corpus object (entry default `main`, returns the exit code) with
     20 #       the per-arch reset stub + (rv32) runtime into a bootable image, run it,
     21 #       and write the decoded guest exit code. What toy/parse need. exec_bare_run
     22 #       echoes a one-line reason and returns 0 (ran; rc in <rcfile>) or 2
     23 #       (build/link failure; caller decides skip vs fail).
     24 #
     25 #   exec_bare_supported <arch>   true if qemu-system-<arch> is on PATH.
     26 #
     27 # Exit-code oracle (so callers compare rc == expected uniformly):
     28 #   aa64  ARM semihosting SYS_EXIT_EXTENDED  → qemu rc = guest code
     29 #   rv*   SiFive test finisher MMIO @0x100000 → qemu rc = guest code
     30 #   x64   isa-debug-exit @0x501              → qemu rc = (code<<1)|1, decoded here
     31 #   arm32 ARM semihosting BKPT #0xAB + SYS_EXIT_EXTENDED → qemu rc = guest code
     32 #
     33 # Corpus depth (exec_bare_setup/run) is fully wired for rv32 only; the aa64/x64/
     34 # rv64 stubs here are smoke-capable (single object, no TLS/rt). Hardening them
     35 # for the toy/parse corpora is the tracked follow-up in doc/plan/PORT.md.
     36 
     37 EXEC_BARE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
     38 EXEC_BARE_KIT="${KIT:-$EXEC_BARE_ROOT/build/kit}"
     39 EXEC_BARE_TIMEOUT="timeout"
     40 command -v "$EXEC_BARE_TIMEOUT" >/dev/null 2>&1 || {
     41     command -v gtimeout >/dev/null 2>&1 && EXEC_BARE_TIMEOUT="gtimeout"
     42 }
     43 EXEC_BARE_TO="${EXEC_BARE_TO:-20}"
     44 
     45 # ---- arch helpers ----------------------------------------------------------
     46 _bare_canon() {
     47     case "$1" in
     48         aa64|aarch64|arm64) echo aa64 ;;
     49         x64|x86_64|amd64)   echo x64 ;;
     50         rv64|riscv64)       echo rv64 ;;
     51         rv32|riscv32)       echo rv32 ;;
     52         arm32|arm|armv7m|armv7em|cortex-m3|cortex-m4|cortex-m7) echo arm32 ;;
     53         *) return 1 ;;
     54     esac
     55 }
     56 _bare_qemu() {
     57     case "$(_bare_canon "$1")" in
     58         aa64) echo qemu-system-aarch64 ;;
     59         x64)  echo qemu-system-x86_64 ;;
     60         rv64) echo qemu-system-riscv64 ;;
     61         rv32) echo qemu-system-riscv32 ;;
     62         arm32) echo qemu-system-arm ;;
     63     esac
     64 }
     65 _bare_triple() {
     66     case "$(_bare_canon "$1")" in
     67         aa64) echo aarch64-none-elf ;;
     68         x64)  echo x86_64-none-elf ;;
     69         rv64) echo riscv64-none-elf ;;
     70         rv32) echo riscv32-none-elf ;;
     71         arm32) echo arm-none-eabi ;;
     72     esac
     73 }
     74 
     75 exec_bare_supported() {
     76     local q; q="$(_bare_qemu "$1")" || return 1
     77     [ -n "$q" ] && command -v "$q" >/dev/null 2>&1
     78 }
     79 
     80 # ---- qemu invocation + exit decode -----------------------------------------
     81 # Sets RUN_RC_RAW to the raw qemu-system exit status.
     82 _bare_qemu_run() {
     83     local arch elf out err q
     84     arch="$(_bare_canon "$1")"; elf="$2"; out="$3"; err="$4"
     85     q="$(_bare_qemu "$arch")"
     86     case "$arch" in
     87         aa64)
     88             $EXEC_BARE_TIMEOUT "$EXEC_BARE_TO" "$q" -machine virt -cpu cortex-a72 \
     89                 -kernel "$elf" -nographic \
     90                 -semihosting-config enable=on,target=native -no-reboot \
     91                 >"$out" 2>"$err"; RUN_RC_RAW=$? ;;
     92         x64)
     93             $EXEC_BARE_TIMEOUT "$EXEC_BARE_TO" "$q" -kernel "$elf" \
     94                 -device isa-debug-exit,iobase=0x501,iosize=0x02 \
     95                 -display none -serial none -monitor none -no-reboot \
     96                 >"$out" 2>"$err"; RUN_RC_RAW=$? ;;
     97         rv64|rv32)
     98             $EXEC_BARE_TIMEOUT "$EXEC_BARE_TO" "$q" -machine virt -bios none \
     99                 -kernel "$elf" -nographic -no-reboot \
    100                 >"$out" 2>"$err"; RUN_RC_RAW=$? ;;
    101         arm32)
    102             $EXEC_BARE_TIMEOUT "$EXEC_BARE_TO" "$q" -machine mps2-an385 -cpu cortex-m3 \
    103                 -kernel "$elf" -nographic -monitor none \
    104                 -semihosting-config enable=on,target=native -no-reboot \
    105                 >"$out" 2>"$err"; RUN_RC_RAW=$? ;;
    106         *) RUN_RC_RAW=127 ;;
    107     esac
    108 }
    109 
    110 # Decode a raw qemu exit into the guest exit code. Only x64's isa-debug-exit
    111 # needs it (qemu maps a guest write of N to (N<<1)|1); for aa64/rv* qemu already
    112 # returns the guest code, so identity. A non-odd x64 rc (e.g. 124 timeout) is a
    113 # genuine failure and is passed through unchanged.
    114 _bare_decode() {
    115     local r="$2"
    116     case "$(_bare_canon "$1")" in
    117         x64) if [ "$r" -ge 1 ] && [ $((r & 1)) -eq 1 ]; then echo $(((r - 1) / 2)); else echo "$r"; fi ;;
    118         *)   echo "$r" ;;
    119     esac
    120 }
    121 
    122 # ---- ready-image runner (exec_kernel contract) -----------------------------
    123 exec_bare_run_image() {  # arch elf out err  -> sets RUN_RC
    124     local arch; arch="$(_bare_canon "$1")" || { RUN_RC=127; return; }
    125     exec_bare_supported "$arch" || { RUN_RC=127; return; }
    126     _bare_qemu_run "$arch" "$2" "$3" "$4"
    127     RUN_RC="$(_bare_decode "$arch" "$RUN_RC_RAW")"
    128 }
    129 
    130 # ---- corpus runner: setup/link/run -----------------------------------------
    131 exec_bare_setup_build() {  # arch work [entry]  -> build per-arch stub
    132     local arch work entry dir
    133     arch="$(_bare_canon "$1")" || return 1
    134     work="$2"; entry="${3:-${EXEC_BARE_ENTRY:-main}}"
    135     [ -x "$EXEC_BARE_KIT" ] || return 1
    136     dir="$work/_bare/$arch"; mkdir -p "$dir"
    137     rm -f "$dir/.ok"
    138     case "$arch" in
    139         aa64) _bare_emit_aa64 "$dir" || return 1 ;;
    140         x64)  _bare_emit_x64  "$dir" || return 1 ;;
    141         rv64) _bare_emit_rv64 "$dir" || return 1 ;;
    142         rv32) _bare_emit_rv32 "$dir" "$entry" || return 1 ;;
    143         arm32) _bare_emit_arm32 "$dir" "$entry" || return 1 ;;
    144     esac
    145     : > "$dir/.ok"
    146     # Cache the stub dir per arch so a caller may pass a different work dir to
    147     # exec_bare_run for the output image (toy/parse set up once under BUILD_DIR
    148     # but link each case under its own KIT_WORK).
    149     eval "EXEC_BARE_DIR_$arch=\$dir"
    150 }
    151 
    152 exec_bare_setup() {  # arch work [entry]
    153     local arch
    154     arch="$(_bare_canon "$1")" || return 1
    155     exec_bare_supported "$arch" || return 1
    156     exec_bare_setup_build "$arch" "$2" "${3:-${EXEC_BARE_ENTRY:-main}}"
    157 }
    158 
    159 exec_bare_link() {  # arch obj work elf  -> echoes reason; 0 linked / 2 failed
    160     local arch obj work elf dir lderr rt
    161     arch="$(_bare_canon "$1")" || { echo "unknown bare arch '$1'"; return 2; }
    162     obj="$2"; work="$3"; elf="$4"
    163     eval "dir=\"\${EXEC_BARE_DIR_$arch:-$work/_bare/$arch}\""
    164     [ -f "$dir/.ok" ] || { echo "bare runner not set up for $arch"; return 2; }
    165     lderr="$elf.ld.err"
    166     if [ "$arch" = rv32 ]; then
    167         rt="$EXEC_BARE_ROOT/build/rt/riscv32-elf-hardfloat/libkit_rt.a"
    168         if ! "$EXEC_BARE_KIT" ld -T "$dir/link.ld" -e _start \
    169                 "$dir/start.o" "$dir/wrap.o" "$obj" "$rt" -o "$elf" 2>"$lderr"; then
    170             echo "kit ld (rv32) failed: $(head -n1 "$lderr" 2>/dev/null)"; return 2
    171         fi
    172     elif [ "$arch" = arm32 ]; then
    173         # The Cortex-M reset stub IS the image entry (Reset_Handler == _start);
    174         # i64 mul/div/shift and soft-double pull __aeabi_*/__*di3 helpers from the
    175         # arm-eabi-thumb2 runtime variant (built on demand by _bare_emit_arm32).
    176         rt="$EXEC_BARE_ROOT/build/rt/arm-eabi-thumb2/libkit_rt.a"
    177         if [ -f "$rt" ]; then
    178             if ! "$EXEC_BARE_KIT" ld -T "$dir/link.ld" -e _start \
    179                     "$dir/start.o" "$obj" "$rt" -o "$elf" 2>"$lderr"; then
    180                 echo "kit ld (arm32) failed: $(head -n1 "$lderr" 2>/dev/null)"; return 2
    181             fi
    182         else
    183             # No runtime variant present: link the stub + corpus object alone.
    184             # Inline-only i64/double cases still resolve; helper-needing cases
    185             # surface as unresolved-symbol link failures (caller decides).
    186             if ! "$EXEC_BARE_KIT" ld -T "$dir/link.ld" -e _start \
    187                     "$dir/start.o" "$obj" -o "$elf" 2>"$lderr"; then
    188                 echo "kit ld (arm32) failed: $(head -n1 "$lderr" 2>/dev/null)"; return 2
    189             fi
    190         fi
    191     else
    192         if ! "$EXEC_BARE_KIT" ld -T "$dir/link.ld" -e _start \
    193                 "$dir/start.o" "$obj" -o "$elf" 2>"$lderr"; then
    194             echo "kit ld ($arch) failed: $(head -n1 "$lderr" 2>/dev/null)"; return 2
    195         fi
    196     fi
    197     return 0
    198 }
    199 
    200 exec_bare_run() {  # arch obj work rcfile  -> echoes reason; 0 ran / 2 build-fail
    201     local arch obj work rcf elf
    202     arch="$(_bare_canon "$1")" || { echo "unknown bare arch '$1'"; return 2; }
    203     obj="$2"; work="$3"; rcf="$4"
    204     elf="$work/$(basename "$obj").$arch.elf"
    205     exec_bare_link "$arch" "$obj" "$work" "$elf" || return 2
    206     _bare_qemu_run "$arch" "$elf" "$elf.out" "$elf.err"
    207     _bare_decode "$arch" "$RUN_RC_RAW" > "$rcf"
    208     return 0
    209 }
    210 
    211 # ===========================================================================
    212 # Per-arch stub emitters. Each writes start.S + link.ld under <dir> and
    213 # assembles start.o with kit-as. The stub sets up the stack (+ FPU on riscv),
    214 # calls main, and reports main's return through the arch's exit oracle.
    215 # ===========================================================================
    216 
    217 _bare_emit_aa64() {
    218     local dir="$1"
    219     # The CPU comes out of reset at EL1 with the FPU trapping and the MMU off.
    220     # Two things general C needs are therefore set up before calling main:
    221     #   * CPACR_EL1.FPEN = 0b11  — else the first float/double op traps.
    222     #   * an identity MMU map marking RAM as Normal memory — with the MMU off
    223     #     all data accesses are Device-nGnRnE, which enforces natural alignment,
    224     #     so the wide unaligned stores kit emits (e.g. an 8-byte aggregate-init
    225     #     store landing on a 4-byte-aligned slot) take an alignment fault. We
    226     #     have no exception vectors, so a fault spins at the vector base forever
    227     #     (timeout). Normal memory permits the unaligned access, like a real OS.
    228     # Sysregs use the generic S<op0>_<op1>_C<crn>_C<crm>_<op2> spelling (kit-as
    229     # only names a handful by mnemonic).
    230     cat > "$dir/start.S" <<'EOF'
    231 .section .text.start,"ax",@progbits
    232 .globl _start
    233 _start:
    234   adrp x0, stack_top
    235   add  x0, x0, :lo12:stack_top
    236   and  x0, x0, #0xfffffffffffffff0   // AArch64 faults on a misaligned SP; the
    237   mov  sp, x0                        // linker-script .bss lump may not 16-align
    238                                      // stack_top, so force it here.
    239 
    240   movz x0, #0x30, lsl #16            // CPACR_EL1.FPEN = 0b11 (don't trap FP/SIMD)
    241   msr  S3_0_C1_C0_2, x0
    242   isb
    243 
    244   movz x0, #0x00ff                   // MAIR_EL1 attr0 = 0xFF (Normal WB)
    245   msr  S3_0_C10_C2_0, x0
    246   movz x0, #0x3519                   // TCR_EL1: T0SZ=25, 4KB granule, WB/IS
    247   movk x0, #0x0080, lsl #16          //   table walks, EPD1=1 (no TTBR1),
    248   movk x0, #0x0001, lsl #32          //   IPS=36-bit
    249   msr  S3_0_C2_C0_2, x0
    250   adrp x1, l1_table                  // fill the 512-entry L1 table with 1GB
    251   add  x1, x1, :lo12:l1_table        // identity blocks, all Normal memory
    252   mov  x2, #0
    253   movz x3, #0x0701                   // block desc: AF | SH=inner | AttrIdx0 | 01
    254 .Lmap:
    255   lsl  x4, x2, #30                   // output PA = index * 1GB == input VA
    256   orr  x4, x4, x3
    257   str  x4, [x1, x2, lsl #3]
    258   add  x2, x2, #1
    259   cmp  x2, #512
    260   b.lo .Lmap
    261   msr  S3_0_C2_C0_0, x1              // TTBR0_EL1 = l1_table
    262   dsb  sy
    263   isb
    264   mrs  x0, S3_0_C1_C0_0              // SCTLR_EL1: enable MMU + caches
    265   orr  x0, x0, #(1 << 0)             //   M  (MMU)
    266   orr  x0, x0, #(1 << 2)             //   C  (data cache)
    267   orr  x0, x0, #(1 << 12)            //   I  (instruction cache)
    268   movn x5, #(1 << 1)                 //   A=0: allow unaligned on Normal memory
    269   and  x0, x0, x5
    270   msr  S3_0_C1_C0_0, x0
    271   isb
    272 
    273   bl   main
    274   adrp x1, semihost_args
    275   add  x1, x1, :lo12:semihost_args
    276   str  x0, [x1, #8]
    277   mov  x0, #0x20
    278   hlt  #0xf000
    279 .Lhang:
    280   b .Lhang
    281 
    282 .section .data.semihost,"aw",@progbits
    283 .balign 8
    284 semihost_args:
    285   .quad 0x20026
    286   .quad 0
    287 
    288 .section .bss.pgtbl,"aw",@nobits
    289 .balign 4096
    290 l1_table:
    291   .zero 4096
    292 
    293 .section .bss.stack,"aw",@nobits
    294 .balign 16
    295 stack_bottom:
    296   .zero 65536
    297 stack_top:
    298 EOF
    299     cat > "$dir/link.ld" <<'EOF'
    300 ENTRY(_start)
    301 SECTIONS {
    302   . = 0x40080000;
    303   .text      : ALIGN(8)    { *(.text.start) *(.text*) }
    304   .rodata    : ALIGN(8)    { *(.rodata*) }
    305   .data      : ALIGN(8)    { *(.data*) }
    306   .bss.pgtbl : ALIGN(4096) { *(.bss.pgtbl) }   /* L1 table needs 4K alignment */
    307   .bss       : ALIGN(16)   { *(.bss*) *(COMMON) . = ALIGN(., 16); }
    308   /DISCARD/ : { *(.comment) }
    309 }
    310 EOF
    311     "$EXEC_BARE_KIT" as -target aarch64-none-elf \
    312         -o "$dir/start.o" "$dir/start.S" 2>"$dir/as.err"
    313 }
    314 
    315 _bare_emit_rv64() {
    316     local dir="$1"
    317     cat > "$dir/start.S" <<'EOF'
    318 .section .text.start,"ax",@progbits
    319 .globl _start
    320 _start:
    321   li sp, 0x81000000
    322   li t0, 0x2000
    323   csrs mstatus, t0
    324   call main
    325   li t0, 0x100000
    326   beqz a0, .Lpass
    327   slli a0, a0, 16
    328   li t1, 0x3333
    329   or a0, a0, t1
    330   sw a0, 0(t0)
    331 .Lhang:
    332   j .Lhang
    333 .Lpass:
    334   li t1, 0x5555
    335   sw t1, 0(t0)
    336   j .Lhang
    337 EOF
    338     cat > "$dir/link.ld" <<'EOF'
    339 ENTRY(_start)
    340 SECTIONS {
    341   . = 0x80000000;
    342   .text   : { *(.text.start) *(.text*) }
    343   .rodata : { *(.rodata*) }
    344   .data   : { *(.data*) }
    345   .bss    : { *(.bss*) *(COMMON) }
    346   /DISCARD/ : { *(.riscv.attributes) *(.comment) }
    347 }
    348 EOF
    349     "$EXEC_BARE_KIT" as -target riscv64-none-elf \
    350         -march=rv64imafd_zicsr_zifencei -mabi=lp64d \
    351         -o "$dir/start.o" "$dir/start.S" 2>"$dir/as.err"
    352 }
    353 
    354 # rv32 is the corpus-capable arch: the reset stub seeds a static-TLS image and
    355 # enables the FPU (ilp32f); the C wrapper maps the entry's return onto the
    356 # SiFive finisher. The runtime archive (i64 + soft-double helpers) is linked in.
    357 # Ported from the former exec_rv32_bare.sh; see doc/plan/PORT.md.
    358 _bare_emit_rv32() {
    359     local dir="$1" entry="${2:-main}"
    360     local march="rv32imafc_zicsr_zifencei" mabi="ilp32f"
    361     local rt="$EXEC_BARE_ROOT/build/rt/riscv32-elf-hardfloat/libkit_rt.a"
    362     if [ ! -f "$rt" ]; then
    363         make -C "$EXEC_BARE_ROOT" rt-riscv32-elf-hardfloat >/dev/null 2>&1 || true
    364     fi
    365     [ -f "$rt" ] || return 1
    366     cat > "$dir/start.S" <<'EOF'
    367 .section .text.start,"ax",@progbits
    368 .globl _start
    369 _start:
    370   li sp, 0x80100000
    371   li t0, 0x2000
    372   csrs mstatus, t0           # mstatus.FS = Initial (enable the FPU for ilp32f)
    373 
    374   // ---- static thread-local storage setup -----------------------------------
    375   la   t0, __rv32_tls_block  # t0 = block base (TCB at +0)
    376   addi t1, t0, 16            # t1 = dst = block + 16 (.tdata copy start)
    377   la   t2, __rv32_tdata_lma  # t2 = src = .tdata load image
    378   la   t3, __rv32_tdata_size # t3 = .tdata byte count (abs symbol: la yields value)
    379 .Lcopy:
    380   beqz t3, .Lcopy_done
    381   lbu  t4, 0(t2)
    382   sb   t4, 0(t1)
    383   addi t1, t1, 1
    384   addi t2, t2, 1
    385   addi t3, t3, -1
    386   j    .Lcopy
    387 .Lcopy_done:
    388   la   t3, __rv32_tbss_size
    389 .Lzero:
    390   beqz t3, .Lzero_done
    391   sb   zero, 0(t1)
    392   addi t1, t1, 1
    393   addi t3, t3, -1
    394   j    .Lzero
    395 .Lzero_done:
    396   la   t2, __rv32_tdata_lma
    397   sub  tp, t0, t2            # tp = block - __rv32_tdata_lma
    398   // ---------------------------------------------------------------------------
    399 
    400   call _rv32_cmain
    401 .Lhang: j .Lhang
    402 
    403 .section .bss.rv32tls,"aw",@nobits
    404 .balign 16
    405 __rv32_tls_block:
    406   .zero 4096
    407 EOF
    408     cat > "$dir/wrap.c" <<EOF
    409 #define FINISHER ((volatile unsigned int*)0x100000)
    410 extern int ${entry}(void);
    411 __attribute__((noreturn)) void _rv32_cmain(void) {
    412   int code = ${entry}();
    413   *FINISHER = code ? (0x3333u | ((unsigned)code << 16)) : 0x5555u;
    414   for (;;) {}
    415 }
    416 EOF
    417     cat > "$dir/link.ld" <<'EOF'
    418 ENTRY(_start)
    419 SECTIONS {
    420   . = 0x80000000;
    421   .text   : { *(.text.start) *(.text*) }
    422   .rodata : { *(.rodata*) }
    423   .data   : { *(.data*) }
    424   .tdata    : { . = ALIGN(16); __rv32_tdata_lma = .; *(.tdata .tdata.*) }
    425   .tdata_end : { __rv32_tdata_end = .; }
    426   .tbss      : { __rv32_tbss_start = .; *(.tbss .tbss.*) }
    427   .tbss_end  : { __rv32_tbss_end = .; }
    428   .bss    : { *(.bss*) *(COMMON) }
    429   __rv32_tdata_size = __rv32_tdata_end - __rv32_tdata_lma;
    430   __rv32_tbss_size  = __rv32_tbss_end - __rv32_tbss_start;
    431   /DISCARD/ : { *(.riscv.attributes) *(.comment) }
    432 }
    433 EOF
    434     "$EXEC_BARE_KIT" as -target riscv32-none-elf -march="$march" -mabi="$mabi" \
    435         -o "$dir/start.o" "$dir/start.S" 2>"$dir/as.err" || return 1
    436     "$EXEC_BARE_KIT" cc -target riscv32-none-elf -march="$march" -mabi="$mabi" \
    437         -O1 -ffreestanding -c "$dir/wrap.c" -o "$dir/wrap.o" 2>"$dir/wrap.err" || return 1
    438 }
    439 
    440 # arm32 (Cortex-M3, Thumb-2) bare-metal reset stub for qemu-system-arm
    441 # -machine mps2-an385. The M-profile boot contract is a vector table at flash
    442 # address 0x0: word[0] = initial MSP (top of SRAM), word[1] = reset handler.
    443 # The reset handler copies .data flash->SRAM, zeroes .bss, calls the corpus
    444 # entry (default `main`), and reports its return through the ARM semihosting
    445 # SYS_EXIT_EXTENDED oracle (r0=0x20, r1=&{ADP_Stopped_ApplicationExit, code},
    446 # BKPT #0xAB) — qemu's own process exit then == the guest exit code.
    447 #
    448 # The startup stub is assembled by kit-as. Keep it within the descriptor-driven
    449 # Thumb-2 subset: MOVW/MOVT symbol materialization, explicit ADDs instead of
    450 # post-indexed memory, and named local labels.
    451 #
    452 # `Reset_Handler` is the true M-profile reset entry (named in the vector table);
    453 # `_start` is aliased to it so the shared `kit ld -e _start` link path resolves.
    454 _bare_emit_arm32() {
    455     local dir="$1" entry="${2:-main}"
    456     # Build the arm-eabi-thumb2 runtime variant on demand for the i64/double
    457     # helpers (best-effort; exec_bare_link degrades gracefully if absent).
    458     local rt="$EXEC_BARE_ROOT/build/rt/arm-eabi-thumb2/libkit_rt.a"
    459     if [ ! -f "$rt" ]; then
    460         make -C "$EXEC_BARE_ROOT" rt-arm-eabi-thumb2 >/dev/null 2>&1 || true
    461     fi
    462     cat > "$dir/start.S" <<EOF
    463     .syntax unified
    464     .thumb
    465     .cpu cortex-m3
    466 
    467 /* ---- Vector table: must be FIRST, placed at address 0x0 ---- */
    468     .section .vectors, "a"
    469     .balign 4
    470     .global _vectors
    471 _vectors:
    472     .word   _estack          /* word[0]: initial MSP = top of SRAM */
    473     .word   Reset_Handler     /* word[1]: reset handler (Thumb bit auto-set) */
    474 
    475 /* ---- Reset handler (== _start for the shared kit-ld entry) ---- */
    476     .section .text.start, "ax"
    477     .thumb_func
    478     .balign 4
    479     .global Reset_Handler
    480     .global _start
    481     .type Reset_Handler, @function
    482     .type _start, @function
    483 Reset_Handler:
    484 _start:
    485     /* Copy .data from flash LMA (_sidata) to SRAM VMA (_sdata.._edata). */
    486     movw    r0, #:lower16:_sidata
    487     movt    r0, #:upper16:_sidata
    488     movw    r1, #:lower16:_sdata
    489     movt    r1, #:upper16:_sdata
    490     movw    r2, #:lower16:_edata
    491     movt    r2, #:upper16:_edata
    492 .Lcopy_data:
    493     cmp     r1, r2
    494     bcs     .Lcopy_done
    495     ldr     r3, [r0]
    496     str     r3, [r1]
    497     add.w   r0, r0, #4
    498     add.w   r1, r1, #4
    499     b       .Lcopy_data
    500 .Lcopy_done:
    501 
    502     /* Zero .bss (_sbss.._ebss). */
    503     movw    r0, #:lower16:_sbss
    504     movt    r0, #:upper16:_sbss
    505     movw    r1, #:lower16:_ebss
    506     movt    r1, #:upper16:_ebss
    507     movs    r2, #0
    508 .Lzero_bss:
    509     cmp     r0, r1
    510     bcs     .Lzero_done
    511     str     r2, [r0]
    512     add.w   r0, r0, #4
    513     b       .Lzero_bss
    514 .Lzero_done:
    515 
    516     /* Call the corpus entry; its return value lands in r0 == exit code. */
    517     bl      ${entry}
    518     bl      _exit_semihost
    519 .Lhang_after_main:
    520     b       .Lhang_after_main
    521 
    522 /* ---- Semihosting SYS_EXIT_EXTENDED (0x20): r0 = exit code on entry. ---- */
    523     .thumb_func
    524     .balign 4
    525     .global _exit_semihost
    526     .type _exit_semihost, @function
    527 _exit_semihost:
    528     mov     r2, r0           /* save exit code */
    529     sub     sp, sp, #8       /* 2-word parameter block on the stack */
    530     movw    r3, #0x0026
    531     movt    r3, #0x0002      /* ADP_Stopped_ApplicationExit */
    532     str     r3, [sp, #0]     /* block[0] = reason */
    533     str     r2, [sp, #4]     /* block[1] = exit code */
    534     mov     r1, sp           /* r1 -> parameter block */
    535     movs    r0, #0x20        /* SYS_EXIT_EXTENDED */
    536     bkpt    0xAB             /* semihosting call */
    537     add     sp, sp, #8
    538 .Lhang_after_exit:
    539     b       .Lhang_after_exit
    540 EOF
    541     # mps2-an385: code in FLASH @ 0x0 (vector table first), data in SRAM @
    542     # 0x20000000 with the .data init image at a flash LMA (AT> FLASH); the stub
    543     # copies it across at reset. kit ld's linker-script front end supports
    544     # MEMORY / AT> / LOADADDR / ORIGIN / LENGTH; the one unsupported GNU form is
    545     # a *top-level* `sym = expr`, so _estack is assigned inside SECTIONS.
    546     cat > "$dir/link.ld" <<'EOF'
    547 ENTRY(_start)
    548 MEMORY {
    549   FLASH (rx)  : ORIGIN = 0x00000000, LENGTH = 4M
    550   SRAM  (rwx) : ORIGIN = 0x20000000, LENGTH = 4M
    551 }
    552 SECTIONS {
    553   .text : {
    554     *(.vectors)
    555     *(.text.start)
    556     *(.text*)
    557     *(.rodata*)
    558     . = ALIGN(4);
    559   } > FLASH
    560   _sidata = LOADADDR(.data);
    561   .data : ALIGN(4) {
    562     _sdata = .;
    563     *(.data*)
    564     /* Single-thread TLS-as-static: __thread initialized data is a plain static
    565      * instance, seeded from flash by the reset stub like .data. */
    566     *(.tdata*)
    567     . = ALIGN(4);
    568     _edata = .;
    569   } > SRAM AT> FLASH
    570   .bss (NOLOAD) : ALIGN(4) {
    571     _sbss = .;
    572     *(.bss*)
    573     /* Single-thread TLS-as-static: __thread zero data folds into .bss. */
    574     *(.tbss*)
    575     *(COMMON)
    576     . = ALIGN(4);
    577     _ebss = .;
    578   } > SRAM
    579   _estack = ORIGIN(SRAM) + LENGTH(SRAM);
    580   /DISCARD/ : { *(.ARM.exidx*) *(.ARM.extab*) *(.ARM.attributes)
    581                 *(.comment) *(.note*) }
    582 }
    583 EOF
    584     "$EXEC_BARE_KIT" as -target arm-none-eabi \
    585         -o "$dir/start.o" "$dir/start.S" 2>"$dir/as.err" || return 1
    586 }
    587 
    588 # x86_64 long-mode reset: a multiboot header, a 32-bit entry that sets up
    589 # identity-mapped paging + long mode, then calls main and reports its return via
    590 # isa-debug-exit (port 0x501). Ported from freestanding_system.sh.
    591 _bare_emit_x64() {
    592     local dir="$1" out="$dir/start.S" i
    593     cat > "$out" <<'EOF'
    594 .section .multiboot,"a",@progbits
    595 .balign 4
    596 .long 0x1badb002
    597 .long 0
    598 .long 0xe4524ffe
    599 
    600 .section .note.Xen,"a",@note
    601 .balign 4
    602 .long 4
    603 .long 4
    604 .long 18
    605 .ascii "Xen"
    606 .byte 0
    607 .long _start
    608 
    609 .section .text.start,"ax",@progbits
    610 .globl _start
    611 _start:
    612   .byte 0xfa                         /* cli */
    613   .byte 0xbc                         /* mov $stack_top, %esp */
    614   .long stack_top
    615   .byte 0xb8                         /* mov $pml4, %eax */
    616   .long pml4
    617   .byte 0x0f, 0x22, 0xd8             /* mov %eax, %cr3 */
    618   .byte 0x0f, 0x20, 0xe0             /* mov %cr4, %eax */
    619   .byte 0x83, 0xc8, 0x20             /* or $CR4_PAE, %eax */
    620   .byte 0x0f, 0x22, 0xe0             /* mov %eax, %cr4 */
    621   .byte 0xb9, 0x80, 0x00, 0x00, 0xc0 /* mov $EFER, %ecx */
    622   .byte 0x0f, 0x32                   /* rdmsr */
    623   .byte 0x0d, 0x00, 0x01, 0x00, 0x00 /* or $EFER_LME, %eax */
    624   .byte 0x0f, 0x30                   /* wrmsr */
    625   .byte 0x0f, 0x20, 0xc0             /* mov %cr0, %eax */
    626   .byte 0x0d, 0x00, 0x00, 0x00, 0x80 /* or $CR0_PG, %eax */
    627   .byte 0x0f, 0x22, 0xc0             /* mov %eax, %cr0 */
    628   .byte 0x0f, 0x01, 0x15             /* lgdt gdt_desc */
    629   .long gdt_desc
    630   .byte 0xea                         /* ljmp $0x08,$long_entry */
    631   .long long_entry
    632   .hword 0x08
    633 
    634 long_entry:
    635   .byte 0x66, 0xb8, 0x10, 0x00       /* mov $0x10, %ax */
    636   .byte 0x8e, 0xd8                   /* mov %ax, %ds */
    637   .byte 0x8e, 0xc0                   /* mov %ax, %es */
    638   .byte 0x8e, 0xd0                   /* mov %ax, %ss */
    639   .byte 0x48, 0xbc                   /* movabs $stack_top, %rsp */
    640   .quad stack_top
    641   call main
    642   .byte 0x66, 0xba, 0x01, 0x05       /* mov $0x501, %dx */
    643   .byte 0x66, 0xef                   /* outw %ax, %dx */
    644 .Lhang:
    645   .byte 0xf4                         /* hlt */
    646   jmp .Lhang
    647 
    648 .section .data.boot,"aw",@progbits
    649 .balign 8
    650 gdt:
    651   .quad 0
    652   .quad 0x00af9a000000ffff
    653   .quad 0x00af92000000ffff
    654 gdt_desc:
    655   .hword 23
    656   .long gdt
    657 
    658 .balign 4096
    659 pml4:
    660   .quad pdpt + 0x3
    661 EOF
    662     for _ in $(seq 1 511); do printf '  .quad 0\n' >> "$out"; done
    663     cat >> "$out" <<'EOF'
    664 .balign 4096
    665 pdpt:
    666   .quad pd + 0x3
    667 EOF
    668     for _ in $(seq 1 511); do printf '  .quad 0\n' >> "$out"; done
    669     printf '.balign 4096\npd:\n' >> "$out"
    670     i=0
    671     while [ "$i" -lt 512 ]; do
    672         printf '  .quad 0x%016x\n' $((i * 0x200000 + 0x83)) >> "$out"
    673         i=$((i + 1))
    674     done
    675     cat >> "$out" <<'EOF'
    676 
    677 .section .bss.stack,"aw",@nobits
    678 .balign 16
    679 stack_bottom:
    680   .zero 65536
    681 stack_top:
    682 EOF
    683     cat > "$dir/link.ld" <<'EOF'
    684 ENTRY(_start)
    685 SECTIONS {
    686   . = 0x100000;
    687   .multiboot : { *(.multiboot) }
    688   .note.Xen  : { *(.note.Xen) }
    689   .text      : { *(.text.start) *(.text*) }
    690   .rodata    : { *(.rodata*) }
    691   .data.boot : ALIGN(4096) { *(.data.boot) }
    692   .data      : { *(.data*) }
    693   .bss.stack : ALIGN(16) { *(.bss.stack) }
    694   .bss       : { *(.bss*) *(COMMON) }
    695   /DISCARD/  : { *(.comment) }
    696 }
    697 EOF
    698     "$EXEC_BARE_KIT" as -target x86_64-none-elf \
    699         -o "$dir/start.o" "$out" 2>"$dir/as.err"
    700 }