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