Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/bpf/bpftool/jit_disasm.c
26285 views
1
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2
/*
3
* Based on:
4
*
5
* Minimal BPF JIT image disassembler
6
*
7
* Disassembles BPF JIT compiler emitted opcodes back to asm insn's for
8
* debugging or verification purposes.
9
*
10
* Copyright 2013 Daniel Borkmann <[email protected]>
11
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
12
*/
13
14
#ifndef _GNU_SOURCE
15
#define _GNU_SOURCE
16
#endif
17
#include <stdio.h>
18
#include <stdarg.h>
19
#include <stdint.h>
20
#include <stdlib.h>
21
#include <unistd.h>
22
#include <string.h>
23
#include <sys/stat.h>
24
#include <limits.h>
25
#include <bpf/libbpf.h>
26
27
#ifdef HAVE_LLVM_SUPPORT
28
#include <llvm-c/Core.h>
29
#include <llvm-c/Disassembler.h>
30
#include <llvm-c/Target.h>
31
#include <llvm-c/TargetMachine.h>
32
#endif
33
34
#ifdef HAVE_LIBBFD_SUPPORT
35
#include <bfd.h>
36
#include <dis-asm.h>
37
#include <tools/dis-asm-compat.h>
38
#endif
39
40
#include "json_writer.h"
41
#include "main.h"
42
43
static int oper_count;
44
45
#ifdef HAVE_LLVM_SUPPORT
46
#define DISASM_SPACER
47
48
typedef LLVMDisasmContextRef disasm_ctx_t;
49
50
static int printf_json(char *s)
51
{
52
s = strtok(s, " \t");
53
jsonw_string_field(json_wtr, "operation", s);
54
55
jsonw_name(json_wtr, "operands");
56
jsonw_start_array(json_wtr);
57
oper_count = 1;
58
59
while ((s = strtok(NULL, " \t,()")) != 0) {
60
jsonw_string(json_wtr, s);
61
oper_count++;
62
}
63
return 0;
64
}
65
66
/* This callback to set the ref_type is necessary to have the LLVM disassembler
67
* print PC-relative addresses instead of byte offsets for branch instruction
68
* targets.
69
*/
70
static const char *
71
symbol_lookup_callback(__maybe_unused void *disasm_info,
72
__maybe_unused uint64_t ref_value,
73
uint64_t *ref_type, __maybe_unused uint64_t ref_PC,
74
__maybe_unused const char **ref_name)
75
{
76
*ref_type = LLVMDisassembler_ReferenceType_InOut_None;
77
return NULL;
78
}
79
80
static int
81
init_context(disasm_ctx_t *ctx, const char *arch,
82
__maybe_unused const char *disassembler_options,
83
__maybe_unused unsigned char *image, __maybe_unused ssize_t len,
84
__maybe_unused __u64 func_ksym)
85
{
86
char *triple;
87
88
if (arch)
89
triple = LLVMNormalizeTargetTriple(arch);
90
else
91
triple = LLVMGetDefaultTargetTriple();
92
if (!triple) {
93
p_err("Failed to retrieve triple");
94
return -1;
95
}
96
*ctx = LLVMCreateDisasm(triple, NULL, 0, NULL, symbol_lookup_callback);
97
LLVMDisposeMessage(triple);
98
99
if (!*ctx) {
100
p_err("Failed to create disassembler");
101
return -1;
102
}
103
104
return 0;
105
}
106
107
static void destroy_context(disasm_ctx_t *ctx)
108
{
109
LLVMDisposeMessage(*ctx);
110
}
111
112
static int
113
disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc,
114
__u64 func_ksym)
115
{
116
char buf[256];
117
int count;
118
119
count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc,
120
buf, sizeof(buf));
121
if (json_output)
122
printf_json(buf);
123
else
124
printf("%s", buf);
125
126
return count;
127
}
128
129
int disasm_init(void)
130
{
131
LLVMInitializeAllTargetInfos();
132
LLVMInitializeAllTargetMCs();
133
LLVMInitializeAllDisassemblers();
134
return 0;
135
}
136
#endif /* HAVE_LLVM_SUPPORT */
137
138
#ifdef HAVE_LIBBFD_SUPPORT
139
#define DISASM_SPACER "\t"
140
141
struct disasm_info {
142
struct disassemble_info info;
143
__u64 func_ksym;
144
};
145
146
static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info)
147
{
148
struct disasm_info *dinfo = container_of(info, struct disasm_info, info);
149
150
addr += dinfo->func_ksym;
151
generic_print_address(addr, info);
152
}
153
154
typedef struct {
155
struct disasm_info *info;
156
disassembler_ftype disassemble;
157
bfd *bfdf;
158
} disasm_ctx_t;
159
160
static int get_exec_path(char *tpath, size_t size)
161
{
162
const char *path = "/proc/self/exe";
163
ssize_t len;
164
165
len = readlink(path, tpath, size - 1);
166
if (len <= 0)
167
return -1;
168
169
tpath[len] = 0;
170
171
return 0;
172
}
173
174
static int printf_json(void *out, const char *fmt, va_list ap)
175
{
176
char *s;
177
int err;
178
179
err = vasprintf(&s, fmt, ap);
180
if (err < 0)
181
return -1;
182
183
if (!oper_count) {
184
int i;
185
186
/* Strip trailing spaces */
187
i = strlen(s) - 1;
188
while (s[i] == ' ')
189
s[i--] = '\0';
190
191
jsonw_string_field(json_wtr, "operation", s);
192
jsonw_name(json_wtr, "operands");
193
jsonw_start_array(json_wtr);
194
oper_count++;
195
} else if (!strcmp(fmt, ",")) {
196
/* Skip */
197
} else {
198
jsonw_string(json_wtr, s);
199
oper_count++;
200
}
201
free(s);
202
return 0;
203
}
204
205
static int fprintf_json(void *out, const char *fmt, ...)
206
{
207
va_list ap;
208
int r;
209
210
va_start(ap, fmt);
211
r = printf_json(out, fmt, ap);
212
va_end(ap);
213
214
return r;
215
}
216
217
static int fprintf_json_styled(void *out,
218
enum disassembler_style style __maybe_unused,
219
const char *fmt, ...)
220
{
221
va_list ap;
222
int r;
223
224
va_start(ap, fmt);
225
r = printf_json(out, fmt, ap);
226
va_end(ap);
227
228
return r;
229
}
230
231
static int init_context(disasm_ctx_t *ctx, const char *arch,
232
const char *disassembler_options,
233
unsigned char *image, ssize_t len, __u64 func_ksym)
234
{
235
struct disassemble_info *info;
236
char tpath[PATH_MAX];
237
bfd *bfdf;
238
239
memset(tpath, 0, sizeof(tpath));
240
if (get_exec_path(tpath, sizeof(tpath))) {
241
p_err("failed to create disassembler (get_exec_path)");
242
return -1;
243
}
244
245
ctx->bfdf = bfd_openr(tpath, NULL);
246
if (!ctx->bfdf) {
247
p_err("failed to create disassembler (bfd_openr)");
248
return -1;
249
}
250
if (!bfd_check_format(ctx->bfdf, bfd_object)) {
251
p_err("failed to create disassembler (bfd_check_format)");
252
goto err_close;
253
}
254
bfdf = ctx->bfdf;
255
256
ctx->info = malloc(sizeof(struct disasm_info));
257
if (!ctx->info) {
258
p_err("mem alloc failed");
259
goto err_close;
260
}
261
ctx->info->func_ksym = func_ksym;
262
info = &ctx->info->info;
263
264
if (json_output)
265
init_disassemble_info_compat(info, stdout,
266
(fprintf_ftype) fprintf_json,
267
fprintf_json_styled);
268
else
269
init_disassemble_info_compat(info, stdout,
270
(fprintf_ftype) fprintf,
271
fprintf_styled);
272
273
/* Update architecture info for offload. */
274
if (arch) {
275
const bfd_arch_info_type *inf = bfd_scan_arch(arch);
276
277
if (inf) {
278
bfdf->arch_info = inf;
279
} else {
280
p_err("No libbfd support for %s", arch);
281
goto err_free;
282
}
283
}
284
285
info->arch = bfd_get_arch(bfdf);
286
info->mach = bfd_get_mach(bfdf);
287
if (disassembler_options)
288
info->disassembler_options = disassembler_options;
289
info->buffer = image;
290
info->buffer_length = len;
291
info->print_address_func = disasm_print_addr;
292
293
disassemble_init_for_target(info);
294
295
#ifdef DISASM_FOUR_ARGS_SIGNATURE
296
ctx->disassemble = disassembler(info->arch,
297
bfd_big_endian(bfdf),
298
info->mach,
299
bfdf);
300
#else
301
ctx->disassemble = disassembler(bfdf);
302
#endif
303
if (!ctx->disassemble) {
304
p_err("failed to create disassembler");
305
goto err_free;
306
}
307
return 0;
308
309
err_free:
310
free(info);
311
err_close:
312
bfd_close(ctx->bfdf);
313
return -1;
314
}
315
316
static void destroy_context(disasm_ctx_t *ctx)
317
{
318
free(ctx->info);
319
bfd_close(ctx->bfdf);
320
}
321
322
static int
323
disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image,
324
__maybe_unused ssize_t len, int pc,
325
__maybe_unused __u64 func_ksym)
326
{
327
return ctx->disassemble(pc, &ctx->info->info);
328
}
329
330
int disasm_init(void)
331
{
332
bfd_init();
333
return 0;
334
}
335
#endif /* HAVE_LIBBPFD_SUPPORT */
336
337
int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
338
const char *arch, const char *disassembler_options,
339
const struct btf *btf,
340
const struct bpf_prog_linfo *prog_linfo,
341
__u64 func_ksym, unsigned int func_idx,
342
bool linum)
343
{
344
const struct bpf_line_info *linfo = NULL;
345
unsigned int nr_skip = 0;
346
int count, i;
347
unsigned int pc = 0;
348
disasm_ctx_t ctx;
349
350
if (!len)
351
return -1;
352
353
if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym))
354
return -1;
355
356
if (json_output)
357
jsonw_start_array(json_wtr);
358
do {
359
if (prog_linfo) {
360
linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
361
func_ksym + pc,
362
func_idx,
363
nr_skip);
364
if (linfo)
365
nr_skip++;
366
}
367
368
if (json_output) {
369
jsonw_start_object(json_wtr);
370
oper_count = 0;
371
if (linfo)
372
btf_dump_linfo_json(btf, linfo, linum);
373
jsonw_name(json_wtr, "pc");
374
jsonw_printf(json_wtr, "\"0x%x\"", pc);
375
} else {
376
if (linfo)
377
btf_dump_linfo_plain(btf, linfo, "; ",
378
linum);
379
printf("%4x:" DISASM_SPACER, pc);
380
}
381
382
count = disassemble_insn(&ctx, image, len, pc, func_ksym);
383
384
if (json_output) {
385
/* Operand array, was started in fprintf_json. Before
386
* that, make sure we have a _null_ value if no operand
387
* other than operation code was present.
388
*/
389
if (oper_count == 1)
390
jsonw_null(json_wtr);
391
jsonw_end_array(json_wtr);
392
}
393
394
if (opcodes) {
395
if (json_output) {
396
jsonw_name(json_wtr, "opcodes");
397
jsonw_start_array(json_wtr);
398
for (i = 0; i < count; ++i)
399
jsonw_printf(json_wtr, "\"0x%02hhx\"",
400
(uint8_t)image[pc + i]);
401
jsonw_end_array(json_wtr);
402
} else {
403
printf("\n\t");
404
for (i = 0; i < count; ++i)
405
printf("%02x ",
406
(uint8_t)image[pc + i]);
407
}
408
}
409
if (json_output)
410
jsonw_end_object(json_wtr);
411
else
412
printf("\n");
413
414
pc += count;
415
} while (count > 0 && pc < len);
416
if (json_output)
417
jsonw_end_array(json_wtr);
418
419
destroy_context(&ctx);
420
421
return 0;
422
}
423
424