crt_arch.h (1531B)
1 /* tcc-build aarch64 crt_arch.h replacement. 2 * 3 * Three things stock musl does that we can't: 4 * 5 * 1. `adrp x1, _DYNAMIC` + `add x1, x1, #:lo12:_DYNAMIC` to pass 6 * _DYNAMIC's address. arm64-asm.c phase 1+2 has neither the 7 * `adrp` mnemonic nor the `:lo12:` ELF reloc syntax, and boot4 8 * only links static binaries (no _DYNAMIC) anyway. _start_c 9 * ignores its second argument; just don't pass it. 10 * 11 * 2. `and sp, x0, #-16` to align the stack. Phase 2's `bic` rejects 12 * the bitmask-immediate form ("invert form requires a register"). 13 * Linux/AAPCS already guarantees a 16-byte-aligned sp at process 14 * entry, so the realignment is defensive only. 15 * 16 * 3. `mov x29, #0` / `mov x30, #0` / `mov x1, #0` — phase 1 emits 17 * these as the 32-bit MOVZ form (sf=0), leaving the upper 32 18 * bits of the X register at their entry value. Subsequent code 19 * treats x29 as a frame-pointer chain anchor; non-zero high bits 20 * crash on the first `stp x29, x30, [...]`. The Linux kernel 21 * zeroes all GPRs except sp at process entry (see 22 * `start_thread` in arch/arm64/kernel/process.c), so omitting 23 * the explicit zeroing is safe — when phase 3 lands, restore 24 * the zeroing for defense-in-depth. 25 * 26 * What's left is the minimum kernel-to-userland glue: pass sp to 27 * _start_c so it can read argc/argv/envp/auxv, then jump. 28 */ 29 __asm__( 30 ".text \n" 31 ".global " START "\n" 32 ".type " START ",%function\n" 33 START ":\n" 34 " mov x0, sp\n" 35 " b " START "_c\n" 36 );