-// This is free and unencumbered software released into the public domain.
-//
-// Anyone is free to copy, modify, publish, use, compile, sell, or
-// distribute this software, either in source code form or as a compiled
-// binary, for any purpose, commercial or non-commercial, and by any
-// means.
-//
-// In jurisdictions that recognize copyright laws, the author or authors
-// of this software dedicate any and all copyright interest in the
-// software to the public domain. We make this dedication for the benefit
-// of the public at large and to the detriment of our heirs and
-// successors. We intend this dedication to be an overt act of
-// relinquishment in perpetuity of all present and future rights to this
-// software under copyright law.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-// OTHER DEALINGS IN THE SOFTWARE.
-//
-// For more information, please refer to <http://unlicense.org/>
-
+// SPDX-License-Identifier: Unlicense
/* based on example code: https://github.com/sheredom/llvm_bc_parsing_example
* which came under the above (un-)license. does not depend on any FRR
* pieces, so no reason to change the license.
#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;
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;
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) {
#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",
}
#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,
LLVMValueRef called = LLVMGetCalledValue(instr);
+ if (LLVMIsAInlineAsm(called))
+ return;
+
if (LLVMIsAConstantExpr(called)) {
LLVMOpcode opcode = LLVMGetConstOpcode(called);
unsigned n_args = LLVMGetNumArgOperands(instr);
bool is_external = LLVMIsDeclaration(called);
- enum called_fn called_type = FN_GENERIC;
js_call = json_object_new_object();
json_object_array_add(js_calls, js_call);
json_object_new_boolean(is_external));
if (!called_name || called_len == 0) {
- called_type = FN_NONAME;
json_object_object_add(js_call, "type",
json_object_new_string("indirect"));
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);
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")) {
- called_type = FN_INSTALL_ELEMENT;
-
+ } else if (!strcmp(called_name, "_install_element")) {
LLVMValueRef param0 = LLVMGetOperand(instr, 0);
if (!LLVMIsAConstantInt(param0))
goto out_nonconst;
json_object_new_string("install_element"));
return;
} else if (is_thread_sched(called_name, called_len)) {
- called_type = FN_THREAD_ADD;
-
json_object_object_add(js_call, "type",
json_object_new_string("thread_sched"));
json_object_object_add(
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;
* - 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;
// 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();