at master 5.2 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2 3/* 4 * Must come before the linux/compiler.h include, which defines several 5 * macros (e.g. noinline) that conflict with compiler builtins used 6 * by LLVM. 7 */ 8#pragma GCC diagnostic push 9#pragma GCC diagnostic ignored "-Wunused-parameter" /* Needed for LLVM <= 15 */ 10#include <llvm/DebugInfo/Symbolize/Symbolize.h> 11#include <llvm/Support/TargetSelect.h> 12#pragma GCC diagnostic pop 13 14#include <inttypes.h> 15#include <stdio.h> 16#include <sys/types.h> 17#include <linux/compiler.h> 18extern "C" { 19#include <linux/zalloc.h> 20} 21#include "llvm-c-helpers.h" 22 23extern "C" 24char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name); 25 26using namespace llvm; 27using llvm::symbolize::LLVMSymbolizer; 28 29/* 30 * Allocate a static LLVMSymbolizer, which will live to the end of the program. 31 * Unlike the bfd paths, LLVMSymbolizer has its own cache, so we do not need 32 * to store anything in the dso struct. 33 */ 34static LLVMSymbolizer *get_symbolizer() 35{ 36 static LLVMSymbolizer *instance = nullptr; 37 if (instance == nullptr) { 38 LLVMSymbolizer::Options opts; 39 /* 40 * LLVM sometimes demangles slightly different from the rest 41 * of the code, and this mismatch can cause new_inline_sym() 42 * to get confused and mark non-inline symbol as inlined 43 * (since the name does not properly match up with base_sym). 44 * Thus, disable the demangling and let the rest of the code 45 * handle it. 46 */ 47 opts.Demangle = false; 48 instance = new LLVMSymbolizer(opts); 49 } 50 return instance; 51} 52 53/* Returns 0 on error, 1 on success. */ 54static int extract_file_and_line(const DILineInfo &line_info, char **file, 55 unsigned int *line) 56{ 57 if (file) { 58 if (line_info.FileName == "<invalid>") { 59 /* Match the convention of libbfd. */ 60 *file = nullptr; 61 } else { 62 /* The caller expects to get something it can free(). */ 63 *file = strdup(line_info.FileName.c_str()); 64 if (*file == nullptr) 65 return 0; 66 } 67 } 68 if (line) 69 *line = line_info.Line; 70 return 1; 71} 72 73extern "C" 74int llvm_addr2line(const char *dso_name, u64 addr, 75 char **file, unsigned int *line, 76 bool unwind_inlines, 77 llvm_a2l_frame **inline_frames) 78{ 79 LLVMSymbolizer *symbolizer = get_symbolizer(); 80 object::SectionedAddress sectioned_addr = { 81 addr, 82 object::SectionedAddress::UndefSection 83 }; 84 85 if (unwind_inlines) { 86 Expected<DIInliningInfo> res_or_err = 87 symbolizer->symbolizeInlinedCode(dso_name, 88 sectioned_addr); 89 if (!res_or_err) 90 return 0; 91 unsigned num_frames = res_or_err->getNumberOfFrames(); 92 if (num_frames == 0) 93 return 0; 94 95 if (extract_file_and_line(res_or_err->getFrame(0), 96 file, line) == 0) 97 return 0; 98 99 *inline_frames = (llvm_a2l_frame *)calloc( 100 num_frames, sizeof(**inline_frames)); 101 if (*inline_frames == nullptr) 102 return 0; 103 104 for (unsigned i = 0; i < num_frames; ++i) { 105 const DILineInfo &src = res_or_err->getFrame(i); 106 107 llvm_a2l_frame &dst = (*inline_frames)[i]; 108 if (src.FileName == "<invalid>") 109 /* Match the convention of libbfd. */ 110 dst.filename = nullptr; 111 else 112 dst.filename = strdup(src.FileName.c_str()); 113 dst.funcname = strdup(src.FunctionName.c_str()); 114 dst.line = src.Line; 115 116 if (dst.filename == nullptr || 117 dst.funcname == nullptr) { 118 for (unsigned j = 0; j <= i; ++j) { 119 zfree(&(*inline_frames)[j].filename); 120 zfree(&(*inline_frames)[j].funcname); 121 } 122 zfree(inline_frames); 123 return 0; 124 } 125 } 126 127 return num_frames; 128 } else { 129 if (inline_frames) 130 *inline_frames = nullptr; 131 132 Expected<DILineInfo> res_or_err = 133 symbolizer->symbolizeCode(dso_name, sectioned_addr); 134 if (!res_or_err) 135 return 0; 136 return extract_file_and_line(*res_or_err, file, line); 137 } 138} 139 140static char * 141make_symbol_relative_string(struct dso *dso, const char *sym_name, 142 u64 addr, u64 base_addr) 143{ 144 if (!strcmp(sym_name, "<invalid>")) 145 return NULL; 146 147 char *demangled = dso__demangle_sym(dso, 0, sym_name); 148 if (base_addr && base_addr != addr) { 149 char buf[256]; 150 snprintf(buf, sizeof(buf), "%s+0x%" PRIx64, 151 demangled ? demangled : sym_name, addr - base_addr); 152 free(demangled); 153 return strdup(buf); 154 } else { 155 if (demangled) 156 return demangled; 157 else 158 return strdup(sym_name); 159 } 160} 161 162extern "C" 163char *llvm_name_for_code(struct dso *dso, const char *dso_name, u64 addr) 164{ 165 LLVMSymbolizer *symbolizer = get_symbolizer(); 166 object::SectionedAddress sectioned_addr = { 167 addr, 168 object::SectionedAddress::UndefSection 169 }; 170 Expected<DILineInfo> res_or_err = 171 symbolizer->symbolizeCode(dso_name, sectioned_addr); 172 if (!res_or_err) { 173 return NULL; 174 } 175 return make_symbol_relative_string( 176 dso, res_or_err->FunctionName.c_str(), 177 addr, res_or_err->StartAddress ? *res_or_err->StartAddress : 0); 178} 179 180extern "C" 181char *llvm_name_for_data(struct dso *dso, const char *dso_name, u64 addr) 182{ 183 LLVMSymbolizer *symbolizer = get_symbolizer(); 184 object::SectionedAddress sectioned_addr = { 185 addr, 186 object::SectionedAddress::UndefSection 187 }; 188 Expected<DIGlobal> res_or_err = 189 symbolizer->symbolizeData(dso_name, sectioned_addr); 190 if (!res_or_err) { 191 return NULL; 192 } 193 return make_symbol_relative_string( 194 dso, res_or_err->Name.c_str(), 195 addr, res_or_err->Start); 196}