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:
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,