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