Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/bpf/jit_disasm_helpers.c
26285 views
1
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2
#include <bpf/bpf.h>
3
#include <bpf/libbpf.h>
4
#include <test_progs.h>
5
6
#ifdef HAVE_LLVM_SUPPORT
7
8
#include <llvm-c/Core.h>
9
#include <llvm-c/Disassembler.h>
10
#include <llvm-c/Target.h>
11
#include <llvm-c/TargetMachine.h>
12
13
/* The intent is to use get_jited_program_text() for small test
14
* programs written in BPF assembly, thus assume that 32 local labels
15
* would be sufficient.
16
*/
17
#define MAX_LOCAL_LABELS 32
18
19
/* Local labels are encoded as 'L42', this requires 4 bytes of storage:
20
* 3 characters + zero byte
21
*/
22
#define LOCAL_LABEL_LEN 4
23
24
static bool llvm_initialized;
25
26
struct local_labels {
27
bool print_phase;
28
__u32 prog_len;
29
__u32 cnt;
30
__u32 pcs[MAX_LOCAL_LABELS];
31
char names[MAX_LOCAL_LABELS][LOCAL_LABEL_LEN];
32
};
33
34
static const char *lookup_symbol(void *data, uint64_t ref_value, uint64_t *ref_type,
35
uint64_t ref_pc, const char **ref_name)
36
{
37
struct local_labels *labels = data;
38
uint64_t type = *ref_type;
39
int i;
40
41
*ref_type = LLVMDisassembler_ReferenceType_InOut_None;
42
*ref_name = NULL;
43
if (type != LLVMDisassembler_ReferenceType_In_Branch)
44
return NULL;
45
/* Depending on labels->print_phase either discover local labels or
46
* return a name assigned with local jump target:
47
* - if print_phase is true and ref_value is in labels->pcs,
48
* return corresponding labels->name.
49
* - if print_phase is false, save program-local jump targets
50
* in labels->pcs;
51
*/
52
if (labels->print_phase) {
53
for (i = 0; i < labels->cnt; ++i)
54
if (labels->pcs[i] == ref_value)
55
return labels->names[i];
56
} else {
57
if (labels->cnt < MAX_LOCAL_LABELS && ref_value < labels->prog_len)
58
labels->pcs[labels->cnt++] = ref_value;
59
}
60
return NULL;
61
}
62
63
static int disasm_insn(LLVMDisasmContextRef ctx, uint8_t *image, __u32 len, __u32 pc,
64
char *buf, __u32 buf_sz)
65
{
66
int i, cnt;
67
68
cnt = LLVMDisasmInstruction(ctx, image + pc, len - pc, pc,
69
buf, buf_sz);
70
if (cnt > 0)
71
return cnt;
72
PRINT_FAIL("Can't disasm instruction at offset %d:", pc);
73
for (i = 0; i < 16 && pc + i < len; ++i)
74
printf(" %02x", image[pc + i]);
75
printf("\n");
76
return -EINVAL;
77
}
78
79
static int cmp_u32(const void *_a, const void *_b)
80
{
81
__u32 a = *(__u32 *)_a;
82
__u32 b = *(__u32 *)_b;
83
84
if (a < b)
85
return -1;
86
if (a > b)
87
return 1;
88
return 0;
89
}
90
91
static int disasm_one_func(FILE *text_out, uint8_t *image, __u32 len)
92
{
93
char *label, *colon, *triple = NULL;
94
LLVMDisasmContextRef ctx = NULL;
95
struct local_labels labels = {};
96
__u32 *label_pc, pc;
97
int i, cnt, err = 0;
98
char buf[64];
99
100
triple = LLVMGetDefaultTargetTriple();
101
ctx = LLVMCreateDisasm(triple, &labels, 0, NULL, lookup_symbol);
102
if (!ASSERT_OK_PTR(ctx, "LLVMCreateDisasm")) {
103
err = -EINVAL;
104
goto out;
105
}
106
107
cnt = LLVMSetDisasmOptions(ctx, LLVMDisassembler_Option_PrintImmHex);
108
if (!ASSERT_EQ(cnt, 1, "LLVMSetDisasmOptions")) {
109
err = -EINVAL;
110
goto out;
111
}
112
113
/* discover labels */
114
labels.prog_len = len;
115
pc = 0;
116
while (pc < len) {
117
cnt = disasm_insn(ctx, image, len, pc, buf, 1);
118
if (cnt < 0) {
119
err = cnt;
120
goto out;
121
}
122
pc += cnt;
123
}
124
qsort(labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);
125
for (i = 0; i < labels.cnt; ++i)
126
/* gcc is unable to infer upper bound for labels.cnt and assumes
127
* it to be U32_MAX. U32_MAX takes 10 decimal digits.
128
* snprintf below prints into labels.names[*],
129
* which has space only for two digits and a letter.
130
* To avoid truncation warning use (i % MAX_LOCAL_LABELS),
131
* which informs gcc about printed value upper bound.
132
*/
133
snprintf(labels.names[i], sizeof(labels.names[i]), "L%d", i % MAX_LOCAL_LABELS);
134
135
/* now print with labels */
136
labels.print_phase = true;
137
pc = 0;
138
while (pc < len) {
139
cnt = disasm_insn(ctx, image, len, pc, buf, sizeof(buf));
140
if (cnt < 0) {
141
err = cnt;
142
goto out;
143
}
144
label_pc = bsearch(&pc, labels.pcs, labels.cnt, sizeof(*labels.pcs), cmp_u32);
145
label = "";
146
colon = "";
147
if (label_pc) {
148
label = labels.names[label_pc - labels.pcs];
149
colon = ":";
150
}
151
fprintf(text_out, "%x:\t", pc);
152
for (i = 0; i < cnt; ++i)
153
fprintf(text_out, "%02x ", image[pc + i]);
154
for (i = cnt * 3; i < 12 * 3; ++i)
155
fputc(' ', text_out);
156
fprintf(text_out, "%s%s%s\n", label, colon, buf);
157
pc += cnt;
158
}
159
160
out:
161
if (triple)
162
LLVMDisposeMessage(triple);
163
if (ctx)
164
LLVMDisasmDispose(ctx);
165
return err;
166
}
167
168
int get_jited_program_text(int fd, char *text, size_t text_sz)
169
{
170
struct bpf_prog_info info = {};
171
__u32 info_len = sizeof(info);
172
__u32 jited_funcs, len, pc;
173
__u32 *func_lens = NULL;
174
FILE *text_out = NULL;
175
uint8_t *image = NULL;
176
int i, err = 0;
177
178
if (!llvm_initialized) {
179
LLVMInitializeAllTargetInfos();
180
LLVMInitializeAllTargetMCs();
181
LLVMInitializeAllDisassemblers();
182
llvm_initialized = 1;
183
}
184
185
text_out = fmemopen(text, text_sz, "w");
186
if (!ASSERT_OK_PTR(text_out, "open_memstream")) {
187
err = -errno;
188
goto out;
189
}
190
191
/* first call is to find out jited program len */
192
err = bpf_prog_get_info_by_fd(fd, &info, &info_len);
193
if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #1"))
194
goto out;
195
196
len = info.jited_prog_len;
197
image = malloc(len);
198
if (!ASSERT_OK_PTR(image, "malloc(info.jited_prog_len)")) {
199
err = -ENOMEM;
200
goto out;
201
}
202
203
jited_funcs = info.nr_jited_func_lens;
204
func_lens = malloc(jited_funcs * sizeof(__u32));
205
if (!ASSERT_OK_PTR(func_lens, "malloc(info.nr_jited_func_lens)")) {
206
err = -ENOMEM;
207
goto out;
208
}
209
210
memset(&info, 0, sizeof(info));
211
info.jited_prog_insns = (__u64)image;
212
info.jited_prog_len = len;
213
info.jited_func_lens = (__u64)func_lens;
214
info.nr_jited_func_lens = jited_funcs;
215
err = bpf_prog_get_info_by_fd(fd, &info, &info_len);
216
if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #2"))
217
goto out;
218
219
for (pc = 0, i = 0; i < jited_funcs; ++i) {
220
fprintf(text_out, "func #%d:\n", i);
221
disasm_one_func(text_out, image + pc, func_lens[i]);
222
fprintf(text_out, "\n");
223
pc += func_lens[i];
224
}
225
226
out:
227
if (text_out)
228
fclose(text_out);
229
if (image)
230
free(image);
231
if (func_lens)
232
free(func_lens);
233
return err;
234
}
235
236
#else /* HAVE_LLVM_SUPPORT */
237
238
int get_jited_program_text(int fd, char *text, size_t text_sz)
239
{
240
if (env.verbosity >= VERBOSE_VERY)
241
printf("compiled w/o llvm development libraries, can't dis-assembly binary code");
242
return -EOPNOTSUPP;
243
}
244
245
#endif /* HAVE_LLVM_SUPPORT */
246
247