~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/bpf/jit_disasm_helpers.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php