kit

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

commit 5fced6f7c4c5295dfbfa8259fb79575e081f26f5
parent 8ace7b4efb4478f3c7a24fd0a4bccf7ab89cc54a
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 06:09:53 -0700

arm32 as: BL/B.W/Bcc.W placeholders must match codegen's encoders

The standalone assembler emitted ad-hoc branch placeholders (0xf000d000 for BL,
0xf0009000 for B.W, 0xf0008000|cond for Bcc.W). The THM_CALL/JUMP24/JUMP19
relocations are REL and the patcher preserves the field's J1/J2 (the ~(^S)
inversion), so a placeholder whose J1/J2 imply a nonzero in-field offset skews
the applied target — a +0xC00000 error that sent every kit-assembled
cross-object `bl SYM` to garbage (e.g. the AEABI compare helpers' `bl __ltdf2`).
This is the first kit-assembled code with cross-object branches to be linked and
run (the bare-metal reset stub is clang-assembled), so it had never surfaced.
Emit arm_bl()/arm_b_w()/arm_b_cond_w() — the exact placeholders codegen uses (and
the linker round-trips). test-isa/test-asm green; AEABI dcmp/fcmp now run
correctly under qemu.

Diffstat:
Msrc/arch/arm32/asm.c | 14+++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/arch/arm32/asm.c b/src/arch/arm32/asm.c @@ -533,7 +533,11 @@ static void assemble_one(AsmDriver* d, const Arm32InsnDesc* desc, u32 cond) { return; } case ARM_FMT_BL: { - emit_t32(d, 0xf000d000u); /* placeholder */ + /* Placeholder MUST match codegen's arm_bl(): the THM_CALL reloc is REL and + * the patcher preserves the field's J1/J2 (XOR-S) base, so an ad-hoc + * placeholder whose J1/J2 imply a nonzero in-field offset corrupts the + * applied target (a +0xC00000 skew on cross-object calls). */ + emit_t32(d, arm_bl()); /* placeholder (== codegen) */ /* reloc rides the BL we just emitted (offset = its start). */ { MCEmitter* mc = asm_driver_mc(d); @@ -547,7 +551,7 @@ static void assemble_one(AsmDriver* d, const Arm32InsnDesc* desc, u32 cond) { return; } case ARM_FMT_BRANCH_T4: { - emit_t32(d, 0xf0009000u); + emit_t32(d, arm_b_w()); { MCEmitter* mc = asm_driver_mc(d); ObjSymId sym = OBJ_SYM_NONE; @@ -560,7 +564,7 @@ static void assemble_one(AsmDriver* d, const Arm32InsnDesc* desc, u32 cond) { return; } case ARM_FMT_BRANCH_T3: { - emit_t32(d, 0xf0008000u | (cond << 22)); + emit_t32(d, arm_b_cond_w(cond)); { MCEmitter* mc = asm_driver_mc(d); ObjSymId sym = OBJ_SYM_NONE; @@ -577,7 +581,7 @@ static void assemble_one(AsmDriver* d, const Arm32InsnDesc* desc, u32 cond) { /* 16-bit branches: emit the 32-bit wide form so the relocation has the * full range (the disassembler still round-trips the wide encoding). */ if (cond == ARM_CC_AL) { - emit_t32(d, 0xf0009000u); + emit_t32(d, arm_b_w()); { MCEmitter* mc = asm_driver_mc(d); ObjSymId sym = OBJ_SYM_NONE; @@ -588,7 +592,7 @@ static void assemble_one(AsmDriver* d, const Arm32InsnDesc* desc, u32 cond) { 0, 0); } } else { - emit_t32(d, 0xf0008000u | (cond << 22)); + emit_t32(d, arm_b_cond_w(cond)); { MCEmitter* mc = asm_driver_mc(d); ObjSymId sym = OBJ_SYM_NONE;