]> git.proxmox.com Git - mirror_frr.git/blobdiff - tools/frr-llvm-cg.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / tools / frr-llvm-cg.c
index 84a756a376bada8e41cb8d3b2f71acedba3789a7..51e8fb7b7ec1be0fd085ccf823ed0fdb1dcd54f7 100644 (file)
@@ -1,28 +1,4 @@
-// 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;
@@ -85,6 +65,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 +219,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 +269,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 +290,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 +518,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);
 
@@ -243,7 +537,6 @@ static void process_call(struct json_object *js_calls,
        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);
@@ -252,7 +545,6 @@ static void process_call(struct json_object *js_calls,
                               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"));
 
@@ -277,6 +569,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,14 +622,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")) {
-               called_type = FN_INSTALL_ELEMENT;
-
+       } else if (!strcmp(called_name, "_install_element")) {
                LLVMValueRef param0 = LLVMGetOperand(instr, 0);
                if (!LLVMIsAConstantInt(param0))
                        goto out_nonconst;
@@ -371,8 +666,6 @@ static void process_call(struct json_object *js_calls,
                        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(
@@ -380,10 +673,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 +724,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 +896,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();