tools/perf/util/llvm.c

Source file repositories/reference/linux-study-clean/tools/perf/util/llvm.c

File Facts

System
Linux kernel
Corpus path
tools/perf/util/llvm.c
Extension
.c
Size
7438 bytes
Lines
280
Domain
Support Tooling And Documentation
Bucket
tools
Inferred role
Support Tooling And Documentation: implementation source
Status
source implementation candidate

Why This File Exists

Repository support layer: documentation, build tooling, samples, user-space helper tools, generated initramfs support, licenses, and validation utilities.

Dependency Surface

Detected Declarations

Annotated Snippet

struct symbol_lookup_storage {
	u64 branch_addr;
	u64 pcrel_load_addr;
};

static const char *
symbol_lookup_callback(void *disinfo, uint64_t value,
		       uint64_t *ref_type,
		       uint64_t address __maybe_unused,
		       const char **ref __maybe_unused)
{
	struct symbol_lookup_storage *storage = disinfo;

	if (*ref_type == LLVMDisassembler_ReferenceType_In_Branch)
		storage->branch_addr = value;
	else if (*ref_type == LLVMDisassembler_ReferenceType_In_PCrel_Load)
		storage->pcrel_load_addr = value;
	*ref_type = LLVMDisassembler_ReferenceType_InOut_None;
	return NULL;
}
#endif

int symbol__disassemble_llvm(const char *filename, struct symbol *sym,
			     struct annotate_args *args __maybe_unused)
{
#ifdef HAVE_LIBLLVM_SUPPORT
	struct annotation *notes = symbol__annotation(sym);
	struct map *map = args->ms->map;
	struct dso *dso = map__dso(map);
	u64 start = map__rip_2objdump(map, sym->start);
	/* Malloc-ed buffer containing instructions read from disk. */
	u8 *code_buf = NULL;
	/* Pointer to code to be disassembled. */
	const u8 *buf;
	u64 buf_len;
	u64 pc;
	bool is_64bit;
	char disasm_buf[2048];
	size_t disasm_len;
	struct disasm_line *dl;
	LLVMDisasmContextRef disasm = NULL;
	struct symbol_lookup_storage storage;
	char *line_storage = NULL;
	size_t line_storage_len = 0;
	int ret = -1;

	if (args->options->objdump_path)
		return -1;

	buf = dso__read_symbol(dso, filename, map, sym,
			       &code_buf, &buf_len, &is_64bit);
	if (buf == NULL)
		return errno;

	init_llvm();
	if (arch__is_x86(args->arch)) {
		const char *triplet = is_64bit ? "x86_64-pc-linux" : "i686-pc-linux";

		disasm = LLVMCreateDisasm(triplet, &storage, /*tag_type=*/0,
					  /*get_op_info=*/NULL, symbol_lookup_callback);
	} else {
		char triplet[64];
		const char *features = NULL;

		scnprintf(triplet, sizeof(triplet), "%s-linux-gnu",
			  args->arch->name);
		if (args->arch->id.e_machine == EM_AARCH64)
			features = "+all";
		disasm = LLVMCreateDisasmCPUFeatures(triplet, /*cpu=*/"",
						     features, &storage,
						     /*tag_type=*/0,
						     /*get_op_info=*/NULL,
						     symbol_lookup_callback);
	}

	if (disasm == NULL)
		goto err;

	if (args->options->disassembler_style &&
	    !strcmp(args->options->disassembler_style, "intel"))
		LLVMSetDisasmOptions(disasm,
				     LLVMDisassembler_Option_AsmPrinterVariant);

	/*
	 * This needs to be set after AsmPrinterVariant, due to a bug in LLVM;
	 * setting AsmPrinterVariant makes a new instruction printer, making it
	 * forget about the PrintImmHex flag (which is applied before if both
	 * are given to the same call).
	 */
	LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex);

Annotation

Implementation Notes