On Tue, Aug 20, 2024 at 3:24 AM Eduard Zingerman <eddyz87@xxxxxxxxx> wrote: > + > +struct local_labels { > + bool print_phase; > + __u32 prog_len; > + __u32 cnt; > + __u32 pcs[MAX_LOCAL_LABELS]; > + char names[MAX_LOCAL_LABELS][4]; > +}; > + > +static const char *lookup_symbol(void *data, uint64_t ref_value, uint64_t *ref_type, > + uint64_t ref_pc, const char **ref_name) > +{ > + struct local_labels *labels = data; > + uint64_t type = *ref_type; > + int i; > + > + *ref_type = LLVMDisassembler_ReferenceType_InOut_None; > + *ref_name = NULL; > + if (type != LLVMDisassembler_ReferenceType_In_Branch) > + return NULL; > + /* Depending on labels->print_phase either discover local labels or > + * return a name assigned with local jump target: > + * - if print_phase is true and ref_value is in labels->pcs, > + * return corresponding labels->name. > + * - if print_phase is false, save program-local jump targets > + * in labels->pcs; > + */ > + if (labels->print_phase) { > + for (i = 0; i < labels->cnt; ++i) > + if (labels->pcs[i] == ref_value) > + return labels->names[i]; > + } else { > + if (labels->cnt < MAX_LOCAL_LABELS && ref_value < labels->prog_len) > + labels->pcs[labels->cnt++] = ref_value; > + } > + return NULL; > +} bpftool should probably adopt similar logic just to be consistent? > + > +static int disasm_insn(LLVMDisasmContextRef ctx, uint8_t *image, __u32 len, __u32 pc, > + char *buf, __u32 buf_sz) > +{ > + int i, cnt; > + > + cnt = LLVMDisasmInstruction(ctx, image + pc, len - pc, pc, > + buf, buf_sz); > + if (cnt > 0) > + return cnt; > + PRINT_FAIL("Can't disasm instruction at offset %d:", pc); > + for (i = 0; i < 16 && pc + i < len; ++i) > + printf(" %02x", image[pc + i]); > + printf("\n"); > + return -EINVAL; > +} > + > +static int cmp_u32(const void *_a, const void *_b) > +{ > + __u32 a = *(__u32 *)_a; > + __u32 b = *(__u32 *)_b; > + > + if (a < b) > + return -1; > + if (a > b) > + return 1; > + return 0; > +} > + > +static int disasm_one_func(FILE *text_out, uint8_t *image, __u32 len) > +{ > + char *label, *colon, *triple = NULL; > + LLVMDisasmContextRef ctx = NULL; > + struct local_labels labels = {}; > + __u32 *label_pc, pc; > + int i, cnt, err = 0; > + char buf[64]; > + > + triple = LLVMGetDefaultTargetTriple(); > + ctx = LLVMCreateDisasm(triple, &labels, 0, NULL, lookup_symbol); > + if (!ASSERT_OK_PTR(ctx, "LLVMCreateDisasm")) { > + err = -EINVAL; > + goto out; > + } > + > + cnt = LLVMSetDisasmOptions(ctx, LLVMDisassembler_Option_PrintImmHex); > + if (!ASSERT_EQ(cnt, 1, "LLVMSetDisasmOptions")) { > + err = -EINVAL; > + goto out; > + } > + > + /* discover labels */ > + labels.prog_len = len; > + pc = 0; > + while (pc < len) { > + cnt = disasm_insn(ctx, image, len, pc, buf, 1); > + if (cnt < 0) { > + err = cnt; > + goto out; > + } > + pc += cnt; > + } > + qsort(labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32); > + for (i = 0; i < labels.cnt; ++i) > + /* use (i % 100) to avoid format truncation warning */ > + snprintf(labels.names[i], sizeof(labels.names[i]), "L%d", i % 100); 100 here and names[..][4] are a bit of magic. Pls add some #define and comments to clarify in the follow up. Overall it looks to be a great improvement to selftests. Applied. Pls add necessary packages to bpf CI.