Merge pull request #8353 from opensourcerouting/llvm-20210327

This commit is contained in:
Quentin Young 2021-06-01 19:08:32 +00:00 committed by GitHub
commit c99313762a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 514 additions and 10 deletions

2
.gitignore vendored
View file

@ -30,6 +30,7 @@
/libtool.orig
/changelog-auto
/test-driver
/test-suite.log
/Makefile
/Makefile.in
@ -57,6 +58,7 @@
*.pb.cc
*_clippy.c
*.bc
*.ll
*.cg.json
*.cg.dot
*.cg.svg

View file

@ -315,10 +315,12 @@ struct prefix_sg {
#ifndef __cplusplus
#define prefixtype(uname, typename, fieldname) \
typename *fieldname;
#define TRANSPARENT_UNION __attribute__((transparent_union))
#else
#define prefixtype(uname, typename, fieldname) \
typename *fieldname; \
uname(typename *x) { this->fieldname = x; }
#define TRANSPARENT_UNION
#endif
union prefixptr {
@ -328,7 +330,7 @@ union prefixptr {
prefixtype(prefixptr, struct prefix_evpn, evp)
prefixtype(prefixptr, struct prefix_fs, fs)
prefixtype(prefixptr, struct prefix_rd, rd)
} __attribute__((transparent_union));
} TRANSPARENT_UNION;
union prefixconstptr {
prefixtype(prefixconstptr, const struct prefix, p)
@ -337,7 +339,10 @@ union prefixconstptr {
prefixtype(prefixconstptr, const struct prefix_evpn, evp)
prefixtype(prefixconstptr, const struct prefix_fs, fs)
prefixtype(prefixconstptr, const struct prefix_rd, rd)
} __attribute__((transparent_union));
} TRANSPARENT_UNION;
#undef prefixtype
#undef TRANSPARENT_UNION
#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16

1
tools/.gitignore vendored
View file

@ -8,3 +8,4 @@
/frrcommon.sh
/frr.service
/frr@.service
/frr-llvm-cg

View file

@ -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();

View file

@ -0,0 +1,112 @@
// 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/>
#include <llvm-c/BitReader.h>
#include <llvm-c/BitWriter.h>
#include <llvm-c/Core.h>
#include <llvm-c/DebugInfo.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Value.h>
#include <llvm/IR/Type.h>
#include <llvm/IR/DebugInfo.h>
#include <llvm/IR/DebugInfoMetadata.h>
#include <llvm/Support/raw_ostream.h>
#include <map>
#include "frr-llvm-debuginfo.h"
/* llvm::DebugInfoFinder is unfortunately not exposed in the llvm-c API... */
struct dbginfo {
llvm::DebugInfoFinder finder;
std::map<std::string, llvm::DICompositeType *> tab;
};
struct dbginfo *dbginfo_load(LLVMModuleRef _mod)
{
llvm::Module *mod = llvm::unwrap(_mod);
struct dbginfo *info = new dbginfo();
info->finder.processModule(*mod);
for (auto ty : info->finder.types()) {
if (ty->getMetadataID() != llvm::Metadata::DICompositeTypeKind)
continue;
llvm::DICompositeType *cty = (llvm::DICompositeType *)ty;
/* empty forward declarations aka "struct foobar;" */
if (cty->getElements().size() == 0)
continue;
info->tab.emplace(std::move(ty->getName().str()), cty);
}
return info;
}
bool dbginfo_struct_member(struct dbginfo *info, LLVMTypeRef _typ,
unsigned long long idx, char **struct_name,
char **member_name)
{
*struct_name = NULL;
*member_name = NULL;
llvm::Type *typ = llvm::unwrap(_typ);
if (!typ->isStructTy())
return false;
llvm::StructType *styp = (llvm::StructType *)typ;
auto sname = styp->getStructName();
if (!sname.startswith("struct."))
return false;
sname = sname.drop_front(7);
size_t dot = sname.find_last_of(".");
if (dot != sname.npos)
sname = sname.take_front(dot);
auto item = info->tab.find(sname.str());
if (item == info->tab.end())
return false;
auto elements = item->second->getElements();
if (idx >= elements.size())
return false;
auto elem = elements[idx];
if (elem->getMetadataID() != llvm::Metadata::DIDerivedTypeKind)
return false;
llvm::DIDerivedType *dtyp = (llvm::DIDerivedType *)elem;
*struct_name = strdup(sname.str().c_str());
*member_name = strdup(dtyp->getName().str().c_str());
return true;
}

View file

@ -0,0 +1,47 @@
// 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/>
#ifndef _FRR_LLVM_DEBUGINFO_H
#define _FRR_LLVM_DEBUGINFO_H
#include <stdbool.h>
#include <llvm-c/Core.h>
#ifdef __cplusplus
extern "C" {
#endif
struct dbginfo;
extern struct dbginfo *dbginfo_load(LLVMModuleRef mod);
extern bool dbginfo_struct_member(struct dbginfo *di, LLVMTypeRef typ,
unsigned long long idx, char **struct_name,
char **member_name);
#ifdef __cplusplus
}
#endif
#endif /* _FRR_LLVM_DEBUGINFO_H */

View file

@ -40,9 +40,15 @@ tools_ssd_CPPFLAGS =
llvm_version = $(shell echo __clang_major__ | $(CC) -xc -P -E -)
tools_frr_llvm_cg_CPPFLAGS = $(CPPFLAGS_BASE)
tools_frr_llvm_cg_CFLAGS = $(AM_CFLAGS) `llvm-config-$(llvm_version) --cflags`
tools_frr_llvm_cg_CXXFLAGS = $(AM_CXXFLAGS) -O0 -ggdb3 `llvm-config-$(llvm_version) --cxxflags`
tools_frr_llvm_cg_LDFLAGS = `llvm-config-$(llvm_version) --ldflags --libs`
tools_frr_llvm_cg_SOURCES = \
tools/frr-llvm-cg.c \
tools/frr-llvm-debuginfo.cpp \
# end
noinst_HEADERS += \
tools/frr-llvm-debuginfo.h \
# end
EXTRA_DIST += \