diff options
author | David Lamparter <equinox@diac24.net> | 2021-03-23 20:19:10 +0100 |
---|---|---|
committer | David Lamparter <equinox@diac24.net> | 2021-04-07 22:31:25 +0200 |
commit | d71449a3f7681dd845504b72387bf68be7cc0d87 (patch) | |
tree | efa28f05729d36f608bdf53871e2ef835e6da07c /tools/frr-llvm-cg.c | |
parent | tools: adapt frr-llvm-cg to xref changes (diff) | |
download | frr-d71449a3f7681dd845504b72387bf68be7cc0d87.tar.xz frr-d71449a3f7681dd845504b72387bf68be7cc0d87.zip |
tools: grok struct member calls in frr-llvm-cg
Calling a function pointer embedded in a struct is quite common & having
this listed in the call graph is useful.
Signed-off-by: David Lamparter <equinox@diac24.net>
Diffstat (limited to 'tools/frr-llvm-cg.c')
-rw-r--r-- | tools/frr-llvm-cg.c | 106 |
1 files changed, 105 insertions, 1 deletions
diff --git a/tools/frr-llvm-cg.c b/tools/frr-llvm-cg.c index 5bdd10d06..81468b734 100644 --- a/tools/frr-llvm-cg.c +++ b/tools/frr-llvm-cg.c @@ -50,11 +50,15 @@ #include <json-c/json.h> +#include "frr-llvm-debuginfo.h" + /* if you want to use this without the special FRRouting defines, * remove the following #define */ #define FRR_SPECIFIC +static struct dbginfo *dbginfo; + static void dbgloc_add(struct json_object *jsobj, LLVMValueRef obj) { unsigned file_len = 0; @@ -85,6 +89,70 @@ static struct json_object *js_get_or_make(struct json_object *parent, return ret; } +static bool try_struct_fptr(struct json_object *js_call, LLVMValueRef gep, + const char *prefix) +{ + unsigned long long val = 0; + bool ret = false; + LLVMTypeRef ptrtype = LLVMTypeOf(LLVMGetOperand(gep, 0)); + LLVMValueRef idx; + + /* middle steps like struct a -> struct b a_member; -> fptr */ + for (int i = 1; ptrtype && i < LLVMGetNumOperands(gep) - 1; i++) { + if (LLVMGetTypeKind(ptrtype) == LLVMPointerTypeKind + || LLVMGetTypeKind(ptrtype) == LLVMArrayTypeKind + || LLVMGetTypeKind(ptrtype) == LLVMVectorTypeKind) { + ptrtype = LLVMGetElementType(ptrtype); + continue; + } + + if (LLVMGetTypeKind(ptrtype) != LLVMStructTypeKind) + return false; + + idx = LLVMGetOperand(gep, i); + if (!LLVMIsConstant(idx)) + return false; + val = LLVMConstIntGetZExtValue(idx); + + unsigned n = LLVMGetNumContainedTypes(ptrtype); + LLVMTypeRef arr[n]; + + if (val > n) + return false; + + LLVMGetSubtypes(ptrtype, arr); + ptrtype = arr[val]; + } + + if (!ptrtype) + return false; + + idx = LLVMGetOperand(gep, LLVMGetNumOperands(gep) - 1); + if (!LLVMIsConstant(idx)) + return false; + + val = LLVMConstIntGetZExtValue(idx); + + char *sname = NULL, *mname = NULL; + + if (dbginfo_struct_member(dbginfo, ptrtype, val, &sname, &mname)) { + fprintf(stderr, "%s: call to struct %s->%s\n", prefix, sname, + mname); + + json_object_object_add(js_call, "type", + json_object_new_string("struct_memb")); + json_object_object_add(js_call, "struct", + json_object_new_string(sname)); + json_object_object_add(js_call, "member", + json_object_new_string(mname)); + ret = true; + } + free(sname); + free(mname); + + return ret; +} + static bool details_fptr_vars = false; static bool details_fptr_consts = true; @@ -175,6 +243,34 @@ static void walk_const_fptrs(struct json_object *js_call, LLVMValueRef value, prefix, hdr_written); return; + case LLVMConstantExprValueKind: + switch (LLVMGetConstOpcode(value)) { + case LLVMGetElementPtr: + if (try_struct_fptr(js_call, value, prefix)) { + *hdr_written = true; + return; + } + + fprintf(stderr, + "%s: calls function pointer from unhandled const GEP\n", + prefix); + *hdr_written = true; + /* fallthru */ + default: + /* to help the user / development */ + if (!*hdr_written) { + fprintf(stderr, + "%s: calls function pointer from constexpr\n", + prefix); + *hdr_written = true; + } + dump = LLVMPrintValueToString(value); + fprintf(stderr, "%s- [opcode=%d] %s\n", prefix, + LLVMGetConstOpcode(value), dump); + LLVMDisposeMessage(dump); + } + return; + default: /* to help the user / development */ if (!*hdr_written) { @@ -280,6 +376,12 @@ static void process_call(struct json_object *js_calls, snprintf(prefix, sizeof(prefix), "%.*s:%d:%.*s()", (int)file_len, file, line, (int)name_len, name_c); + if (LLVMIsALoadInst(called) + && LLVMIsAGetElementPtrInst(LLVMGetOperand(called, 0)) + && try_struct_fptr(js_call, LLVMGetOperand(called, 0), + prefix)) + goto out_struct_fptr; + while (LLVMIsALoadInst(last) || LLVMIsAGetElementPtrInst(last)) /* skipping over details for GEP here, but meh. */ last = LLVMGetOperand(last, 0); @@ -330,7 +432,6 @@ static void process_call(struct json_object *js_calls, fprintf(stderr, "%s: ??? %s\n", prefix, dump); LLVMDisposeMessage(dump); } - return; #ifdef FRR_SPECIFIC } else if (!strcmp(called_name, "_install_element")) { called_type = FN_INSTALL_ELEMENT; @@ -440,6 +541,7 @@ static void process_call(struct json_object *js_calls, json_object_new_string_len(called_name, called_len)); } +out_struct_fptr: for (unsigned argno = 0; argno < n_args; argno++) { LLVMValueRef param = LLVMGetOperand(instr, argno); size_t target_len; @@ -597,6 +699,8 @@ int main(int argc, char **argv) // done with the memory buffer now, so dispose of it LLVMDisposeMemoryBuffer(memoryBuffer); + dbginfo = dbginfo_load(module); + struct json_object *js_root, *js_funcs, *js_special; js_root = json_object_new_object(); |