]> git.proxmox.com Git - mirror_frr.git/blob - tools/frr-llvm-cg.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[mirror_frr.git] / tools / frr-llvm-cg.c
1 // This is free and unencumbered software released into the public domain.
2 //
3 // Anyone is free to copy, modify, publish, use, compile, sell, or
4 // distribute this software, either in source code form or as a compiled
5 // binary, for any purpose, commercial or non-commercial, and by any
6 // means.
7 //
8 // In jurisdictions that recognize copyright laws, the author or authors
9 // of this software dedicate any and all copyright interest in the
10 // software to the public domain. We make this dedication for the benefit
11 // of the public at large and to the detriment of our heirs and
12 // successors. We intend this dedication to be an overt act of
13 // relinquishment in perpetuity of all present and future rights to this
14 // software under copyright law.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 // OTHER DEALINGS IN THE SOFTWARE.
23 //
24 // For more information, please refer to <http://unlicense.org/>
25
26 /* based on example code: https://github.com/sheredom/llvm_bc_parsing_example
27 * which came under the above (un-)license. does not depend on any FRR
28 * pieces, so no reason to change the license.
29 *
30 * please note that while included in the FRR sources, this tool is in no way
31 * supported or maintained by the FRR community. it is provided as a
32 * "convenience"; while it worked at some point (using LLVM 8 / 9), it may
33 * easily break with a future LLVM version or any other factors.
34 *
35 * 2020-05-04, David Lamparter
36 */
37
38 #include <string.h>
39 #include <strings.h>
40 #include <stdbool.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <assert.h>
46
47 #include <llvm-c/BitReader.h>
48 #include <llvm-c/BitWriter.h>
49 #include <llvm-c/Core.h>
50
51 #include <json-c/json.h>
52
53 #include "frr-llvm-debuginfo.h"
54
55 /* if you want to use this without the special FRRouting defines,
56 * remove the following #define
57 */
58 #define FRR_SPECIFIC
59
60 static struct dbginfo *dbginfo;
61
62 static void dbgloc_add(struct json_object *jsobj, LLVMValueRef obj)
63 {
64 unsigned file_len = 0;
65 const char *file = LLVMGetDebugLocFilename(obj, &file_len);
66 unsigned line = LLVMGetDebugLocLine(obj);
67
68 if (!file)
69 file = "???", file_len = 3;
70 else if (file[0] == '.' && file[1] == '/')
71 file += 2, file_len -= 2;
72
73 json_object_object_add(jsobj, "filename",
74 json_object_new_string_len(file, file_len));
75 json_object_object_add(jsobj, "line", json_object_new_int64(line));
76 }
77
78 static struct json_object *js_get_or_make(struct json_object *parent,
79 const char *key,
80 struct json_object *(*maker)(void))
81 {
82 struct json_object *ret;
83
84 ret = json_object_object_get(parent, key);
85 if (ret)
86 return ret;
87 ret = maker();
88 json_object_object_add(parent, key, ret);
89 return ret;
90 }
91
92 static bool try_struct_fptr(struct json_object *js_call, LLVMValueRef gep,
93 const char *prefix)
94 {
95 unsigned long long val = 0;
96 bool ret = false;
97 LLVMTypeRef ptrtype = LLVMTypeOf(LLVMGetOperand(gep, 0));
98 LLVMValueRef idx;
99
100 /* middle steps like struct a -> struct b a_member; -> fptr */
101 for (int i = 1; ptrtype && i < LLVMGetNumOperands(gep) - 1; i++) {
102 if (LLVMGetTypeKind(ptrtype) == LLVMPointerTypeKind
103 || LLVMGetTypeKind(ptrtype) == LLVMArrayTypeKind
104 || LLVMGetTypeKind(ptrtype) == LLVMVectorTypeKind) {
105 ptrtype = LLVMGetElementType(ptrtype);
106 continue;
107 }
108
109 if (LLVMGetTypeKind(ptrtype) != LLVMStructTypeKind)
110 return false;
111
112 idx = LLVMGetOperand(gep, i);
113 if (!LLVMIsConstant(idx))
114 return false;
115 val = LLVMConstIntGetZExtValue(idx);
116
117 unsigned n = LLVMGetNumContainedTypes(ptrtype);
118 LLVMTypeRef arr[n];
119
120 if (val > n)
121 return false;
122
123 LLVMGetSubtypes(ptrtype, arr);
124 ptrtype = arr[val];
125 }
126
127 if (!ptrtype)
128 return false;
129
130 idx = LLVMGetOperand(gep, LLVMGetNumOperands(gep) - 1);
131 if (!LLVMIsConstant(idx))
132 return false;
133
134 val = LLVMConstIntGetZExtValue(idx);
135
136 char *sname = NULL, *mname = NULL;
137
138 if (dbginfo_struct_member(dbginfo, ptrtype, val, &sname, &mname)) {
139 fprintf(stderr, "%s: call to struct %s->%s\n", prefix, sname,
140 mname);
141
142 json_object_object_add(js_call, "type",
143 json_object_new_string("struct_memb"));
144 json_object_object_add(js_call, "struct",
145 json_object_new_string(sname));
146 json_object_object_add(js_call, "member",
147 json_object_new_string(mname));
148 ret = true;
149 }
150 free(sname);
151 free(mname);
152
153 return ret;
154 }
155
156 static bool details_fptr_vars = false;
157 static bool details_fptr_consts = true;
158
159 enum called_fn {
160 FN_GENERIC = 0,
161 FN_NONAME,
162 FN_INSTALL_ELEMENT,
163 FN_THREAD_ADD,
164 };
165
166 static void walk_const_fptrs(struct json_object *js_call, LLVMValueRef value,
167 const char *prefix, bool *hdr_written)
168 {
169 LLVMTypeRef type;
170 LLVMValueKind kind;
171
172 if (LLVMIsAGlobalVariable(value)) {
173 type = LLVMGlobalGetValueType(value);
174 value = LLVMGetInitializer(value);
175 } else {
176 type = LLVMTypeOf(value);
177 }
178
179 if (LLVMIsAFunction(value)) {
180 struct json_object *js_fptrs;
181
182 js_fptrs = js_get_or_make(js_call, "funcptrs",
183 json_object_new_array);
184
185 size_t fn_len;
186 const char *fn_name = LLVMGetValueName2(value, &fn_len);
187
188 size_t curlen = json_object_array_length(js_fptrs);
189 struct json_object *jsobj;
190 const char *s;
191
192 for (size_t i = 0; i < curlen; i++) {
193 jsobj = json_object_array_get_idx(js_fptrs, i);
194 s = json_object_get_string(jsobj);
195
196 if (s && !strcmp(s, fn_name))
197 return;
198 }
199
200 if (details_fptr_consts && !*hdr_written) {
201 fprintf(stderr,
202 "%s: calls function pointer from constant or global data\n",
203 prefix);
204 *hdr_written = true;
205 }
206 if (details_fptr_consts)
207 fprintf(stderr, "%s- constant: %.*s()\n",
208 prefix, (int)fn_len, fn_name);
209
210 json_object_array_add(js_fptrs,
211 json_object_new_string_len(fn_name,
212 fn_len));
213 return;
214 }
215
216 kind = LLVMGetValueKind(value);
217
218 unsigned len;
219 char *dump;
220
221 switch (kind) {
222 case LLVMUndefValueValueKind:
223 case LLVMConstantAggregateZeroValueKind:
224 case LLVMConstantPointerNullValueKind:
225 /* null pointer / array - ignore */
226 break;
227
228 case LLVMConstantIntValueKind:
229 /* integer - ignore */
230 break;
231
232 case LLVMConstantStructValueKind:
233 len = LLVMCountStructElementTypes(type);
234 for (unsigned i = 0; i < len; i++)
235 walk_const_fptrs(js_call, LLVMGetOperand(value, i),
236 prefix, hdr_written);
237 break;
238
239 case LLVMConstantArrayValueKind:
240 len = LLVMGetArrayLength(type);
241 for (unsigned i = 0; i < len; i++)
242 walk_const_fptrs(js_call, LLVMGetOperand(value, i),
243 prefix, hdr_written);
244 return;
245
246 case LLVMConstantExprValueKind:
247 switch (LLVMGetConstOpcode(value)) {
248 case LLVMGetElementPtr:
249 if (try_struct_fptr(js_call, value, prefix)) {
250 *hdr_written = true;
251 return;
252 }
253
254 fprintf(stderr,
255 "%s: calls function pointer from unhandled const GEP\n",
256 prefix);
257 *hdr_written = true;
258 /* fallthru */
259 default:
260 /* to help the user / development */
261 if (!*hdr_written) {
262 fprintf(stderr,
263 "%s: calls function pointer from constexpr\n",
264 prefix);
265 *hdr_written = true;
266 }
267 dump = LLVMPrintValueToString(value);
268 fprintf(stderr, "%s- [opcode=%d] %s\n", prefix,
269 LLVMGetConstOpcode(value), dump);
270 LLVMDisposeMessage(dump);
271 }
272 return;
273
274 default:
275 /* to help the user / development */
276 if (!*hdr_written) {
277 fprintf(stderr,
278 "%s: calls function pointer from constant or global data\n",
279 prefix);
280 *hdr_written = true;
281 }
282 dump = LLVMPrintValueToString(value);
283 fprintf(stderr,
284 "%s- value could not be processed:\n"
285 "%s- [kind=%d] %s\n",
286 prefix, prefix, kind, dump);
287 LLVMDisposeMessage(dump);
288 return;
289 }
290 return;
291 }
292
293 #ifdef FRR_SPECIFIC
294 static bool is_thread_sched(const char *name, size_t len)
295 {
296 #define thread_prefix "_"
297 static const char *const names[] = {
298 thread_prefix "thread_add_read_write",
299 thread_prefix "thread_add_timer",
300 thread_prefix "thread_add_timer_msec",
301 thread_prefix "thread_add_timer_tv",
302 thread_prefix "thread_add_event",
303 thread_prefix "thread_execute",
304 };
305 size_t i;
306
307 for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
308 if (strlen(names[i]) != len)
309 continue;
310 if (!memcmp(names[i], name, len))
311 return true;
312 }
313 return false;
314 }
315 #endif
316
317 static bool _check_val(bool cond, const char *text, LLVMValueRef dumpval)
318 {
319 if (cond)
320 return true;
321
322 char *dump = LLVMPrintValueToString(dumpval);
323 fprintf(stderr, "check failed: %s\ndump:\n\t%s\n", text, dump);
324 LLVMDisposeMessage(dump);
325 return false;
326 }
327
328 #define check_val(cond, dump) \
329 if (!_check_val(cond, #cond, dump)) \
330 return;
331
332 static char *get_string(LLVMValueRef value)
333 {
334 if (!LLVMIsAConstant(value))
335 return strdup("!NOT-A-CONST");
336
337 if (LLVMGetValueKind(value) == LLVMConstantExprValueKind
338 && LLVMGetConstOpcode(value) == LLVMGetElementPtr) {
339 value = LLVMGetOperand(value, 0);
340
341 if (!LLVMIsAConstant(value))
342 return strdup("!NOT-A-CONST-2");
343 }
344
345 if (LLVMIsAGlobalVariable(value))
346 value = LLVMGetInitializer(value);
347
348 size_t len = 0;
349 const char *sval = LLVMGetAsString(value, &len);
350
351 return strndup(sval, len);
352 }
353
354 static void handle_yang_module(struct json_object *js_special,
355 LLVMValueRef yang_mod)
356 {
357 check_val(LLVMIsAGlobalVariable(yang_mod), yang_mod);
358
359 LLVMValueRef value;
360
361 value = LLVMGetInitializer(yang_mod);
362 LLVMValueKind kind = LLVMGetValueKind(value);
363
364 check_val(kind == LLVMConstantStructValueKind, value);
365
366 size_t var_len = 0;
367 const char *var_name = LLVMGetValueName2(yang_mod, &var_len);
368 char buf_name[var_len + 1];
369
370 memcpy(buf_name, var_name, var_len);
371 buf_name[var_len] = '\0';
372
373 struct json_object *js_yang, *js_yangmod, *js_items;
374
375 js_yang = js_get_or_make(js_special, "yang", json_object_new_object);
376 js_yangmod = js_get_or_make(js_yang, buf_name, json_object_new_object);
377 js_items = js_get_or_make(js_yangmod, "items", json_object_new_array);
378
379 char *mod_name = get_string(LLVMGetOperand(value, 0));
380 json_object_object_add(js_yangmod, "name",
381 json_object_new_string(mod_name));
382 free(mod_name);
383
384 value = LLVMGetOperand(value, 1);
385 kind = LLVMGetValueKind(value);
386 check_val(kind == LLVMConstantArrayValueKind, value);
387
388 unsigned len = LLVMGetArrayLength(LLVMTypeOf(value));
389
390 for (unsigned i = 0; i < len - 1; i++) {
391 struct json_object *js_item, *js_cbs;
392 LLVMValueRef item = LLVMGetOperand(value, i);
393 char *xpath = get_string(LLVMGetOperand(item, 0));
394
395 js_item = json_object_new_object();
396 json_object_array_add(js_items, js_item);
397
398 json_object_object_add(js_item, "xpath",
399 json_object_new_string(xpath));
400 js_cbs = js_get_or_make(js_item, "cbs", json_object_new_object);
401
402 free(xpath);
403
404 LLVMValueRef cbs = LLVMGetOperand(item, 1);
405
406 check_val(LLVMGetValueKind(cbs) == LLVMConstantStructValueKind,
407 value);
408
409 LLVMTypeRef cbs_type = LLVMTypeOf(cbs);
410 unsigned cblen = LLVMCountStructElementTypes(cbs_type);
411
412 for (unsigned i = 0; i < cblen; i++) {
413 LLVMValueRef cb = LLVMGetOperand(cbs, i);
414
415 char *sname = NULL;
416 char *mname = NULL;
417
418 if (dbginfo_struct_member(dbginfo, cbs_type, i, &sname,
419 &mname)) {
420 (void)0;
421 }
422
423 if (LLVMIsAFunction(cb)) {
424 size_t fn_len;
425 const char *fn_name;
426
427 fn_name = LLVMGetValueName2(cb, &fn_len);
428
429 json_object_object_add(
430 js_cbs, mname,
431 json_object_new_string_len(fn_name,
432 fn_len));
433 }
434
435 free(sname);
436 free(mname);
437 }
438 }
439 }
440
441 static void handle_daemoninfo(struct json_object *js_special,
442 LLVMValueRef daemoninfo)
443 {
444 check_val(LLVMIsAGlobalVariable(daemoninfo), daemoninfo);
445
446 LLVMTypeRef type;
447 LLVMValueRef value;
448 unsigned len;
449
450 type = LLVMGlobalGetValueType(daemoninfo);
451 value = LLVMGetInitializer(daemoninfo);
452 LLVMValueKind kind = LLVMGetValueKind(value);
453
454 check_val(kind == LLVMConstantStructValueKind, value);
455
456 int yang_idx = -1;
457
458 len = LLVMCountStructElementTypes(type);
459
460 LLVMTypeRef fieldtypes[len];
461 LLVMGetSubtypes(type, fieldtypes);
462
463 for (unsigned i = 0; i < len; i++) {
464 LLVMTypeRef t = fieldtypes[i];
465
466 if (LLVMGetTypeKind(t) != LLVMPointerTypeKind)
467 continue;
468 t = LLVMGetElementType(t);
469 if (LLVMGetTypeKind(t) != LLVMPointerTypeKind)
470 continue;
471 t = LLVMGetElementType(t);
472 if (LLVMGetTypeKind(t) != LLVMStructTypeKind)
473 continue;
474
475 const char *name = LLVMGetStructName(t);
476 if (!strcmp(name, "struct.frr_yang_module_info"))
477 yang_idx = i;
478 }
479
480 if (yang_idx == -1)
481 return;
482
483 LLVMValueRef yang_mods = LLVMGetOperand(value, yang_idx);
484 LLVMValueRef yang_size = LLVMGetOperand(value, yang_idx + 1);
485
486 check_val(LLVMIsConstant(yang_size), yang_size);
487
488 unsigned long long ival = LLVMConstIntGetZExtValue(yang_size);
489
490 check_val(LLVMGetValueKind(yang_mods) == LLVMConstantExprValueKind
491 && LLVMGetConstOpcode(yang_mods) == LLVMGetElementPtr,
492 yang_mods);
493
494 yang_mods = LLVMGetOperand(yang_mods, 0);
495
496 check_val(LLVMIsAGlobalVariable(yang_mods), yang_mods);
497
498 yang_mods = LLVMGetInitializer(yang_mods);
499
500 check_val(LLVMGetValueKind(yang_mods) == LLVMConstantArrayValueKind,
501 yang_mods);
502
503 len = LLVMGetArrayLength(LLVMTypeOf(yang_mods));
504
505 if (len != ival)
506 fprintf(stderr, "length mismatch - %llu vs. %u\n", ival, len);
507
508 for (unsigned i = 0; i < len; i++) {
509 char *dump;
510
511 LLVMValueRef item = LLVMGetOperand(yang_mods, i);
512 LLVMValueKind kind = LLVMGetValueKind(item);
513
514 check_val(kind == LLVMGlobalVariableValueKind
515 || kind == LLVMConstantExprValueKind,
516 item);
517
518 if (kind == LLVMGlobalVariableValueKind)
519 continue;
520
521 LLVMOpcode opcode = LLVMGetConstOpcode(item);
522 switch (opcode) {
523 case LLVMBitCast:
524 item = LLVMGetOperand(item, 0);
525 handle_yang_module(js_special, item);
526 break;
527
528 default:
529 dump = LLVMPrintValueToString(item);
530 printf("[%u] = [opcode=%u] %s\n", i, opcode, dump);
531 LLVMDisposeMessage(dump);
532 }
533 }
534 }
535
536 static void process_call(struct json_object *js_calls,
537 struct json_object *js_special,
538 LLVMValueRef instr,
539 LLVMValueRef function)
540 {
541 struct json_object *js_call, *js_fptrs = NULL;
542
543 LLVMValueRef called = LLVMGetCalledValue(instr);
544
545 if (LLVMIsAInlineAsm(called))
546 return;
547
548 if (LLVMIsAConstantExpr(called)) {
549 LLVMOpcode opcode = LLVMGetConstOpcode(called);
550
551 if (opcode == LLVMBitCast) {
552 LLVMValueRef op0 = LLVMGetOperand(called, 0);
553
554 if (LLVMIsAFunction(op0))
555 called = op0;
556 }
557 }
558
559 size_t called_len = 0;
560 const char *called_name = LLVMGetValueName2(called, &called_len);
561 unsigned n_args = LLVMGetNumArgOperands(instr);
562
563 bool is_external = LLVMIsDeclaration(called);
564
565 js_call = json_object_new_object();
566 json_object_array_add(js_calls, js_call);
567 dbgloc_add(js_call, instr);
568 json_object_object_add(js_call, "is_external",
569 json_object_new_boolean(is_external));
570
571 if (!called_name || called_len == 0) {
572 json_object_object_add(js_call, "type",
573 json_object_new_string("indirect"));
574
575 LLVMValueRef last = called;
576
577 size_t name_len = 0;
578 const char *name_c = LLVMGetValueName2(function, &name_len);
579
580 #ifdef FRR_SPECIFIC
581 /* information for FRR hooks is dumped for the registration
582 * in _hook_typecheck; we can safely ignore the funcptr here
583 */
584 if (strncmp(name_c, "hook_call_", 10) == 0)
585 return;
586 #endif
587
588 unsigned file_len = 0;
589 const char *file = LLVMGetDebugLocFilename(instr, &file_len);
590 unsigned line = LLVMGetDebugLocLine(instr);
591
592 char prefix[256];
593 snprintf(prefix, sizeof(prefix), "%.*s:%d:%.*s()",
594 (int)file_len, file, line, (int)name_len, name_c);
595
596 if (LLVMIsALoadInst(called)
597 && LLVMIsAGetElementPtrInst(LLVMGetOperand(called, 0))
598 && try_struct_fptr(js_call, LLVMGetOperand(called, 0),
599 prefix))
600 goto out_struct_fptr;
601
602 while (LLVMIsALoadInst(last) || LLVMIsAGetElementPtrInst(last))
603 /* skipping over details for GEP here, but meh. */
604 last = LLVMGetOperand(last, 0);
605
606 if (LLVMIsAAllocaInst(last)) {
607 /* "alloca" is just generically all variables on the
608 * stack, this does not refer to C alloca() calls
609 *
610 * looking at the control flow in the function can
611 * give better results here, it's just not implemented
612 * (yet?)
613 */
614 fprintf(stderr,
615 "%s: call to a function pointer variable\n",
616 prefix);
617
618 if (details_fptr_vars) {
619 char *dump = LLVMPrintValueToString(called);
620 printf("%s- %s\n", prefix, dump);
621 LLVMDisposeMessage(dump);
622 }
623
624 json_object_object_add(
625 js_call, "type",
626 json_object_new_string("stack_fptr"));
627 } else if (LLVMIsACallInst(last)) {
628 /* calling the a function pointer returned from
629 * another function.
630 */
631 struct json_object *js_indirect;
632
633 js_indirect = js_get_or_make(js_call, "return_of",
634 json_object_new_array);
635
636 process_call(js_indirect, js_special, last, function);
637 } else if (LLVMIsAConstant(last)) {
638 /* function pointer is a constant (includes loading
639 * from complicated constants like structs or arrays.)
640 */
641 bool hdr_written = false;
642 walk_const_fptrs(js_call, last, prefix, &hdr_written);
643 if (details_fptr_consts && !hdr_written)
644 fprintf(stderr,
645 "%s: calls function pointer from constant or global data, but no non-NULL function pointers found\n",
646 prefix);
647 } else {
648 char *dump = LLVMPrintValueToString(called);
649 fprintf(stderr, "%s: ??? %s\n", prefix, dump);
650 LLVMDisposeMessage(dump);
651 }
652 #ifdef FRR_SPECIFIC
653 } else if (!strcmp(called_name, "_install_element")) {
654 LLVMValueRef param0 = LLVMGetOperand(instr, 0);
655 if (!LLVMIsAConstantInt(param0))
656 goto out_nonconst;
657
658 long long vty_node = LLVMConstIntGetSExtValue(param0);
659 json_object_object_add(js_call, "vty_node",
660 json_object_new_int64(vty_node));
661
662 LLVMValueRef param1 = LLVMGetOperand(instr, 1);
663 if (!LLVMIsAGlobalVariable(param1))
664 goto out_nonconst;
665
666 LLVMValueRef intlz = LLVMGetInitializer(param1);
667 assert(intlz && LLVMIsConstant(intlz));
668
669 LLVMValueKind intlzkind = LLVMGetValueKind(intlz);
670 assert(intlzkind == LLVMConstantStructValueKind);
671
672 LLVMValueRef funcptr = LLVMGetOperand(intlz, 4);
673 assert(LLVMIsAFunction(funcptr));
674
675 size_t target_len = 0;
676 const char *target;
677 target = LLVMGetValueName2(funcptr, &target_len);
678
679 json_object_object_add(
680 js_call, "type",
681 json_object_new_string("install_element"));
682 json_object_object_add(
683 js_call, "target",
684 json_object_new_string_len(target, target_len));
685 return;
686
687 out_nonconst:
688 json_object_object_add(
689 js_call, "target",
690 json_object_new_string("install_element"));
691 return;
692 } else if (is_thread_sched(called_name, called_len)) {
693 json_object_object_add(js_call, "type",
694 json_object_new_string("thread_sched"));
695 json_object_object_add(
696 js_call, "subtype",
697 json_object_new_string_len(called_name, called_len));
698
699 LLVMValueRef fparam;
700 fparam = LLVMGetOperand(instr, 2);
701 assert(fparam);
702
703 size_t target_len = 0;
704 const char *target;
705 target = LLVMGetValueName2(fparam, &target_len);
706
707 json_object_object_add(js_call, "target",
708 !target_len ? NULL :
709 json_object_new_string_len(target, target_len));
710 if (!LLVMIsAFunction(fparam))
711 json_object_object_add(js_call, "target_unresolved",
712 json_object_new_boolean(true));
713 return;
714 } else if (!strncmp(called_name, "_hook_typecheck_",
715 strlen("_hook_typecheck_"))) {
716 struct json_object *js_hook, *js_this;
717 const char *hook_name;
718
719 hook_name = called_name + strlen("_hook_typecheck_");
720
721 json_object_object_add(js_call, "type",
722 json_object_new_string("hook"));
723
724 LLVMValueRef param0 = LLVMGetOperand(instr, 0);
725 if (!LLVMIsAFunction(param0))
726 return;
727
728 size_t target_len = 0;
729 const char *target;
730 target = LLVMGetValueName2(param0, &target_len);
731
732 js_hook = js_get_or_make(js_special, "hooks",
733 json_object_new_object);
734 js_hook = js_get_or_make(js_hook, hook_name,
735 json_object_new_array);
736
737 js_this = json_object_new_object();
738 json_object_array_add(js_hook, js_this);
739
740 dbgloc_add(js_this, instr);
741 json_object_object_add(
742 js_this, "target",
743 json_object_new_string_len(target, target_len));
744 return;
745
746 /* TODO (FRR specifics):
747 * - workqueues - not sure we can do much there
748 * - zclient->* ?
749 */
750 #endif /* FRR_SPECIFIC */
751 } else if (!strcmp(called_name, "frr_preinit")) {
752 LLVMValueRef daemoninfo = LLVMGetOperand(instr, 0);
753
754 handle_daemoninfo(js_special, daemoninfo);
755
756 json_object_object_add(
757 js_call, "target",
758 json_object_new_string_len(called_name, called_len));
759 } else {
760 json_object_object_add(
761 js_call, "target",
762 json_object_new_string_len(called_name, called_len));
763 }
764
765 out_struct_fptr:
766 for (unsigned argno = 0; argno < n_args; argno++) {
767 LLVMValueRef param = LLVMGetOperand(instr, argno);
768 size_t target_len;
769 const char *target_name;
770
771 if (LLVMIsAFunction(param)) {
772 js_fptrs = js_get_or_make(js_call, "funcptrs",
773 json_object_new_array);
774
775 target_name = LLVMGetValueName2(param, &target_len);
776
777 json_object_array_add(js_fptrs,
778 json_object_new_string_len(
779 target_name, target_len));
780 }
781 }
782 }
783
784 static void process_fn(struct json_object *funcs,
785 struct json_object *js_special,
786 LLVMValueRef function)
787 {
788 struct json_object *js_func, *js_calls;
789
790 size_t name_len = 0;
791 const char *name_c = LLVMGetValueName2(function, &name_len);
792 char *name;
793
794 name = strndup(name_c, name_len);
795
796 js_func = json_object_object_get(funcs, name);
797 if (js_func) {
798 unsigned file_len = 0;
799 const char *file = LLVMGetDebugLocFilename(function, &file_len);
800 unsigned line = LLVMGetDebugLocLine(function);
801
802 fprintf(stderr, "%.*s:%d:%s(): duplicate definition!\n",
803 (int)file_len, file, line, name);
804 free(name);
805 return;
806 }
807
808 js_func = json_object_new_object();
809 json_object_object_add(funcs, name, js_func);
810 free(name);
811
812 js_calls = json_object_new_array();
813 json_object_object_add(js_func, "calls", js_calls);
814
815 dbgloc_add(js_func, function);
816
817 for (LLVMBasicBlockRef basicBlock = LLVMGetFirstBasicBlock(function);
818 basicBlock; basicBlock = LLVMGetNextBasicBlock(basicBlock)) {
819
820 for (LLVMValueRef instr = LLVMGetFirstInstruction(basicBlock);
821 instr; instr = LLVMGetNextInstruction(instr)) {
822
823 if (LLVMIsAIntrinsicInst(instr))
824 continue;
825
826 if (LLVMIsACallInst(instr) || LLVMIsAInvokeInst(instr))
827 process_call(js_calls, js_special, instr,
828 function);
829 }
830 }
831 }
832
833 static void help(int retcode)
834 {
835 fprintf(stderr,
836 "FRR LLVM bitcode to callgraph analyzer\n"
837 "\n"
838 "usage: frr-llvm-cg [-q|-v] [-o <JSONOUTPUT>] BITCODEINPUT\n"
839 "\n"
840 "\t-o FILENAME\twrite JSON output to file instead of stdout\n"
841 "\t-v\t\tbe more verbose\n"
842 "\t-q\t\tbe quiet\n"
843 "\n"
844 "BITCODEINPUT must be a LLVM binary bitcode file (not text\n"
845 "representation.) Use - to read from stdin.\n"
846 "\n"
847 "Note it may be necessary to build this binary tool against\n"
848 "the specific LLVM version that created the bitcode file.\n");
849 exit(retcode);
850 }
851
852 int main(int argc, char **argv)
853 {
854 int opt;
855 const char *out = NULL;
856 const char *inp = NULL;
857 char v_or_q = '\0';
858
859 while ((opt = getopt(argc, argv, "hvqo:")) != -1) {
860 switch (opt) {
861 case 'o':
862 if (out)
863 help(1);
864 out = optarg;
865 break;
866 case 'v':
867 if (v_or_q && v_or_q != 'v')
868 help(1);
869 details_fptr_vars = true;
870 details_fptr_consts = true;
871 v_or_q = 'v';
872 break;
873 case 'q':
874 if (v_or_q && v_or_q != 'q')
875 help(1);
876 details_fptr_vars = false;
877 details_fptr_consts = false;
878 v_or_q = 'q';
879 break;
880 case 'h':
881 help(0);
882 return 0;
883 default:
884 help(1);
885 }
886 }
887
888 if (optind != argc - 1)
889 help(1);
890
891 inp = argv[optind];
892
893 LLVMMemoryBufferRef memoryBuffer;
894 char *message;
895 int ret;
896
897 // check if we are to read our input file from stdin
898 if (!strcmp(inp, "-")) {
899 inp = "<stdin>";
900 ret = LLVMCreateMemoryBufferWithSTDIN(&memoryBuffer, &message);
901 } else {
902 ret = LLVMCreateMemoryBufferWithContentsOfFile(
903 inp, &memoryBuffer, &message);
904 }
905
906 if (ret) {
907 fprintf(stderr, "failed to open %s: %s\n", inp, message);
908 free(message);
909 return 1;
910 }
911
912 // now create our module using the memorybuffer
913 LLVMModuleRef module;
914 if (LLVMParseBitcode2(memoryBuffer, &module)) {
915 fprintf(stderr, "%s: invalid bitcode\n", inp);
916 LLVMDisposeMemoryBuffer(memoryBuffer);
917 return 1;
918 }
919
920 // done with the memory buffer now, so dispose of it
921 LLVMDisposeMemoryBuffer(memoryBuffer);
922
923 dbginfo = dbginfo_load(module);
924
925 struct json_object *js_root, *js_funcs, *js_special;
926
927 js_root = json_object_new_object();
928 js_funcs = json_object_new_object();
929 json_object_object_add(js_root, "functions", js_funcs);
930 js_special = json_object_new_object();
931 json_object_object_add(js_root, "special", js_special);
932
933 // loop through all the functions in the module
934 for (LLVMValueRef function = LLVMGetFirstFunction(module); function;
935 function = LLVMGetNextFunction(function)) {
936 if (LLVMIsDeclaration(function))
937 continue;
938
939 process_fn(js_funcs, js_special, function);
940 }
941
942 if (out) {
943 char tmpout[strlen(out) + 5];
944
945 snprintf(tmpout, sizeof(tmpout), "%s.tmp", out);
946 ret = json_object_to_file_ext(tmpout, js_root,
947 JSON_C_TO_STRING_PRETTY |
948 JSON_C_TO_STRING_PRETTY_TAB |
949 JSON_C_TO_STRING_NOSLASHESCAPE);
950 if (ret < 0) {
951 fprintf(stderr, "could not write JSON to file\n");
952 return 1;
953 }
954 if (rename(tmpout, out)) {
955 fprintf(stderr, "could not rename JSON output: %s\n",
956 strerror(errno));
957 unlink(tmpout);
958 return 1;
959 }
960 } else {
961 ret = json_object_to_fd(1, js_root,
962 JSON_C_TO_STRING_PRETTY |
963 JSON_C_TO_STRING_PRETTY_TAB |
964 JSON_C_TO_STRING_NOSLASHESCAPE);
965 if (ret < 0) {
966 fprintf(stderr, "could not write JSON to stdout\n");
967 return 1;
968 }
969 }
970
971 LLVMDisposeModule(module);
972
973 return 0;
974 }