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 <linux/objtool.h>
8 #include <asm/orc_types.h>
9 #include <objtool/objtool.h>
10 #include <objtool/warn.h>
11 #include <objtool/endianness.h>
12 
reg_name(unsigned int reg)13 static const char *reg_name(unsigned int reg)
14 {
15 	switch (reg) {
16 	case ORC_REG_PREV_SP:
17 		return "prevsp";
18 	case ORC_REG_DX:
19 		return "dx";
20 	case ORC_REG_DI:
21 		return "di";
22 	case ORC_REG_BP:
23 		return "bp";
24 	case ORC_REG_SP:
25 		return "sp";
26 	case ORC_REG_R10:
27 		return "r10";
28 	case ORC_REG_R13:
29 		return "r13";
30 	case ORC_REG_BP_INDIRECT:
31 		return "bp(ind)";
32 	case ORC_REG_SP_INDIRECT:
33 		return "sp(ind)";
34 	default:
35 		return "?";
36 	}
37 }
38 
orc_type_name(unsigned int type)39 static const char *orc_type_name(unsigned int type)
40 {
41 	switch (type) {
42 	case UNWIND_HINT_TYPE_CALL:
43 		return "call";
44 	case UNWIND_HINT_TYPE_REGS:
45 		return "regs";
46 	case UNWIND_HINT_TYPE_REGS_PARTIAL:
47 		return "regs (partial)";
48 	default:
49 		return "?";
50 	}
51 }
52 
print_reg(unsigned int reg,int offset)53 static void print_reg(unsigned int reg, int offset)
54 {
55 	if (reg == ORC_REG_BP_INDIRECT)
56 		printf("(bp%+d)", offset);
57 	else if (reg == ORC_REG_SP_INDIRECT)
58 		printf("(sp)%+d", offset);
59 	else if (reg == ORC_REG_UNDEFINED)
60 		printf("(und)");
61 	else
62 		printf("%s%+d", reg_name(reg), offset);
63 }
64 
orc_dump(const char * _objname)65 int orc_dump(const char *_objname)
66 {
67 	int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0;
68 	struct orc_entry *orc = NULL;
69 	char *name;
70 	size_t nr_sections;
71 	Elf64_Addr orc_ip_addr = 0;
72 	size_t shstrtab_idx, strtab_idx = 0;
73 	Elf *elf;
74 	Elf_Scn *scn;
75 	GElf_Shdr sh;
76 	GElf_Rela rela;
77 	GElf_Sym sym;
78 	Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL;
79 
80 
81 	objname = _objname;
82 
83 	elf_version(EV_CURRENT);
84 
85 	fd = open(objname, O_RDONLY);
86 	if (fd == -1) {
87 		perror("open");
88 		return -1;
89 	}
90 
91 	elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
92 	if (!elf) {
93 		WARN_ELF("elf_begin");
94 		return -1;
95 	}
96 
97 	if (elf_getshdrnum(elf, &nr_sections)) {
98 		WARN_ELF("elf_getshdrnum");
99 		return -1;
100 	}
101 
102 	if (elf_getshdrstrndx(elf, &shstrtab_idx)) {
103 		WARN_ELF("elf_getshdrstrndx");
104 		return -1;
105 	}
106 
107 	for (i = 0; i < nr_sections; i++) {
108 		scn = elf_getscn(elf, i);
109 		if (!scn) {
110 			WARN_ELF("elf_getscn");
111 			return -1;
112 		}
113 
114 		if (!gelf_getshdr(scn, &sh)) {
115 			WARN_ELF("gelf_getshdr");
116 			return -1;
117 		}
118 
119 		name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
120 		if (!name) {
121 			WARN_ELF("elf_strptr");
122 			return -1;
123 		}
124 
125 		data = elf_getdata(scn, NULL);
126 		if (!data) {
127 			WARN_ELF("elf_getdata");
128 			return -1;
129 		}
130 
131 		if (!strcmp(name, ".symtab")) {
132 			symtab = data;
133 		} else if (!strcmp(name, ".strtab")) {
134 			strtab_idx = i;
135 		} else if (!strcmp(name, ".orc_unwind")) {
136 			orc = data->d_buf;
137 			orc_size = sh.sh_size;
138 		} else if (!strcmp(name, ".orc_unwind_ip")) {
139 			orc_ip = data->d_buf;
140 			orc_ip_addr = sh.sh_addr;
141 		} else if (!strcmp(name, ".rela.orc_unwind_ip")) {
142 			rela_orc_ip = data;
143 		}
144 	}
145 
146 	if (!symtab || !strtab_idx || !orc || !orc_ip)
147 		return 0;
148 
149 	if (orc_size % sizeof(*orc) != 0) {
150 		WARN("bad .orc_unwind section size");
151 		return -1;
152 	}
153 
154 	nr_entries = orc_size / sizeof(*orc);
155 	for (i = 0; i < nr_entries; i++) {
156 		if (rela_orc_ip) {
157 			if (!gelf_getrela(rela_orc_ip, i, &rela)) {
158 				WARN_ELF("gelf_getrela");
159 				return -1;
160 			}
161 
162 			if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) {
163 				WARN_ELF("gelf_getsym");
164 				return -1;
165 			}
166 
167 			if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) {
168 				scn = elf_getscn(elf, sym.st_shndx);
169 				if (!scn) {
170 					WARN_ELF("elf_getscn");
171 					return -1;
172 				}
173 
174 				if (!gelf_getshdr(scn, &sh)) {
175 					WARN_ELF("gelf_getshdr");
176 					return -1;
177 				}
178 
179 				name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
180 				if (!name) {
181 					WARN_ELF("elf_strptr");
182 					return -1;
183 				}
184 			} else {
185 				name = elf_strptr(elf, strtab_idx, sym.st_name);
186 				if (!name) {
187 					WARN_ELF("elf_strptr");
188 					return -1;
189 				}
190 			}
191 
192 			printf("%s+%llx:", name, (unsigned long long)rela.r_addend);
193 
194 		} else {
195 			printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i]));
196 		}
197 
198 
199 		printf(" sp:");
200 
201 		print_reg(orc[i].sp_reg, bswap_if_needed(orc[i].sp_offset));
202 
203 		printf(" bp:");
204 
205 		print_reg(orc[i].bp_reg, bswap_if_needed(orc[i].bp_offset));
206 
207 		printf(" type:%s end:%d\n",
208 		       orc_type_name(orc[i].type), orc[i].end);
209 	}
210 
211 	elf_end(elf);
212 	close(fd);
213 
214 	return 0;
215 }
216