-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch '__jited-test-tag-to-check-disassembly-after-jit'
Eduard Zingerman says: ==================== __jited test tag to check disassembly after jit Some of the logic in the BPF jits might be non-trivial. It might be useful to allow testing this logic by comparing generated native code with expected code template. This patch set adds a macro __jited() that could be used for test_loader based tests in a following manner: SEC("tp") __arch_x86_64 __jited(" endbr64") __jited(" nopl (%rax,%rax)") __jited(" xorq %rax, %rax") ... __naked void some_test(void) { ... } Also add a test for jit code generated for tail calls handling to demonstrate the feature. The feature uses LLVM libraries to do the disassembly. At selftests compilation time Makefile detects if these libraries are available. When libraries are not available tests using __jit_x86() are skipped. Current CI environment does not include llvm development libraries, but changes to add these are trivial. This was previously discussed here: https://lore.kernel.org/bpf/[email protected]/ Patch-set includes a few auxiliary steps: - patches #2 and #3 fix a few bugs in test_loader behaviour; - patch #4 replaces __regex macro with ability to specify regular expressions in __msg and __xlated using "{{" "}}" escapes; - patch #8 updates __xlated to match disassembly lines consequently, same way as __jited does. Changes v2->v3: - changed macro name from __jit_x86 to __jited with __arch_* to specify disassembly arch (Yonghong); - __jited matches disassembly lines consequently with "..." allowing to skip some number of lines (Andrii); - __xlated matches disassembly lines consequently, same as __jited; - "{{...}}" regex brackets instead of __regex macro; - bug fixes for old commits. Changes v1->v2: - stylistic changes suggested by Yonghong; - fix for -Wformat-truncation related warning when compiled with llvm15 (Yonghong). v1: https://lore.kernel.org/bpf/[email protected]/ v2: https://lore.kernel.org/bpf/[email protected]/ ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
- Loading branch information
Showing
13 changed files
with
772 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) | ||
#include <bpf/bpf.h> | ||
#include <bpf/libbpf.h> | ||
#include <test_progs.h> | ||
|
||
#ifdef HAVE_LLVM_SUPPORT | ||
|
||
#include <llvm-c/Core.h> | ||
#include <llvm-c/Disassembler.h> | ||
#include <llvm-c/Target.h> | ||
#include <llvm-c/TargetMachine.h> | ||
|
||
/* The intent is to use get_jited_program_text() for small test | ||
* programs written in BPF assembly, thus assume that 32 local labels | ||
* would be sufficient. | ||
*/ | ||
#define MAX_LOCAL_LABELS 32 | ||
|
||
static bool llvm_initialized; | ||
|
||
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; | ||
} | ||
|
||
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); | ||
|
||
/* now print with labels */ | ||
labels.print_phase = true; | ||
pc = 0; | ||
while (pc < len) { | ||
cnt = disasm_insn(ctx, image, len, pc, buf, sizeof(buf)); | ||
if (cnt < 0) { | ||
err = cnt; | ||
goto out; | ||
} | ||
label_pc = bsearch(&pc, labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32); | ||
label = ""; | ||
colon = ""; | ||
if (label_pc) { | ||
label = labels.names[label_pc - labels.pcs]; | ||
colon = ":"; | ||
} | ||
fprintf(text_out, "%x:\t", pc); | ||
for (i = 0; i < cnt; ++i) | ||
fprintf(text_out, "%02x ", image[pc + i]); | ||
for (i = cnt * 3; i < 12 * 3; ++i) | ||
fputc(' ', text_out); | ||
fprintf(text_out, "%s%s%s\n", label, colon, buf); | ||
pc += cnt; | ||
} | ||
|
||
out: | ||
if (triple) | ||
LLVMDisposeMessage(triple); | ||
if (ctx) | ||
LLVMDisasmDispose(ctx); | ||
return err; | ||
} | ||
|
||
int get_jited_program_text(int fd, char *text, size_t text_sz) | ||
{ | ||
struct bpf_prog_info info = {}; | ||
__u32 info_len = sizeof(info); | ||
__u32 jited_funcs, len, pc; | ||
__u32 *func_lens = NULL; | ||
FILE *text_out = NULL; | ||
uint8_t *image = NULL; | ||
int i, err = 0; | ||
|
||
if (!llvm_initialized) { | ||
LLVMInitializeAllTargetInfos(); | ||
LLVMInitializeAllTargetMCs(); | ||
LLVMInitializeAllDisassemblers(); | ||
llvm_initialized = 1; | ||
} | ||
|
||
text_out = fmemopen(text, text_sz, "w"); | ||
if (!ASSERT_OK_PTR(text_out, "open_memstream")) { | ||
err = -errno; | ||
goto out; | ||
} | ||
|
||
/* first call is to find out jited program len */ | ||
err = bpf_prog_get_info_by_fd(fd, &info, &info_len); | ||
if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #1")) | ||
goto out; | ||
|
||
len = info.jited_prog_len; | ||
image = malloc(len); | ||
if (!ASSERT_OK_PTR(image, "malloc(info.jited_prog_len)")) { | ||
err = -ENOMEM; | ||
goto out; | ||
} | ||
|
||
jited_funcs = info.nr_jited_func_lens; | ||
func_lens = malloc(jited_funcs * sizeof(__u32)); | ||
if (!ASSERT_OK_PTR(func_lens, "malloc(info.nr_jited_func_lens)")) { | ||
err = -ENOMEM; | ||
goto out; | ||
} | ||
|
||
memset(&info, 0, sizeof(info)); | ||
info.jited_prog_insns = (__u64)image; | ||
info.jited_prog_len = len; | ||
info.jited_func_lens = (__u64)func_lens; | ||
info.nr_jited_func_lens = jited_funcs; | ||
err = bpf_prog_get_info_by_fd(fd, &info, &info_len); | ||
if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #2")) | ||
goto out; | ||
|
||
for (pc = 0, i = 0; i < jited_funcs; ++i) { | ||
fprintf(text_out, "func #%d:\n", i); | ||
disasm_one_func(text_out, image + pc, func_lens[i]); | ||
fprintf(text_out, "\n"); | ||
pc += func_lens[i]; | ||
} | ||
|
||
out: | ||
if (text_out) | ||
fclose(text_out); | ||
if (image) | ||
free(image); | ||
if (func_lens) | ||
free(func_lens); | ||
return err; | ||
} | ||
|
||
#else /* HAVE_LLVM_SUPPORT */ | ||
|
||
int get_jited_program_text(int fd, char *text, size_t text_sz) | ||
{ | ||
if (env.verbosity >= VERBOSE_VERY) | ||
printf("compiled w/o llvm development libraries, can't dis-assembly binary code"); | ||
return -EOPNOTSUPP; | ||
} | ||
|
||
#endif /* HAVE_LLVM_SUPPORT */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ | ||
|
||
#ifndef __JIT_DISASM_HELPERS_H | ||
#define __JIT_DISASM_HELPERS_H | ||
|
||
#include <stddef.h> | ||
|
||
int get_jited_program_text(int fd, char *text, size_t text_sz); | ||
|
||
#endif /* __JIT_DISASM_HELPERS_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.