xref: /linux/tools/objtool/orc_dump.c (revision 7fc2cd2e4b398c57c9cf961cfea05eadbf34c05c)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
4  */
5 
6 #include <unistd.h>
7 #include <asm/orc_types.h>
8 #include <objtool/objtool.h>
9 #include <objtool/orc.h>
10 #include <objtool/warn.h>
11 
12 int orc_dump(const char *filename)
13 {
14 	int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0;
15 	struct orc_entry *orc = NULL;
16 	char *name;
17 	size_t nr_sections;
18 	Elf64_Addr orc_ip_addr = 0;
19 	size_t shstrtab_idx, strtab_idx = 0;
20 	Elf *elf;
21 	Elf_Scn *scn;
22 	GElf_Shdr sh;
23 	GElf_Rela rela;
24 	GElf_Sym sym;
25 	Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL;
26 	struct elf dummy_elf = {};
27 
28 	elf_version(EV_CURRENT);
29 
30 	fd = open(filename, O_RDONLY);
31 	if (fd == -1) {
32 		perror("open");
33 		return -1;
34 	}
35 
36 	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
37 	if (!elf) {
38 		ERROR_ELF("elf_begin");
39 		return -1;
40 	}
41 
42 	if (!elf64_getehdr(elf)) {
43 		ERROR_ELF("elf64_getehdr");
44 		return -1;
45 	}
46 	memcpy(&dummy_elf.ehdr, elf64_getehdr(elf), sizeof(dummy_elf.ehdr));
47 
48 	if (elf_getshdrnum(elf, &nr_sections)) {
49 		ERROR_ELF("elf_getshdrnum");
50 		return -1;
51 	}
52 
53 	if (elf_getshdrstrndx(elf, &shstrtab_idx)) {
54 		ERROR_ELF("elf_getshdrstrndx");
55 		return -1;
56 	}
57 
58 	for (i = 0; i < nr_sections; i++) {
59 		scn = elf_getscn(elf, i);
60 		if (!scn) {
61 			ERROR_ELF("elf_getscn");
62 			return -1;
63 		}
64 
65 		if (!gelf_getshdr(scn, &sh)) {
66 			ERROR_ELF("gelf_getshdr");
67 			return -1;
68 		}
69 
70 		name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
71 		if (!name) {
72 			ERROR_ELF("elf_strptr");
73 			return -1;
74 		}
75 
76 		data = elf_getdata(scn, NULL);
77 		if (!data) {
78 			ERROR_ELF("elf_getdata");
79 			return -1;
80 		}
81 
82 		if (!strcmp(name, ".symtab")) {
83 			symtab = data;
84 		} else if (!strcmp(name, ".strtab")) {
85 			strtab_idx = i;
86 		} else if (!strcmp(name, ".orc_unwind")) {
87 			orc = data->d_buf;
88 			orc_size = sh.sh_size;
89 		} else if (!strcmp(name, ".orc_unwind_ip")) {
90 			orc_ip = data->d_buf;
91 			orc_ip_addr = sh.sh_addr;
92 		} else if (!strcmp(name, ".rela.orc_unwind_ip")) {
93 			rela_orc_ip = data;
94 		}
95 	}
96 
97 	if (!symtab || !strtab_idx || !orc || !orc_ip)
98 		return 0;
99 
100 	if (orc_size % sizeof(*orc) != 0) {
101 		ERROR("bad .orc_unwind section size");
102 		return -1;
103 	}
104 
105 	nr_entries = orc_size / sizeof(*orc);
106 	for (i = 0; i < nr_entries; i++) {
107 		if (rela_orc_ip) {
108 			if (!gelf_getrela(rela_orc_ip, i, &rela)) {
109 				ERROR_ELF("gelf_getrela");
110 				return -1;
111 			}
112 
113 			if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) {
114 				ERROR_ELF("gelf_getsym");
115 				return -1;
116 			}
117 
118 			if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) {
119 				scn = elf_getscn(elf, sym.st_shndx);
120 				if (!scn) {
121 					ERROR_ELF("elf_getscn");
122 					return -1;
123 				}
124 
125 				if (!gelf_getshdr(scn, &sh)) {
126 					ERROR_ELF("gelf_getshdr");
127 					return -1;
128 				}
129 
130 				name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
131 				if (!name) {
132 					ERROR_ELF("elf_strptr");
133 					return -1;
134 				}
135 			} else {
136 				name = elf_strptr(elf, strtab_idx, sym.st_name);
137 				if (!name) {
138 					ERROR_ELF("elf_strptr");
139 					return -1;
140 				}
141 			}
142 
143 			printf("%s+%llx:", name, (unsigned long long)rela.r_addend);
144 
145 		} else {
146 			printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i]));
147 		}
148 
149 		orc_print_dump(&dummy_elf, orc, i);
150 	}
151 
152 	elf_end(elf);
153 	close(fd);
154 
155 	return 0;
156 }
157