kit

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

commit 2a857c40bd4dabad0c15b3685b82c1f168e365ef
parent 00369dc5e19be75c9a467c98adf499cda78edb6d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon,  1 Jun 2026 17:14:26 -0700

api/asm_emit: symbolize PC-relative local branches at section offset 0

A local branch whose target the printer rendered as a "#<dec>" PC-relative
displacement (rather than "0x<hex>") lacks address context, which happens when
the branch is the first instruction of .text -- as -O1 leaf functions can
produce. parse_branch_tail resolves both forms to an absolute section-relative
target so the branch still gets a label.

Diffstat:
Msrc/api/asm_emit.c | 39+++++++++++++++++++++++++++++++++++++--
1 file changed, 37 insertions(+), 2 deletions(-)

diff --git a/src/api/asm_emit.c b/src/api/asm_emit.c @@ -801,6 +801,41 @@ static int parse_hex_tail(CfreeSlice ops, u64* out) { return 1; } +/* Parse a local-branch target from the operand tail into an absolute, + * section-relative offset. Two render forms occur: + * "0x<hex>" — an absolute target (the printer had the instruction's address) + * "#<dec>" — a PC-relative displacement, emitted when the printer had no + * address context, i.e. the instruction sits at section offset 0 + * (a branch as the first instruction of .text, which -O1 leaf + * functions can produce). The absolute target is inst_off + disp. + * Returns 1 and sets *out to the absolute target. */ +static int parse_branch_tail(CfreeSlice ops, u32 inst_off, u64* out) { + i32 start = 0, p; + i64 v = 0; + int neg = 0, any = 0; + u32 i; + if (parse_hex_tail(ops, out)) return 1; + for (i = 0; i < ops.len; ++i) + if (ops.s[i] == ',') start = (i32)i + 1; + while (start < (i32)ops.len && (ops.s[start] == ' ' || ops.s[start] == '\t')) + ++start; + if (start >= (i32)ops.len || ops.s[start] != '#') return 0; + p = start + 1; + if (p < (i32)ops.len && (ops.s[p] == '-' || ops.s[p] == '+')) + neg = (ops.s[p++] == '-'); + for (; p < (i32)ops.len; ++p) { + char c = ops.s[p]; + if (c < '0' || c > '9') break; + v = v * 10 + (c - '0'); + any = 1; + } + while (p < (i32)ops.len && ops.s[p] == ' ') ++p; + if (!any || p != (i32)ops.len) return 0; + if (neg) v = -v; + *out = (u64)((i64)inst_off + v); + return 1; +} + typedef struct { Compiler* c; u32 secidx; @@ -948,7 +983,7 @@ static u32* collect_branch_targets(Compiler* c, ArchDisasm* dasm, } if (!reloc_in_range(relocs, nrelocs, off, nb) && arch_is_local_branch(c, insn.mnemonic) && - parse_hex_tail(insn.operands, &tgt) && tgt < total) { + parse_branch_tail(insn.operands, off, &tgt) && tgt < total) { anchor_add(c, &arr, &n, &cap, (u32)tgt); } off += nb; @@ -997,7 +1032,7 @@ static CfreeStatus emit_operands(Writer* w, const EmitCtx* x, } } else if (arch_is_local_branch(x->c, insn->mnemonic)) { u64 tgt; - if (parse_hex_tail(insn->operands, &tgt) && is_btarget(x, (u32)tgt)) { + if (parse_branch_tail(insn->operands, off, &tgt) && is_btarget(x, (u32)tgt)) { char name[256]; build_label_name(name, sizeof name, x, (u32)tgt); return w_symbolized(w, insn->operands.s, insn->operands.len, name,