196 lines · 5.0 KB · C++
RawDownload
// SPDX-License-Identifier: GPL-2.0 /* * Must come before the linux/compiler.h include, which defines several * macros (e.g. noinline) that conflict with compiler builtins used * by LLVM. */#pragma GCC diagnostic push#pragma GCC diagnostic ignored "-Wunused-parameter"  /* Needed for LLVM <= 15 */#include <llvm/DebugInfo/Symbolize/Symbolize.h>#include <llvm/Support/TargetSelect.h>#pragma GCC diagnostic pop #include <inttypes.h>#include <stdio.h>#include <sys/types.h>#include <linux/compiler.h>extern "C" {#include <linux/zalloc.h>}#include "llvm-c-helpers.h" extern "C"char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name); using namespace llvm;using llvm::symbolize::LLVMSymbolizer; /* * Allocate a static LLVMSymbolizer, which will live to the end of the program. * Unlike the bfd paths, LLVMSymbolizer has its own cache, so we do not need * to store anything in the dso struct. */static LLVMSymbolizer *get_symbolizer(){	static LLVMSymbolizer *instance = nullptr;	if (instance == nullptr) {		LLVMSymbolizer::Options opts;		/*		 * LLVM sometimes demangles slightly different from the rest		 * of the code, and this mismatch can cause new_inline_sym()		 * to get confused and mark non-inline symbol as inlined		 * (since the name does not properly match up with base_sym).		 * Thus, disable the demangling and let the rest of the code		 * handle it.		 */		opts.Demangle = false;		instance = new LLVMSymbolizer(opts);	}	return instance;} /* Returns 0 on error, 1 on success. */static int extract_file_and_line(const DILineInfo &line_info, char **file,				 unsigned int *line){	if (file) {		if (line_info.FileName == "<invalid>") {			/* Match the convention of libbfd. */			*file = nullptr;		} else {			/* The caller expects to get something it can free(). */			*file = strdup(line_info.FileName.c_str());			if (*file == nullptr)				return 0;		}	}	if (line)		*line = line_info.Line;	return 1;} extern "C"int llvm_addr2line(const char *dso_name, u64 addr,		   char **file, unsigned int *line,		   bool unwind_inlines,		   llvm_a2l_frame **inline_frames){	LLVMSymbolizer *symbolizer = get_symbolizer();	object::SectionedAddress sectioned_addr = {		addr,		object::SectionedAddress::UndefSection	}; 	if (unwind_inlines) {		Expected<DIInliningInfo> res_or_err =			symbolizer->symbolizeInlinedCode(dso_name,							 sectioned_addr);		if (!res_or_err)			return 0;		unsigned num_frames = res_or_err->getNumberOfFrames();		if (num_frames == 0)			return 0; 		if (extract_file_and_line(res_or_err->getFrame(0),					  file, line) == 0)			return 0; 		*inline_frames = (llvm_a2l_frame *)calloc(			num_frames, sizeof(**inline_frames));		if (*inline_frames == nullptr)			return 0; 		for (unsigned i = 0; i < num_frames; ++i) {			const DILineInfo &src = res_or_err->getFrame(i); 			llvm_a2l_frame &dst = (*inline_frames)[i];			if (src.FileName == "<invalid>")				/* Match the convention of libbfd. */				dst.filename = nullptr;			else				dst.filename = strdup(src.FileName.c_str());			dst.funcname = strdup(src.FunctionName.c_str());			dst.line = src.Line; 			if (dst.filename == nullptr ||			    dst.funcname == nullptr) {				for (unsigned j = 0; j <= i; ++j) {					zfree(&(*inline_frames)[j].filename);					zfree(&(*inline_frames)[j].funcname);				}				zfree(inline_frames);				return 0;			}		} 		return num_frames;	} else {		if (inline_frames)			*inline_frames = nullptr; 		Expected<DILineInfo> res_or_err =			symbolizer->symbolizeCode(dso_name, sectioned_addr);		if (!res_or_err)			return 0;		return extract_file_and_line(*res_or_err, file, line);	}} static char *make_symbol_relative_string(struct dso *dso, const char *sym_name,			    u64 addr, u64 base_addr){	if (!strcmp(sym_name, "<invalid>"))		return NULL; 	char *demangled = dso__demangle_sym(dso, 0, sym_name);	if (base_addr && base_addr != addr) {		char buf[256];		snprintf(buf, sizeof(buf), "%s+0x%" PRIx64,			 demangled ? demangled : sym_name, addr - base_addr);		free(demangled);		return strdup(buf);	} else {		if (demangled)			return demangled;		else			return strdup(sym_name);	}} extern "C"char *llvm_name_for_code(struct dso *dso, const char *dso_name, u64 addr){	LLVMSymbolizer *symbolizer = get_symbolizer();	object::SectionedAddress sectioned_addr = {		addr,		object::SectionedAddress::UndefSection	};	Expected<DILineInfo> res_or_err =		symbolizer->symbolizeCode(dso_name, sectioned_addr);	if (!res_or_err) {		return NULL;	}	return make_symbol_relative_string(		dso, res_or_err->FunctionName.c_str(),		addr, res_or_err->StartAddress ? *res_or_err->StartAddress : 0);} extern "C"char *llvm_name_for_data(struct dso *dso, const char *dso_name, u64 addr){	LLVMSymbolizer *symbolizer = get_symbolizer();	object::SectionedAddress sectioned_addr = {		addr,		object::SectionedAddress::UndefSection	};	Expected<DIGlobal> res_or_err =		symbolizer->symbolizeData(dso_name, sectioned_addr);	if (!res_or_err) {		return NULL;	}	return make_symbol_relative_string(		dso, res_or_err->Name.c_str(),		addr, res_or_err->Start);}