xref: /linux/tools/objtool/orc_gen.c (revision e5e95a7639ed5f7dc3e404858ad7910de5fa2057)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
4  */
5 
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include <linux/objtool.h>
10 #include <asm/orc_types.h>
11 
12 #include "check.h"
13 #include "warn.h"
14 
15 int create_orc(struct objtool_file *file)
16 {
17 	struct instruction *insn;
18 
19 	for_each_insn(file, insn) {
20 		struct orc_entry *orc = &insn->orc;
21 		struct cfi_reg *cfa = &insn->cfi.cfa;
22 		struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
23 
24 		if (!insn->sec->text)
25 			continue;
26 
27 		orc->end = insn->cfi.end;
28 
29 		if (cfa->base == CFI_UNDEFINED) {
30 			orc->sp_reg = ORC_REG_UNDEFINED;
31 			continue;
32 		}
33 
34 		switch (cfa->base) {
35 		case CFI_SP:
36 			orc->sp_reg = ORC_REG_SP;
37 			break;
38 		case CFI_SP_INDIRECT:
39 			orc->sp_reg = ORC_REG_SP_INDIRECT;
40 			break;
41 		case CFI_BP:
42 			orc->sp_reg = ORC_REG_BP;
43 			break;
44 		case CFI_BP_INDIRECT:
45 			orc->sp_reg = ORC_REG_BP_INDIRECT;
46 			break;
47 		case CFI_R10:
48 			orc->sp_reg = ORC_REG_R10;
49 			break;
50 		case CFI_R13:
51 			orc->sp_reg = ORC_REG_R13;
52 			break;
53 		case CFI_DI:
54 			orc->sp_reg = ORC_REG_DI;
55 			break;
56 		case CFI_DX:
57 			orc->sp_reg = ORC_REG_DX;
58 			break;
59 		default:
60 			WARN_FUNC("unknown CFA base reg %d",
61 				  insn->sec, insn->offset, cfa->base);
62 			return -1;
63 		}
64 
65 		switch(bp->base) {
66 		case CFI_UNDEFINED:
67 			orc->bp_reg = ORC_REG_UNDEFINED;
68 			break;
69 		case CFI_CFA:
70 			orc->bp_reg = ORC_REG_PREV_SP;
71 			break;
72 		case CFI_BP:
73 			orc->bp_reg = ORC_REG_BP;
74 			break;
75 		default:
76 			WARN_FUNC("unknown BP base reg %d",
77 				  insn->sec, insn->offset, bp->base);
78 			return -1;
79 		}
80 
81 		orc->sp_offset = cfa->offset;
82 		orc->bp_offset = bp->offset;
83 		orc->type = insn->cfi.type;
84 	}
85 
86 	return 0;
87 }
88 
89 static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec,
90 				unsigned int idx, struct section *insn_sec,
91 				unsigned long insn_off, struct orc_entry *o)
92 {
93 	struct orc_entry *orc;
94 	struct reloc *reloc;
95 
96 	/* populate ORC data */
97 	orc = (struct orc_entry *)u_sec->data->d_buf + idx;
98 	memcpy(orc, o, sizeof(*orc));
99 
100 	/* populate reloc for ip */
101 	reloc = malloc(sizeof(*reloc));
102 	if (!reloc) {
103 		perror("malloc");
104 		return -1;
105 	}
106 	memset(reloc, 0, sizeof(*reloc));
107 
108 	insn_to_reloc_sym_addend(insn_sec, insn_off, reloc);
109 	if (!reloc->sym) {
110 		WARN("missing symbol for insn at offset 0x%lx",
111 		     insn_off);
112 		return -1;
113 	}
114 
115 	reloc->type = R_X86_64_PC32;
116 	reloc->offset = idx * sizeof(int);
117 	reloc->sec = ip_relocsec;
118 
119 	elf_add_reloc(elf, reloc);
120 
121 	return 0;
122 }
123 
124 int create_orc_sections(struct objtool_file *file)
125 {
126 	struct instruction *insn, *prev_insn;
127 	struct section *sec, *u_sec, *ip_relocsec;
128 	unsigned int idx;
129 
130 	struct orc_entry empty = {
131 		.sp_reg = ORC_REG_UNDEFINED,
132 		.bp_reg  = ORC_REG_UNDEFINED,
133 		.type    = UNWIND_HINT_TYPE_CALL,
134 	};
135 
136 	sec = find_section_by_name(file->elf, ".orc_unwind");
137 	if (sec) {
138 		WARN("file already has .orc_unwind section, skipping");
139 		return -1;
140 	}
141 
142 	/* count the number of needed orcs */
143 	idx = 0;
144 	for_each_sec(file, sec) {
145 		if (!sec->text)
146 			continue;
147 
148 		prev_insn = NULL;
149 		sec_for_each_insn(file, sec, insn) {
150 			if (!prev_insn ||
151 			    memcmp(&insn->orc, &prev_insn->orc,
152 				   sizeof(struct orc_entry))) {
153 				idx++;
154 			}
155 			prev_insn = insn;
156 		}
157 
158 		/* section terminator */
159 		if (prev_insn)
160 			idx++;
161 	}
162 	if (!idx)
163 		return -1;
164 
165 
166 	/* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
167 	sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), idx);
168 	if (!sec)
169 		return -1;
170 
171 	ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
172 	if (!ip_relocsec)
173 		return -1;
174 
175 	/* create .orc_unwind section */
176 	u_sec = elf_create_section(file->elf, ".orc_unwind", 0,
177 				   sizeof(struct orc_entry), idx);
178 
179 	/* populate sections */
180 	idx = 0;
181 	for_each_sec(file, sec) {
182 		if (!sec->text)
183 			continue;
184 
185 		prev_insn = NULL;
186 		sec_for_each_insn(file, sec, insn) {
187 			if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
188 						 sizeof(struct orc_entry))) {
189 
190 				if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
191 						     insn->sec, insn->offset,
192 						     &insn->orc))
193 					return -1;
194 
195 				idx++;
196 			}
197 			prev_insn = insn;
198 		}
199 
200 		/* section terminator */
201 		if (prev_insn) {
202 			if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
203 					     prev_insn->sec,
204 					     prev_insn->offset + prev_insn->len,
205 					     &empty))
206 				return -1;
207 
208 			idx++;
209 		}
210 	}
211 
212 	if (elf_rebuild_reloc_section(file->elf, ip_relocsec))
213 		return -1;
214 
215 	return 0;
216 }
217