1 // SPDX-License-Identifier: GPL-2.0 2 #include "llvm.h" 3 #include "annotate.h" 4 #include "debug.h" 5 #include "dso.h" 6 #include "map.h" 7 #include "namespaces.h" 8 #include "srcline.h" 9 #include "symbol.h" 10 #include <fcntl.h> 11 #include <unistd.h> 12 #include <linux/zalloc.h> 13 14 #ifdef HAVE_LIBLLVM_SUPPORT 15 #include "llvm-c-helpers.h" 16 #include <llvm-c/Disassembler.h> 17 #include <llvm-c/Target.h> 18 #endif 19 20 #ifdef HAVE_LIBLLVM_SUPPORT 21 static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames, 22 int num_frames) 23 { 24 if (inline_frames != NULL) { 25 for (int i = 0; i < num_frames; ++i) { 26 zfree(&inline_frames[i].filename); 27 zfree(&inline_frames[i].funcname); 28 } 29 zfree(&inline_frames); 30 } 31 } 32 #endif 33 34 int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused, 35 char **file __maybe_unused, unsigned int *line __maybe_unused, 36 struct dso *dso __maybe_unused, bool unwind_inlines __maybe_unused, 37 struct inline_node *node __maybe_unused, struct symbol *sym __maybe_unused) 38 { 39 #ifdef HAVE_LIBLLVM_SUPPORT 40 struct llvm_a2l_frame *inline_frames = NULL; 41 int num_frames = llvm_addr2line(dso_name, addr, file, line, 42 node && unwind_inlines, &inline_frames); 43 44 if (num_frames == 0 || !inline_frames) { 45 /* Error, or we didn't want inlines. */ 46 return num_frames; 47 } 48 49 for (int i = 0; i < num_frames; ++i) { 50 struct symbol *inline_sym = 51 new_inline_sym(dso, sym, inline_frames[i].funcname); 52 char *srcline = NULL; 53 54 if (inline_frames[i].filename) { 55 srcline = 56 srcline_from_fileline(inline_frames[i].filename, 57 inline_frames[i].line); 58 } 59 if (inline_list__append(inline_sym, srcline, node) != 0) { 60 free_llvm_inline_frames(inline_frames, num_frames); 61 return 0; 62 } 63 } 64 free_llvm_inline_frames(inline_frames, num_frames); 65 66 return num_frames; 67 #else 68 return -1; 69 #endif 70 } 71 72 void dso__free_a2l_llvm(struct dso *dso __maybe_unused) 73 { 74 /* Nothing to free. */ 75 } 76 77 #ifdef HAVE_LIBLLVM_SUPPORT 78 static void init_llvm(void) 79 { 80 static bool init; 81 82 if (!init) { 83 LLVMInitializeAllTargetInfos(); 84 LLVMInitializeAllTargetMCs(); 85 LLVMInitializeAllDisassemblers(); 86 init = true; 87 } 88 } 89 90 struct find_file_offset_data { 91 u64 ip; 92 u64 offset; 93 }; 94 95 /* This will be called for each PHDR in an ELF binary */ 96 static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg) 97 { 98 struct find_file_offset_data *data = arg; 99 100 if (start <= data->ip && data->ip < start + len) { 101 data->offset = pgoff + data->ip - start; 102 return 1; 103 } 104 return 0; 105 } 106 107 static u8 * 108 read_symbol(const char *filename, struct map *map, struct symbol *sym, 109 u64 *len, bool *is_64bit) 110 { 111 struct dso *dso = map__dso(map); 112 struct nscookie nsc; 113 u64 start = map__rip_2objdump(map, sym->start); 114 u64 end = map__rip_2objdump(map, sym->end); 115 int fd, count; 116 u8 *buf = NULL; 117 struct find_file_offset_data data = { 118 .ip = start, 119 }; 120 121 *is_64bit = false; 122 123 nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); 124 fd = open(filename, O_RDONLY); 125 nsinfo__mountns_exit(&nsc); 126 if (fd < 0) 127 return NULL; 128 129 if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data, 130 is_64bit) == 0) 131 goto err; 132 133 *len = end - start; 134 buf = malloc(*len); 135 if (buf == NULL) 136 goto err; 137 138 count = pread(fd, buf, *len, data.offset); 139 close(fd); 140 fd = -1; 141 142 if ((u64)count != *len) 143 goto err; 144 145 return buf; 146 147 err: 148 if (fd >= 0) 149 close(fd); 150 free(buf); 151 return NULL; 152 } 153 #endif 154 155 /* 156 * Whenever LLVM wants to resolve an address into a symbol, it calls this 157 * callback. We don't ever actually _return_ anything (in particular, because 158 * it puts quotation marks around what we return), but we use this as a hint 159 * that there is a branch or PC-relative address in the expression that we 160 * should add some textual annotation for after the instruction. The caller 161 * will use this information to add the actual annotation. 162 */ 163 #ifdef HAVE_LIBLLVM_SUPPORT 164 struct symbol_lookup_storage { 165 u64 branch_addr; 166 u64 pcrel_load_addr; 167 }; 168 169 static const char * 170 symbol_lookup_callback(void *disinfo, uint64_t value, 171 uint64_t *ref_type, 172 uint64_t address __maybe_unused, 173 const char **ref __maybe_unused) 174 { 175 struct symbol_lookup_storage *storage = disinfo; 176 177 if (*ref_type == LLVMDisassembler_ReferenceType_In_Branch) 178 storage->branch_addr = value; 179 else if (*ref_type == LLVMDisassembler_ReferenceType_In_PCrel_Load) 180 storage->pcrel_load_addr = value; 181 *ref_type = LLVMDisassembler_ReferenceType_InOut_None; 182 return NULL; 183 } 184 #endif 185 186 int symbol__disassemble_llvm(const char *filename, struct symbol *sym, 187 struct annotate_args *args __maybe_unused) 188 { 189 #ifdef HAVE_LIBLLVM_SUPPORT 190 struct annotation *notes = symbol__annotation(sym); 191 struct map *map = args->ms.map; 192 struct dso *dso = map__dso(map); 193 u64 start = map__rip_2objdump(map, sym->start); 194 u8 *buf; 195 u64 len; 196 u64 pc; 197 bool is_64bit; 198 char disasm_buf[2048]; 199 size_t disasm_len; 200 struct disasm_line *dl; 201 LLVMDisasmContextRef disasm = NULL; 202 struct symbol_lookup_storage storage; 203 char *line_storage = NULL; 204 size_t line_storage_len = 0; 205 int ret = -1; 206 207 if (args->options->objdump_path) 208 return -1; 209 210 buf = read_symbol(filename, map, sym, &len, &is_64bit); 211 if (buf == NULL) 212 return -1; 213 214 init_llvm(); 215 if (arch__is(args->arch, "x86")) { 216 const char *triplet = is_64bit ? "x86_64-pc-linux" : "i686-pc-linux"; 217 218 disasm = LLVMCreateDisasm(triplet, &storage, /*tag_type=*/0, 219 /*get_op_info=*/NULL, symbol_lookup_callback); 220 } else { 221 char triplet[64]; 222 223 scnprintf(triplet, sizeof(triplet), "%s-linux-gnu", 224 args->arch->name); 225 disasm = LLVMCreateDisasm(triplet, &storage, /*tag_type=*/0, 226 /*get_op_info=*/NULL, symbol_lookup_callback); 227 } 228 229 if (disasm == NULL) 230 goto err; 231 232 if (args->options->disassembler_style && 233 !strcmp(args->options->disassembler_style, "intel")) 234 LLVMSetDisasmOptions(disasm, 235 LLVMDisassembler_Option_AsmPrinterVariant); 236 237 /* 238 * This needs to be set after AsmPrinterVariant, due to a bug in LLVM; 239 * setting AsmPrinterVariant makes a new instruction printer, making it 240 * forget about the PrintImmHex flag (which is applied before if both 241 * are given to the same call). 242 */ 243 LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex); 244 245 /* add the function address and name */ 246 scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:", 247 start, sym->name); 248 249 args->offset = -1; 250 args->line = disasm_buf; 251 args->line_nr = 0; 252 args->fileloc = NULL; 253 args->ms.sym = sym; 254 255 dl = disasm_line__new(args); 256 if (dl == NULL) 257 goto err; 258 259 annotation_line__add(&dl->al, ¬es->src->source); 260 261 pc = start; 262 for (u64 offset = 0; offset < len; ) { 263 unsigned int ins_len; 264 265 storage.branch_addr = 0; 266 storage.pcrel_load_addr = 0; 267 268 ins_len = LLVMDisasmInstruction(disasm, buf + offset, 269 len - offset, pc, 270 disasm_buf, sizeof(disasm_buf)); 271 if (ins_len == 0) 272 goto err; 273 disasm_len = strlen(disasm_buf); 274 275 if (storage.branch_addr != 0) { 276 char *name = llvm_name_for_code(dso, filename, 277 storage.branch_addr); 278 if (name != NULL) { 279 disasm_len += scnprintf(disasm_buf + disasm_len, 280 sizeof(disasm_buf) - 281 disasm_len, 282 " <%s>", name); 283 free(name); 284 } 285 } 286 if (storage.pcrel_load_addr != 0) { 287 char *name = llvm_name_for_data(dso, filename, 288 storage.pcrel_load_addr); 289 disasm_len += scnprintf(disasm_buf + disasm_len, 290 sizeof(disasm_buf) - disasm_len, 291 " # %#"PRIx64, 292 storage.pcrel_load_addr); 293 if (name) { 294 disasm_len += scnprintf(disasm_buf + disasm_len, 295 sizeof(disasm_buf) - 296 disasm_len, 297 " <%s>", name); 298 free(name); 299 } 300 } 301 302 args->offset = offset; 303 args->line = expand_tabs(disasm_buf, &line_storage, 304 &line_storage_len); 305 args->line_nr = 0; 306 args->fileloc = NULL; 307 args->ms.sym = sym; 308 309 llvm_addr2line(filename, pc, &args->fileloc, 310 (unsigned int *)&args->line_nr, false, NULL); 311 312 dl = disasm_line__new(args); 313 if (dl == NULL) 314 goto err; 315 316 annotation_line__add(&dl->al, ¬es->src->source); 317 318 free(args->fileloc); 319 pc += ins_len; 320 offset += ins_len; 321 } 322 323 ret = 0; 324 325 err: 326 LLVMDisasmDispose(disasm); 327 free(buf); 328 free(line_storage); 329 return ret; 330 #else // HAVE_LIBLLVM_SUPPORT 331 pr_debug("The LLVM disassembler isn't linked in for %s in %s\n", 332 sym->name, filename); 333 return -1; 334 #endif 335 } 336