xref: /openbmc/linux/tools/objtool/orc_gen.c (revision e7c0219b)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2627fce14SJosh Poimboeuf /*
3627fce14SJosh Poimboeuf  * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
4627fce14SJosh Poimboeuf  */
5627fce14SJosh Poimboeuf 
6627fce14SJosh Poimboeuf #include <stdlib.h>
7627fce14SJosh Poimboeuf #include <string.h>
8627fce14SJosh Poimboeuf 
9627fce14SJosh Poimboeuf #include "orc.h"
10627fce14SJosh Poimboeuf #include "check.h"
11627fce14SJosh Poimboeuf #include "warn.h"
12627fce14SJosh Poimboeuf 
13627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file)
14627fce14SJosh Poimboeuf {
15627fce14SJosh Poimboeuf 	struct instruction *insn;
16627fce14SJosh Poimboeuf 
17627fce14SJosh Poimboeuf 	for_each_insn(file, insn) {
18627fce14SJosh Poimboeuf 		struct orc_entry *orc = &insn->orc;
19e7c0219bSPeter Zijlstra 		struct cfi_reg *cfa = &insn->cfi.cfa;
20e7c0219bSPeter Zijlstra 		struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
21627fce14SJosh Poimboeuf 
22e7c0219bSPeter Zijlstra 		orc->end = insn->cfi.end;
23d31a5802SJosh Poimboeuf 
24627fce14SJosh Poimboeuf 		if (cfa->base == CFI_UNDEFINED) {
25627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_UNDEFINED;
26627fce14SJosh Poimboeuf 			continue;
27627fce14SJosh Poimboeuf 		}
28627fce14SJosh Poimboeuf 
29627fce14SJosh Poimboeuf 		switch (cfa->base) {
30627fce14SJosh Poimboeuf 		case CFI_SP:
31627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP;
32627fce14SJosh Poimboeuf 			break;
33627fce14SJosh Poimboeuf 		case CFI_SP_INDIRECT:
34627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP_INDIRECT;
35627fce14SJosh Poimboeuf 			break;
36627fce14SJosh Poimboeuf 		case CFI_BP:
37627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP;
38627fce14SJosh Poimboeuf 			break;
39627fce14SJosh Poimboeuf 		case CFI_BP_INDIRECT:
40627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP_INDIRECT;
41627fce14SJosh Poimboeuf 			break;
42627fce14SJosh Poimboeuf 		case CFI_R10:
43627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R10;
44627fce14SJosh Poimboeuf 			break;
45627fce14SJosh Poimboeuf 		case CFI_R13:
46627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R13;
47627fce14SJosh Poimboeuf 			break;
48627fce14SJosh Poimboeuf 		case CFI_DI:
49627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DI;
50627fce14SJosh Poimboeuf 			break;
51627fce14SJosh Poimboeuf 		case CFI_DX:
52627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DX;
53627fce14SJosh Poimboeuf 			break;
54627fce14SJosh Poimboeuf 		default:
55627fce14SJosh Poimboeuf 			WARN_FUNC("unknown CFA base reg %d",
56627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, cfa->base);
57627fce14SJosh Poimboeuf 			return -1;
58627fce14SJosh Poimboeuf 		}
59627fce14SJosh Poimboeuf 
60627fce14SJosh Poimboeuf 		switch(bp->base) {
61627fce14SJosh Poimboeuf 		case CFI_UNDEFINED:
62627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_UNDEFINED;
63627fce14SJosh Poimboeuf 			break;
64627fce14SJosh Poimboeuf 		case CFI_CFA:
65627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_PREV_SP;
66627fce14SJosh Poimboeuf 			break;
67627fce14SJosh Poimboeuf 		case CFI_BP:
68627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_BP;
69627fce14SJosh Poimboeuf 			break;
70627fce14SJosh Poimboeuf 		default:
71627fce14SJosh Poimboeuf 			WARN_FUNC("unknown BP base reg %d",
72627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, bp->base);
73627fce14SJosh Poimboeuf 			return -1;
74627fce14SJosh Poimboeuf 		}
75627fce14SJosh Poimboeuf 
76627fce14SJosh Poimboeuf 		orc->sp_offset = cfa->offset;
77627fce14SJosh Poimboeuf 		orc->bp_offset = bp->offset;
78e7c0219bSPeter Zijlstra 		orc->type = insn->cfi.type;
79627fce14SJosh Poimboeuf 	}
80627fce14SJosh Poimboeuf 
81627fce14SJosh Poimboeuf 	return 0;
82627fce14SJosh Poimboeuf }
83627fce14SJosh Poimboeuf 
848b5fa6bcSPeter Zijlstra static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relasec,
85627fce14SJosh Poimboeuf 				unsigned int idx, struct section *insn_sec,
86627fce14SJosh Poimboeuf 				unsigned long insn_off, struct orc_entry *o)
87627fce14SJosh Poimboeuf {
88627fce14SJosh Poimboeuf 	struct orc_entry *orc;
89627fce14SJosh Poimboeuf 	struct rela *rela;
90627fce14SJosh Poimboeuf 
91627fce14SJosh Poimboeuf 	/* populate ORC data */
92627fce14SJosh Poimboeuf 	orc = (struct orc_entry *)u_sec->data->d_buf + idx;
93627fce14SJosh Poimboeuf 	memcpy(orc, o, sizeof(*orc));
94627fce14SJosh Poimboeuf 
95627fce14SJosh Poimboeuf 	/* populate rela for ip */
96627fce14SJosh Poimboeuf 	rela = malloc(sizeof(*rela));
97627fce14SJosh Poimboeuf 	if (!rela) {
98627fce14SJosh Poimboeuf 		perror("malloc");
99627fce14SJosh Poimboeuf 		return -1;
100627fce14SJosh Poimboeuf 	}
101627fce14SJosh Poimboeuf 	memset(rela, 0, sizeof(*rela));
102627fce14SJosh Poimboeuf 
103e81e0724SJosh Poimboeuf 	if (insn_sec->sym) {
104627fce14SJosh Poimboeuf 		rela->sym = insn_sec->sym;
105627fce14SJosh Poimboeuf 		rela->addend = insn_off;
106e81e0724SJosh Poimboeuf 	} else {
107e81e0724SJosh Poimboeuf 		/*
108e81e0724SJosh Poimboeuf 		 * The Clang assembler doesn't produce section symbols, so we
109e81e0724SJosh Poimboeuf 		 * have to reference the function symbol instead:
110e81e0724SJosh Poimboeuf 		 */
111e81e0724SJosh Poimboeuf 		rela->sym = find_symbol_containing(insn_sec, insn_off);
112e81e0724SJosh Poimboeuf 		if (!rela->sym) {
113e81e0724SJosh Poimboeuf 			/*
114e81e0724SJosh Poimboeuf 			 * Hack alert.  This happens when we need to reference
115e81e0724SJosh Poimboeuf 			 * the NOP pad insn immediately after the function.
116e81e0724SJosh Poimboeuf 			 */
117e81e0724SJosh Poimboeuf 			rela->sym = find_symbol_containing(insn_sec,
118e81e0724SJosh Poimboeuf 							   insn_off - 1);
119e81e0724SJosh Poimboeuf 		}
120e81e0724SJosh Poimboeuf 		if (!rela->sym) {
121e81e0724SJosh Poimboeuf 			WARN("missing symbol for insn at offset 0x%lx\n",
122e81e0724SJosh Poimboeuf 			     insn_off);
123e81e0724SJosh Poimboeuf 			return -1;
124e81e0724SJosh Poimboeuf 		}
125e81e0724SJosh Poimboeuf 
126e81e0724SJosh Poimboeuf 		rela->addend = insn_off - rela->sym->offset;
127e81e0724SJosh Poimboeuf 	}
128e81e0724SJosh Poimboeuf 
129627fce14SJosh Poimboeuf 	rela->type = R_X86_64_PC32;
130627fce14SJosh Poimboeuf 	rela->offset = idx * sizeof(int);
1318b5fa6bcSPeter Zijlstra 	rela->sec = ip_relasec;
132627fce14SJosh Poimboeuf 
133627fce14SJosh Poimboeuf 	list_add_tail(&rela->list, &ip_relasec->rela_list);
1348b5fa6bcSPeter Zijlstra 	hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
135627fce14SJosh Poimboeuf 
136627fce14SJosh Poimboeuf 	return 0;
137627fce14SJosh Poimboeuf }
138627fce14SJosh Poimboeuf 
139627fce14SJosh Poimboeuf int create_orc_sections(struct objtool_file *file)
140627fce14SJosh Poimboeuf {
141627fce14SJosh Poimboeuf 	struct instruction *insn, *prev_insn;
142627fce14SJosh Poimboeuf 	struct section *sec, *u_sec, *ip_relasec;
143627fce14SJosh Poimboeuf 	unsigned int idx;
144627fce14SJosh Poimboeuf 
145627fce14SJosh Poimboeuf 	struct orc_entry empty = {
146627fce14SJosh Poimboeuf 		.sp_reg = ORC_REG_UNDEFINED,
147627fce14SJosh Poimboeuf 		.bp_reg  = ORC_REG_UNDEFINED,
148627fce14SJosh Poimboeuf 		.type    = ORC_TYPE_CALL,
149627fce14SJosh Poimboeuf 	};
150627fce14SJosh Poimboeuf 
151627fce14SJosh Poimboeuf 	sec = find_section_by_name(file->elf, ".orc_unwind");
152627fce14SJosh Poimboeuf 	if (sec) {
153627fce14SJosh Poimboeuf 		WARN("file already has .orc_unwind section, skipping");
154627fce14SJosh Poimboeuf 		return -1;
155627fce14SJosh Poimboeuf 	}
156627fce14SJosh Poimboeuf 
157627fce14SJosh Poimboeuf 	/* count the number of needed orcs */
158627fce14SJosh Poimboeuf 	idx = 0;
159627fce14SJosh Poimboeuf 	for_each_sec(file, sec) {
160627fce14SJosh Poimboeuf 		if (!sec->text)
161627fce14SJosh Poimboeuf 			continue;
162627fce14SJosh Poimboeuf 
163627fce14SJosh Poimboeuf 		prev_insn = NULL;
164627fce14SJosh Poimboeuf 		sec_for_each_insn(file, sec, insn) {
165627fce14SJosh Poimboeuf 			if (!prev_insn ||
166627fce14SJosh Poimboeuf 			    memcmp(&insn->orc, &prev_insn->orc,
167627fce14SJosh Poimboeuf 				   sizeof(struct orc_entry))) {
168627fce14SJosh Poimboeuf 				idx++;
169627fce14SJosh Poimboeuf 			}
170627fce14SJosh Poimboeuf 			prev_insn = insn;
171627fce14SJosh Poimboeuf 		}
172627fce14SJosh Poimboeuf 
173627fce14SJosh Poimboeuf 		/* section terminator */
174627fce14SJosh Poimboeuf 		if (prev_insn)
175627fce14SJosh Poimboeuf 			idx++;
176627fce14SJosh Poimboeuf 	}
177627fce14SJosh Poimboeuf 	if (!idx)
178627fce14SJosh Poimboeuf 		return -1;
179627fce14SJosh Poimboeuf 
180627fce14SJosh Poimboeuf 
181627fce14SJosh Poimboeuf 	/* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
182627fce14SJosh Poimboeuf 	sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx);
183ce90aaf5SSimon Ser 	if (!sec)
184ce90aaf5SSimon Ser 		return -1;
185627fce14SJosh Poimboeuf 
186627fce14SJosh Poimboeuf 	ip_relasec = elf_create_rela_section(file->elf, sec);
187627fce14SJosh Poimboeuf 	if (!ip_relasec)
188627fce14SJosh Poimboeuf 		return -1;
189627fce14SJosh Poimboeuf 
190627fce14SJosh Poimboeuf 	/* create .orc_unwind section */
191627fce14SJosh Poimboeuf 	u_sec = elf_create_section(file->elf, ".orc_unwind",
192627fce14SJosh Poimboeuf 				   sizeof(struct orc_entry), idx);
193627fce14SJosh Poimboeuf 
194627fce14SJosh Poimboeuf 	/* populate sections */
195627fce14SJosh Poimboeuf 	idx = 0;
196627fce14SJosh Poimboeuf 	for_each_sec(file, sec) {
197627fce14SJosh Poimboeuf 		if (!sec->text)
198627fce14SJosh Poimboeuf 			continue;
199627fce14SJosh Poimboeuf 
200627fce14SJosh Poimboeuf 		prev_insn = NULL;
201627fce14SJosh Poimboeuf 		sec_for_each_insn(file, sec, insn) {
202627fce14SJosh Poimboeuf 			if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
203627fce14SJosh Poimboeuf 						 sizeof(struct orc_entry))) {
204627fce14SJosh Poimboeuf 
2058b5fa6bcSPeter Zijlstra 				if (create_orc_entry(file->elf, u_sec, ip_relasec, idx,
206627fce14SJosh Poimboeuf 						     insn->sec, insn->offset,
207627fce14SJosh Poimboeuf 						     &insn->orc))
208627fce14SJosh Poimboeuf 					return -1;
209627fce14SJosh Poimboeuf 
210627fce14SJosh Poimboeuf 				idx++;
211627fce14SJosh Poimboeuf 			}
212627fce14SJosh Poimboeuf 			prev_insn = insn;
213627fce14SJosh Poimboeuf 		}
214627fce14SJosh Poimboeuf 
215627fce14SJosh Poimboeuf 		/* section terminator */
216627fce14SJosh Poimboeuf 		if (prev_insn) {
2178b5fa6bcSPeter Zijlstra 			if (create_orc_entry(file->elf, u_sec, ip_relasec, idx,
218627fce14SJosh Poimboeuf 					     prev_insn->sec,
219627fce14SJosh Poimboeuf 					     prev_insn->offset + prev_insn->len,
220627fce14SJosh Poimboeuf 					     &empty))
221627fce14SJosh Poimboeuf 				return -1;
222627fce14SJosh Poimboeuf 
223627fce14SJosh Poimboeuf 			idx++;
224627fce14SJosh Poimboeuf 		}
225627fce14SJosh Poimboeuf 	}
226627fce14SJosh Poimboeuf 
227627fce14SJosh Poimboeuf 	if (elf_rebuild_rela_section(ip_relasec))
228627fce14SJosh Poimboeuf 		return -1;
229627fce14SJosh Poimboeuf 
230627fce14SJosh Poimboeuf 	return 0;
231627fce14SJosh Poimboeuf }
232