diff options
| author | Quentin Young <qlyoung@users.noreply.github.com> | 2021-06-01 19:08:32 +0000 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-06-01 19:08:32 +0000 | 
| commit | c99313762a9663ad3749eca2c51a44c546a93fe7 (patch) | |
| tree | 6fca73b00e6b737c794b1467025698ab2e4e8362 | |
| parent | 465d4a6c5b341de72fdd2a7de6458fc3968cf5cd (diff) | |
| parent | b0b14dfdd13963ab07889602d132ac218ce8526c (diff) | |
Merge pull request #8353 from opensourcerouting/llvm-20210327
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | lib/prefix.h | 9 | ||||
| -rw-r--r-- | tools/.gitignore | 1 | ||||
| -rw-r--r-- | tools/frr-llvm-cg.c | 347 | ||||
| -rw-r--r-- | tools/frr-llvm-debuginfo.cpp | 112 | ||||
| -rw-r--r-- | tools/frr-llvm-debuginfo.h | 47 | ||||
| -rw-r--r-- | tools/subdir.am | 6 | 
7 files changed, 514 insertions, 10 deletions
diff --git a/.gitignore b/.gitignore index 4e120dae85..33d03296db 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/lib/prefix.h b/lib/prefix.h index d7ee1b8e4c..217a23d561 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -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 diff --git a/tools/.gitignore b/tools/.gitignore index 63a5b61c35..1cc343a11a 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -8,3 +8,4 @@  /frrcommon.sh  /frr.service  /frr@.service +/frr-llvm-cg 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(); diff --git a/tools/frr-llvm-debuginfo.cpp b/tools/frr-llvm-debuginfo.cpp new file mode 100644 index 0000000000..ed3ad956b8 --- /dev/null +++ b/tools/frr-llvm-debuginfo.cpp @@ -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; +} diff --git a/tools/frr-llvm-debuginfo.h b/tools/frr-llvm-debuginfo.h new file mode 100644 index 0000000000..fca4bc1f97 --- /dev/null +++ b/tools/frr-llvm-debuginfo.h @@ -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 */ diff --git a/tools/subdir.am b/tools/subdir.am index 6a03a23baa..e4b9ecd84f 100644 --- a/tools/subdir.am +++ b/tools/subdir.am @@ -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 += \  | 
