1 // This is free and unencumbered software released into the public domain.
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
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.
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.
24 // For more information, please refer to <http://unlicense.org/>
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.
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.
35 * 2020-05-04, David Lamparter
47 #include <llvm-c/BitReader.h>
48 #include <llvm-c/BitWriter.h>
49 #include <llvm-c/Core.h>
51 #include <json-c/json.h>
53 #include "frr-llvm-debuginfo.h"
55 /* if you want to use this without the special FRRouting defines,
56 * remove the following #define
60 static struct dbginfo
*dbginfo
;
62 static void dbgloc_add(struct json_object
*jsobj
, LLVMValueRef obj
)
64 unsigned file_len
= 0;
65 const char *file
= LLVMGetDebugLocFilename(obj
, &file_len
);
66 unsigned line
= LLVMGetDebugLocLine(obj
);
69 file
= "???", file_len
= 3;
70 else if (file
[0] == '.' && file
[1] == '/')
71 file
+= 2, file_len
-= 2;
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
));
78 static struct json_object
*js_get_or_make(struct json_object
*parent
,
80 struct json_object
*(*maker
)(void))
82 struct json_object
*ret
;
84 ret
= json_object_object_get(parent
, key
);
88 json_object_object_add(parent
, key
, ret
);
92 static bool try_struct_fptr(struct json_object
*js_call
, LLVMValueRef gep
,
95 unsigned long long val
= 0;
97 LLVMTypeRef ptrtype
= LLVMTypeOf(LLVMGetOperand(gep
, 0));
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
);
109 if (LLVMGetTypeKind(ptrtype
) != LLVMStructTypeKind
)
112 idx
= LLVMGetOperand(gep
, i
);
113 if (!LLVMIsConstant(idx
))
115 val
= LLVMConstIntGetZExtValue(idx
);
117 unsigned n
= LLVMGetNumContainedTypes(ptrtype
);
123 LLVMGetSubtypes(ptrtype
, arr
);
130 idx
= LLVMGetOperand(gep
, LLVMGetNumOperands(gep
) - 1);
131 if (!LLVMIsConstant(idx
))
134 val
= LLVMConstIntGetZExtValue(idx
);
136 char *sname
= NULL
, *mname
= NULL
;
138 if (dbginfo_struct_member(dbginfo
, ptrtype
, val
, &sname
, &mname
)) {
139 fprintf(stderr
, "%s: call to struct %s->%s\n", prefix
, sname
,
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
));
156 static bool details_fptr_vars
= false;
157 static bool details_fptr_consts
= true;
166 static void walk_const_fptrs(struct json_object
*js_call
, LLVMValueRef value
,
167 const char *prefix
, bool *hdr_written
)
172 if (LLVMIsAGlobalVariable(value
)) {
173 type
= LLVMGlobalGetValueType(value
);
174 value
= LLVMGetInitializer(value
);
176 type
= LLVMTypeOf(value
);
179 if (LLVMIsAFunction(value
)) {
180 struct json_object
*js_fptrs
;
182 js_fptrs
= js_get_or_make(js_call
, "funcptrs",
183 json_object_new_array
);
186 const char *fn_name
= LLVMGetValueName2(value
, &fn_len
);
188 size_t curlen
= json_object_array_length(js_fptrs
);
189 struct json_object
*jsobj
;
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
);
196 if (s
&& !strcmp(s
, fn_name
))
200 if (details_fptr_consts
&& !*hdr_written
) {
202 "%s: calls function pointer from constant or global data\n",
206 if (details_fptr_consts
)
207 fprintf(stderr
, "%s- constant: %.*s()\n",
208 prefix
, (int)fn_len
, fn_name
);
210 json_object_array_add(js_fptrs
,
211 json_object_new_string_len(fn_name
,
216 kind
= LLVMGetValueKind(value
);
222 case LLVMUndefValueValueKind
:
223 case LLVMConstantAggregateZeroValueKind
:
224 case LLVMConstantPointerNullValueKind
:
225 /* null pointer / array - ignore */
228 case LLVMConstantIntValueKind
:
229 /* integer - ignore */
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
);
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
);
246 case LLVMConstantExprValueKind
:
247 switch (LLVMGetConstOpcode(value
)) {
248 case LLVMGetElementPtr
:
249 if (try_struct_fptr(js_call
, value
, prefix
)) {
255 "%s: calls function pointer from unhandled const GEP\n",
260 /* to help the user / development */
263 "%s: calls function pointer from constexpr\n",
267 dump
= LLVMPrintValueToString(value
);
268 fprintf(stderr
, "%s- [opcode=%d] %s\n", prefix
,
269 LLVMGetConstOpcode(value
), dump
);
270 LLVMDisposeMessage(dump
);
275 /* to help the user / development */
278 "%s: calls function pointer from constant or global data\n",
282 dump
= LLVMPrintValueToString(value
);
284 "%s- value could not be processed:\n"
285 "%s- [kind=%d] %s\n",
286 prefix
, prefix
, kind
, dump
);
287 LLVMDisposeMessage(dump
);
294 static bool is_thread_sched(const char *name
, size_t len
)
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",
307 for (i
= 0; i
< sizeof(names
) / sizeof(names
[0]); i
++) {
308 if (strlen(names
[i
]) != len
)
310 if (!memcmp(names
[i
], name
, len
))
317 static bool _check_val(bool cond
, const char *text
, LLVMValueRef dumpval
)
322 char *dump
= LLVMPrintValueToString(dumpval
);
323 fprintf(stderr
, "check failed: %s\ndump:\n\t%s\n", text
, dump
);
324 LLVMDisposeMessage(dump
);
328 #define check_val(cond, dump) \
329 if (!_check_val(cond, #cond, dump)) \
332 static char *get_string(LLVMValueRef value
)
334 if (!LLVMIsAConstant(value
))
335 return strdup("!NOT-A-CONST");
337 if (LLVMGetValueKind(value
) == LLVMConstantExprValueKind
338 && LLVMGetConstOpcode(value
) == LLVMGetElementPtr
) {
339 value
= LLVMGetOperand(value
, 0);
341 if (!LLVMIsAConstant(value
))
342 return strdup("!NOT-A-CONST-2");
345 if (LLVMIsAGlobalVariable(value
))
346 value
= LLVMGetInitializer(value
);
349 const char *sval
= LLVMGetAsString(value
, &len
);
351 return strndup(sval
, len
);
354 static void handle_yang_module(struct json_object
*js_special
,
355 LLVMValueRef yang_mod
)
357 check_val(LLVMIsAGlobalVariable(yang_mod
), yang_mod
);
361 value
= LLVMGetInitializer(yang_mod
);
362 LLVMValueKind kind
= LLVMGetValueKind(value
);
364 check_val(kind
== LLVMConstantStructValueKind
, value
);
367 const char *var_name
= LLVMGetValueName2(yang_mod
, &var_len
);
368 char buf_name
[var_len
+ 1];
370 memcpy(buf_name
, var_name
, var_len
);
371 buf_name
[var_len
] = '\0';
373 struct json_object
*js_yang
, *js_yangmod
, *js_items
;
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
);
379 char *mod_name
= get_string(LLVMGetOperand(value
, 0));
380 json_object_object_add(js_yangmod
, "name",
381 json_object_new_string(mod_name
));
384 value
= LLVMGetOperand(value
, 1);
385 kind
= LLVMGetValueKind(value
);
386 check_val(kind
== LLVMConstantArrayValueKind
, value
);
388 unsigned len
= LLVMGetArrayLength(LLVMTypeOf(value
));
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));
395 js_item
= json_object_new_object();
396 json_object_array_add(js_items
, js_item
);
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
);
404 LLVMValueRef cbs
= LLVMGetOperand(item
, 1);
406 check_val(LLVMGetValueKind(cbs
) == LLVMConstantStructValueKind
,
409 LLVMTypeRef cbs_type
= LLVMTypeOf(cbs
);
410 unsigned cblen
= LLVMCountStructElementTypes(cbs_type
);
412 for (unsigned i
= 0; i
< cblen
; i
++) {
413 LLVMValueRef cb
= LLVMGetOperand(cbs
, i
);
418 if (dbginfo_struct_member(dbginfo
, cbs_type
, i
, &sname
,
423 if (LLVMIsAFunction(cb
)) {
427 fn_name
= LLVMGetValueName2(cb
, &fn_len
);
429 json_object_object_add(
431 json_object_new_string_len(fn_name
,
441 static void handle_daemoninfo(struct json_object
*js_special
,
442 LLVMValueRef daemoninfo
)
444 check_val(LLVMIsAGlobalVariable(daemoninfo
), daemoninfo
);
450 type
= LLVMGlobalGetValueType(daemoninfo
);
451 value
= LLVMGetInitializer(daemoninfo
);
452 LLVMValueKind kind
= LLVMGetValueKind(value
);
454 check_val(kind
== LLVMConstantStructValueKind
, value
);
458 len
= LLVMCountStructElementTypes(type
);
460 LLVMTypeRef fieldtypes
[len
];
461 LLVMGetSubtypes(type
, fieldtypes
);
463 for (unsigned i
= 0; i
< len
; i
++) {
464 LLVMTypeRef t
= fieldtypes
[i
];
466 if (LLVMGetTypeKind(t
) != LLVMPointerTypeKind
)
468 t
= LLVMGetElementType(t
);
469 if (LLVMGetTypeKind(t
) != LLVMPointerTypeKind
)
471 t
= LLVMGetElementType(t
);
472 if (LLVMGetTypeKind(t
) != LLVMStructTypeKind
)
475 const char *name
= LLVMGetStructName(t
);
476 if (!strcmp(name
, "struct.frr_yang_module_info"))
483 LLVMValueRef yang_mods
= LLVMGetOperand(value
, yang_idx
);
484 LLVMValueRef yang_size
= LLVMGetOperand(value
, yang_idx
+ 1);
486 check_val(LLVMIsConstant(yang_size
), yang_size
);
488 unsigned long long ival
= LLVMConstIntGetZExtValue(yang_size
);
490 check_val(LLVMGetValueKind(yang_mods
) == LLVMConstantExprValueKind
491 && LLVMGetConstOpcode(yang_mods
) == LLVMGetElementPtr
,
494 yang_mods
= LLVMGetOperand(yang_mods
, 0);
496 check_val(LLVMIsAGlobalVariable(yang_mods
), yang_mods
);
498 yang_mods
= LLVMGetInitializer(yang_mods
);
500 check_val(LLVMGetValueKind(yang_mods
) == LLVMConstantArrayValueKind
,
503 len
= LLVMGetArrayLength(LLVMTypeOf(yang_mods
));
506 fprintf(stderr
, "length mismatch - %llu vs. %u\n", ival
, len
);
508 for (unsigned i
= 0; i
< len
; i
++) {
511 LLVMValueRef item
= LLVMGetOperand(yang_mods
, i
);
512 LLVMValueKind kind
= LLVMGetValueKind(item
);
514 check_val(kind
== LLVMGlobalVariableValueKind
515 || kind
== LLVMConstantExprValueKind
,
518 if (kind
== LLVMGlobalVariableValueKind
)
521 LLVMOpcode opcode
= LLVMGetConstOpcode(item
);
524 item
= LLVMGetOperand(item
, 0);
525 handle_yang_module(js_special
, item
);
529 dump
= LLVMPrintValueToString(item
);
530 printf("[%u] = [opcode=%u] %s\n", i
, opcode
, dump
);
531 LLVMDisposeMessage(dump
);
536 static void process_call(struct json_object
*js_calls
,
537 struct json_object
*js_special
,
539 LLVMValueRef function
)
541 struct json_object
*js_call
, *js_fptrs
= NULL
;
543 LLVMValueRef called
= LLVMGetCalledValue(instr
);
545 if (LLVMIsAInlineAsm(called
))
548 if (LLVMIsAConstantExpr(called
)) {
549 LLVMOpcode opcode
= LLVMGetConstOpcode(called
);
551 if (opcode
== LLVMBitCast
) {
552 LLVMValueRef op0
= LLVMGetOperand(called
, 0);
554 if (LLVMIsAFunction(op0
))
559 size_t called_len
= 0;
560 const char *called_name
= LLVMGetValueName2(called
, &called_len
);
561 unsigned n_args
= LLVMGetNumArgOperands(instr
);
563 bool is_external
= LLVMIsDeclaration(called
);
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
));
571 if (!called_name
|| called_len
== 0) {
572 json_object_object_add(js_call
, "type",
573 json_object_new_string("indirect"));
575 LLVMValueRef last
= called
;
578 const char *name_c
= LLVMGetValueName2(function
, &name_len
);
581 /* information for FRR hooks is dumped for the registration
582 * in _hook_typecheck; we can safely ignore the funcptr here
584 if (strncmp(name_c
, "hook_call_", 10) == 0)
588 unsigned file_len
= 0;
589 const char *file
= LLVMGetDebugLocFilename(instr
, &file_len
);
590 unsigned line
= LLVMGetDebugLocLine(instr
);
593 snprintf(prefix
, sizeof(prefix
), "%.*s:%d:%.*s()",
594 (int)file_len
, file
, line
, (int)name_len
, name_c
);
596 if (LLVMIsALoadInst(called
)
597 && LLVMIsAGetElementPtrInst(LLVMGetOperand(called
, 0))
598 && try_struct_fptr(js_call
, LLVMGetOperand(called
, 0),
600 goto out_struct_fptr
;
602 while (LLVMIsALoadInst(last
) || LLVMIsAGetElementPtrInst(last
))
603 /* skipping over details for GEP here, but meh. */
604 last
= LLVMGetOperand(last
, 0);
606 if (LLVMIsAAllocaInst(last
)) {
607 /* "alloca" is just generically all variables on the
608 * stack, this does not refer to C alloca() calls
610 * looking at the control flow in the function can
611 * give better results here, it's just not implemented
615 "%s: call to a function pointer variable\n",
618 if (details_fptr_vars
) {
619 char *dump
= LLVMPrintValueToString(called
);
620 printf("%s- %s\n", prefix
, dump
);
621 LLVMDisposeMessage(dump
);
624 json_object_object_add(
626 json_object_new_string("stack_fptr"));
627 } else if (LLVMIsACallInst(last
)) {
628 /* calling the a function pointer returned from
631 struct json_object
*js_indirect
;
633 js_indirect
= js_get_or_make(js_call
, "return_of",
634 json_object_new_array
);
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.)
641 bool hdr_written
= false;
642 walk_const_fptrs(js_call
, last
, prefix
, &hdr_written
);
643 if (details_fptr_consts
&& !hdr_written
)
645 "%s: calls function pointer from constant or global data, but no non-NULL function pointers found\n",
648 char *dump
= LLVMPrintValueToString(called
);
649 fprintf(stderr
, "%s: ??? %s\n", prefix
, dump
);
650 LLVMDisposeMessage(dump
);
653 } else if (!strcmp(called_name
, "_install_element")) {
654 LLVMValueRef param0
= LLVMGetOperand(instr
, 0);
655 if (!LLVMIsAConstantInt(param0
))
658 long long vty_node
= LLVMConstIntGetSExtValue(param0
);
659 json_object_object_add(js_call
, "vty_node",
660 json_object_new_int64(vty_node
));
662 LLVMValueRef param1
= LLVMGetOperand(instr
, 1);
663 if (!LLVMIsAGlobalVariable(param1
))
666 LLVMValueRef intlz
= LLVMGetInitializer(param1
);
667 assert(intlz
&& LLVMIsConstant(intlz
));
669 LLVMValueKind intlzkind
= LLVMGetValueKind(intlz
);
670 assert(intlzkind
== LLVMConstantStructValueKind
);
672 LLVMValueRef funcptr
= LLVMGetOperand(intlz
, 4);
673 assert(LLVMIsAFunction(funcptr
));
675 size_t target_len
= 0;
677 target
= LLVMGetValueName2(funcptr
, &target_len
);
679 json_object_object_add(
681 json_object_new_string("install_element"));
682 json_object_object_add(
684 json_object_new_string_len(target
, target_len
));
688 json_object_object_add(
690 json_object_new_string("install_element"));
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(
697 json_object_new_string_len(called_name
, called_len
));
700 fparam
= LLVMGetOperand(instr
, 2);
703 size_t target_len
= 0;
705 target
= LLVMGetValueName2(fparam
, &target_len
);
707 json_object_object_add(js_call
, "target",
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));
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
;
719 hook_name
= called_name
+ strlen("_hook_typecheck_");
721 json_object_object_add(js_call
, "type",
722 json_object_new_string("hook"));
724 LLVMValueRef param0
= LLVMGetOperand(instr
, 0);
725 if (!LLVMIsAFunction(param0
))
728 size_t target_len
= 0;
730 target
= LLVMGetValueName2(param0
, &target_len
);
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
);
737 js_this
= json_object_new_object();
738 json_object_array_add(js_hook
, js_this
);
740 dbgloc_add(js_this
, instr
);
741 json_object_object_add(
743 json_object_new_string_len(target
, target_len
));
746 /* TODO (FRR specifics):
747 * - workqueues - not sure we can do much there
750 #endif /* FRR_SPECIFIC */
751 } else if (!strcmp(called_name
, "frr_preinit")) {
752 LLVMValueRef daemoninfo
= LLVMGetOperand(instr
, 0);
754 handle_daemoninfo(js_special
, daemoninfo
);
756 json_object_object_add(
758 json_object_new_string_len(called_name
, called_len
));
760 json_object_object_add(
762 json_object_new_string_len(called_name
, called_len
));
766 for (unsigned argno
= 0; argno
< n_args
; argno
++) {
767 LLVMValueRef param
= LLVMGetOperand(instr
, argno
);
769 const char *target_name
;
771 if (LLVMIsAFunction(param
)) {
772 js_fptrs
= js_get_or_make(js_call
, "funcptrs",
773 json_object_new_array
);
775 target_name
= LLVMGetValueName2(param
, &target_len
);
777 json_object_array_add(js_fptrs
,
778 json_object_new_string_len(
779 target_name
, target_len
));
784 static void process_fn(struct json_object
*funcs
,
785 struct json_object
*js_special
,
786 LLVMValueRef function
)
788 struct json_object
*js_func
, *js_calls
;
791 const char *name_c
= LLVMGetValueName2(function
, &name_len
);
794 name
= strndup(name_c
, name_len
);
796 js_func
= json_object_object_get(funcs
, name
);
798 unsigned file_len
= 0;
799 const char *file
= LLVMGetDebugLocFilename(function
, &file_len
);
800 unsigned line
= LLVMGetDebugLocLine(function
);
802 fprintf(stderr
, "%.*s:%d:%s(): duplicate definition!\n",
803 (int)file_len
, file
, line
, name
);
808 js_func
= json_object_new_object();
809 json_object_object_add(funcs
, name
, js_func
);
812 js_calls
= json_object_new_array();
813 json_object_object_add(js_func
, "calls", js_calls
);
815 dbgloc_add(js_func
, function
);
817 for (LLVMBasicBlockRef basicBlock
= LLVMGetFirstBasicBlock(function
);
818 basicBlock
; basicBlock
= LLVMGetNextBasicBlock(basicBlock
)) {
820 for (LLVMValueRef instr
= LLVMGetFirstInstruction(basicBlock
);
821 instr
; instr
= LLVMGetNextInstruction(instr
)) {
823 if (LLVMIsAIntrinsicInst(instr
))
826 if (LLVMIsACallInst(instr
) || LLVMIsAInvokeInst(instr
))
827 process_call(js_calls
, js_special
, instr
,
833 static void help(int retcode
)
836 "FRR LLVM bitcode to callgraph analyzer\n"
838 "usage: frr-llvm-cg [-q|-v] [-o <JSONOUTPUT>] BITCODEINPUT\n"
840 "\t-o FILENAME\twrite JSON output to file instead of stdout\n"
841 "\t-v\t\tbe more verbose\n"
844 "BITCODEINPUT must be a LLVM binary bitcode file (not text\n"
845 "representation.) Use - to read from stdin.\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");
852 int main(int argc
, char **argv
)
855 const char *out
= NULL
;
856 const char *inp
= NULL
;
859 while ((opt
= getopt(argc
, argv
, "hvqo:")) != -1) {
867 if (v_or_q
&& v_or_q
!= 'v')
869 details_fptr_vars
= true;
870 details_fptr_consts
= true;
874 if (v_or_q
&& v_or_q
!= 'q')
876 details_fptr_vars
= false;
877 details_fptr_consts
= false;
888 if (optind
!= argc
- 1)
893 LLVMMemoryBufferRef memoryBuffer
;
897 // check if we are to read our input file from stdin
898 if (!strcmp(inp
, "-")) {
900 ret
= LLVMCreateMemoryBufferWithSTDIN(&memoryBuffer
, &message
);
902 ret
= LLVMCreateMemoryBufferWithContentsOfFile(
903 inp
, &memoryBuffer
, &message
);
907 fprintf(stderr
, "failed to open %s: %s\n", inp
, message
);
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
);
920 // done with the memory buffer now, so dispose of it
921 LLVMDisposeMemoryBuffer(memoryBuffer
);
923 dbginfo
= dbginfo_load(module
);
925 struct json_object
*js_root
, *js_funcs
, *js_special
;
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
);
933 // loop through all the functions in the module
934 for (LLVMValueRef function
= LLVMGetFirstFunction(module
); function
;
935 function
= LLVMGetNextFunction(function
)) {
936 if (LLVMIsDeclaration(function
))
939 process_fn(js_funcs
, js_special
, function
);
943 char tmpout
[strlen(out
) + 5];
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
);
951 fprintf(stderr
, "could not write JSON to file\n");
954 if (rename(tmpout
, out
)) {
955 fprintf(stderr
, "could not rename JSON output: %s\n",
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
);
966 fprintf(stderr
, "could not write JSON to stdout\n");
971 LLVMDisposeModule(module
);