// 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
/* Local labels are encoded as 'L42', this requires 4 bytes of storage:
* 3 characters + zero byte
*/
#define LOCAL_LABEL_LEN 4
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][LOCAL_LABEL_LEN];
};
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)
/* gcc is unable to infer upper bound for labels.cnt and assumes
* it to be U32_MAX . U32_MAX takes 10 decimal digits .
* snprintf below prints into labels . names [ * ] ,
* which has space only for two digits and a letter .
* To avoid truncation warning use ( i % MAX_LOCAL_LABELS ) ,
* which informs gcc about printed value upper bound .
*/
snprintf(labels.names[i], sizeof (labels.names[i]), "L%d" , i % MAX_LOCAL_LABELS);
/* 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 */
Messung V0.5 in Prozent C=96 H=66 G=82
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-10-12)
¤
*© Formatika GbR, Deutschland