diff options
Diffstat (limited to 'tools/frr-llvm-cg.c')
| -rw-r--r-- | tools/frr-llvm-cg.c | 347 |
1 files changed, 339 insertions, 8 deletions
diff --git a/tools/frr-llvm-cg.c b/tools/frr-llvm-cg.c index 84a756a376..fbcf9222d2 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) { @@ -197,7 +293,7 @@ static void walk_const_fptrs(struct json_object *js_call, LLVMValueRef value, #ifdef FRR_SPECIFIC static bool is_thread_sched(const char *name, size_t len) { -#define thread_prefix "funcname_" +#define thread_prefix "_" static const char *const names[] = { thread_prefix "thread_add_read_write", thread_prefix "thread_add_timer", @@ -218,6 +314,225 @@ static bool is_thread_sched(const char *name, size_t len) } #endif +static bool _check_val(bool cond, const char *text, LLVMValueRef dumpval) +{ + if (cond) + return true; + + char *dump = LLVMPrintValueToString(dumpval); + fprintf(stderr, "check failed: %s\ndump:\n\t%s\n", text, dump); + LLVMDisposeMessage(dump); + return false; +} + +#define check_val(cond, dump) \ + if (!_check_val(cond, #cond, dump)) \ + return; + +static char *get_string(LLVMValueRef value) +{ + if (!LLVMIsAConstant(value)) + return strdup("!NOT-A-CONST"); + + if (LLVMGetValueKind(value) == LLVMConstantExprValueKind + && LLVMGetConstOpcode(value) == LLVMGetElementPtr) { + value = LLVMGetOperand(value, 0); + + if (!LLVMIsAConstant(value)) + return strdup("!NOT-A-CONST-2"); + } + + if (LLVMIsAGlobalVariable(value)) + value = LLVMGetInitializer(value); + + size_t len = 0; + const char *sval = LLVMGetAsString(value, &len); + + return strndup(sval, len); +} + +static void handle_yang_module(struct json_object *js_special, + LLVMValueRef yang_mod) +{ + check_val(LLVMIsAGlobalVariable(yang_mod), yang_mod); + + LLVMValueRef value; + + value = LLVMGetInitializer(yang_mod); + LLVMValueKind kind = LLVMGetValueKind(value); + + check_val(kind == LLVMConstantStructValueKind, value); + + size_t var_len = 0; + const char *var_name = LLVMGetValueName2(yang_mod, &var_len); + char buf_name[var_len + 1]; + + memcpy(buf_name, var_name, var_len); + buf_name[var_len] = '\0'; + + struct json_object *js_yang, *js_yangmod, *js_items; + + js_yang = js_get_or_make(js_special, "yang", json_object_new_object); + js_yangmod = js_get_or_make(js_yang, buf_name, json_object_new_object); + js_items = js_get_or_make(js_yangmod, "items", json_object_new_array); + + char *mod_name = get_string(LLVMGetOperand(value, 0)); + json_object_object_add(js_yangmod, "name", + json_object_new_string(mod_name)); + free(mod_name); + + value = LLVMGetOperand(value, 1); + kind = LLVMGetValueKind(value); + check_val(kind == LLVMConstantArrayValueKind, value); + + unsigned len = LLVMGetArrayLength(LLVMTypeOf(value)); + + for (unsigned i = 0; i < len - 1; i++) { + struct json_object *js_item, *js_cbs; + LLVMValueRef item = LLVMGetOperand(value, i); + char *xpath = get_string(LLVMGetOperand(item, 0)); + + js_item = json_object_new_object(); + json_object_array_add(js_items, js_item); + + json_object_object_add(js_item, "xpath", + json_object_new_string(xpath)); + js_cbs = js_get_or_make(js_item, "cbs", json_object_new_object); + + free(xpath); + + LLVMValueRef cbs = LLVMGetOperand(item, 1); + + check_val(LLVMGetValueKind(cbs) == LLVMConstantStructValueKind, + value); + + LLVMTypeRef cbs_type = LLVMTypeOf(cbs); + unsigned cblen = LLVMCountStructElementTypes(cbs_type); + + for (unsigned i = 0; i < cblen; i++) { + LLVMValueRef cb = LLVMGetOperand(cbs, i); + + char *sname = NULL; + char *mname = NULL; + + if (dbginfo_struct_member(dbginfo, cbs_type, i, &sname, + &mname)) { + (void)0; + } + + if (LLVMIsAFunction(cb)) { + size_t fn_len; + const char *fn_name; + + fn_name = LLVMGetValueName2(cb, &fn_len); + + json_object_object_add( + js_cbs, mname, + json_object_new_string_len(fn_name, + fn_len)); + } + + free(sname); + free(mname); + } + } +} + +static void handle_daemoninfo(struct json_object *js_special, + LLVMValueRef daemoninfo) +{ + check_val(LLVMIsAGlobalVariable(daemoninfo), daemoninfo); + + LLVMTypeRef type; + LLVMValueRef value; + unsigned len; + + type = LLVMGlobalGetValueType(daemoninfo); + value = LLVMGetInitializer(daemoninfo); + LLVMValueKind kind = LLVMGetValueKind(value); + + check_val(kind == LLVMConstantStructValueKind, value); + + int yang_idx = -1; + + len = LLVMCountStructElementTypes(type); + + LLVMTypeRef fieldtypes[len]; + LLVMGetSubtypes(type, fieldtypes); + + for (unsigned i = 0; i < len; i++) { + LLVMTypeRef t = fieldtypes[i]; + + if (LLVMGetTypeKind(t) != LLVMPointerTypeKind) + continue; + t = LLVMGetElementType(t); + if (LLVMGetTypeKind(t) != LLVMPointerTypeKind) + continue; + t = LLVMGetElementType(t); + if (LLVMGetTypeKind(t) != LLVMStructTypeKind) + continue; + + const char *name = LLVMGetStructName(t); + if (!strcmp(name, "struct.frr_yang_module_info")) + yang_idx = i; + } + + if (yang_idx == -1) + return; + + LLVMValueRef yang_mods = LLVMGetOperand(value, yang_idx); + LLVMValueRef yang_size = LLVMGetOperand(value, yang_idx + 1); + + check_val(LLVMIsConstant(yang_size), yang_size); + + unsigned long long ival = LLVMConstIntGetZExtValue(yang_size); + + check_val(LLVMGetValueKind(yang_mods) == LLVMConstantExprValueKind + && LLVMGetConstOpcode(yang_mods) == LLVMGetElementPtr, + yang_mods); + + yang_mods = LLVMGetOperand(yang_mods, 0); + + check_val(LLVMIsAGlobalVariable(yang_mods), yang_mods); + + yang_mods = LLVMGetInitializer(yang_mods); + + check_val(LLVMGetValueKind(yang_mods) == LLVMConstantArrayValueKind, + yang_mods); + + len = LLVMGetArrayLength(LLVMTypeOf(yang_mods)); + + if (len != ival) + fprintf(stderr, "length mismatch - %llu vs. %u\n", ival, len); + + for (unsigned i = 0; i < len; i++) { + char *dump; + + LLVMValueRef item = LLVMGetOperand(yang_mods, i); + LLVMValueKind kind = LLVMGetValueKind(item); + + check_val(kind == LLVMGlobalVariableValueKind + || kind == LLVMConstantExprValueKind, + item); + + if (kind == LLVMGlobalVariableValueKind) + continue; + + LLVMOpcode opcode = LLVMGetConstOpcode(item); + switch (opcode) { + case LLVMBitCast: + item = LLVMGetOperand(item, 0); + handle_yang_module(js_special, item); + break; + + default: + dump = LLVMPrintValueToString(item); + printf("[%u] = [opcode=%u] %s\n", i, opcode, dump); + LLVMDisposeMessage(dump); + } + } +} + static void process_call(struct json_object *js_calls, struct json_object *js_special, LLVMValueRef instr, @@ -227,6 +542,9 @@ static void process_call(struct json_object *js_calls, LLVMValueRef called = LLVMGetCalledValue(instr); + if (LLVMIsAInlineAsm(called)) + return; + if (LLVMIsAConstantExpr(called)) { LLVMOpcode opcode = LLVMGetConstOpcode(called); @@ -277,6 +595,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); @@ -324,12 +648,11 @@ static void process_call(struct json_object *js_calls, prefix); } else { char *dump = LLVMPrintValueToString(called); - printf("\t%s\n", dump); + fprintf(stderr, "%s: ??? %s\n", prefix, dump); LLVMDisposeMessage(dump); } - return; #ifdef FRR_SPECIFIC - } else if (!strcmp(called_name, "install_element")) { + } else if (!strcmp(called_name, "_install_element")) { called_type = FN_INSTALL_ELEMENT; LLVMValueRef param0 = LLVMGetOperand(instr, 0); @@ -380,10 +703,7 @@ static void process_call(struct json_object *js_calls, json_object_new_string_len(called_name, called_len)); LLVMValueRef fparam; - if (strstr(called_name, "_read_")) - fparam = LLVMGetOperand(instr, 2); - else - fparam = LLVMGetOperand(instr, 1); + fparam = LLVMGetOperand(instr, 2); assert(fparam); size_t target_len = 0; @@ -434,12 +754,21 @@ static void process_call(struct json_object *js_calls, * - zclient->* ? */ #endif /* FRR_SPECIFIC */ + } else if (!strcmp(called_name, "frr_preinit")) { + LLVMValueRef daemoninfo = LLVMGetOperand(instr, 0); + + handle_daemoninfo(js_special, daemoninfo); + + json_object_object_add( + js_call, "target", + json_object_new_string_len(called_name, called_len)); } else { json_object_object_add( js_call, "target", 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 +926,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(); |
