xref: /openbmc/linux/tools/objtool/orc_gen.c (revision fb414783)
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 "check.h"
10627fce14SJosh Poimboeuf #include "warn.h"
11627fce14SJosh Poimboeuf 
12627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file)
13627fce14SJosh Poimboeuf {
14627fce14SJosh Poimboeuf 	struct instruction *insn;
15627fce14SJosh Poimboeuf 
16627fce14SJosh Poimboeuf 	for_each_insn(file, insn) {
17627fce14SJosh Poimboeuf 		struct orc_entry *orc = &insn->orc;
18e7c0219bSPeter Zijlstra 		struct cfi_reg *cfa = &insn->cfi.cfa;
19e7c0219bSPeter Zijlstra 		struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
20627fce14SJosh Poimboeuf 
21e7c0219bSPeter Zijlstra 		orc->end = insn->cfi.end;
22d31a5802SJosh Poimboeuf 
23627fce14SJosh Poimboeuf 		if (cfa->base == CFI_UNDEFINED) {
24627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_UNDEFINED;
25627fce14SJosh Poimboeuf 			continue;
26627fce14SJosh Poimboeuf 		}
27627fce14SJosh Poimboeuf 
28627fce14SJosh Poimboeuf 		switch (cfa->base) {
29627fce14SJosh Poimboeuf 		case CFI_SP:
30627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP;
31627fce14SJosh Poimboeuf 			break;
32627fce14SJosh Poimboeuf 		case CFI_SP_INDIRECT:
33627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP_INDIRECT;
34627fce14SJosh Poimboeuf 			break;
35627fce14SJosh Poimboeuf 		case CFI_BP:
36627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP;
37627fce14SJosh Poimboeuf 			break;
38627fce14SJosh Poimboeuf 		case CFI_BP_INDIRECT:
39627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP_INDIRECT;
40627fce14SJosh Poimboeuf 			break;
41627fce14SJosh Poimboeuf 		case CFI_R10:
42627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R10;
43627fce14SJosh Poimboeuf 			break;
44627fce14SJosh Poimboeuf 		case CFI_R13:
45627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R13;
46627fce14SJosh Poimboeuf 			break;
47627fce14SJosh Poimboeuf 		case CFI_DI:
48627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DI;
49627fce14SJosh Poimboeuf 			break;
50627fce14SJosh Poimboeuf 		case CFI_DX:
51627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DX;
52627fce14SJosh Poimboeuf 			break;
53627fce14SJosh Poimboeuf 		default:
54627fce14SJosh Poimboeuf 			WARN_FUNC("unknown CFA base reg %d",
55627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, cfa->base);
56627fce14SJosh Poimboeuf 			return -1;
57627fce14SJosh Poimboeuf 		}
58627fce14SJosh Poimboeuf 
59627fce14SJosh Poimboeuf 		switch(bp->base) {
60627fce14SJosh Poimboeuf 		case CFI_UNDEFINED:
61627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_UNDEFINED;
62627fce14SJosh Poimboeuf 			break;
63627fce14SJosh Poimboeuf 		case CFI_CFA:
64627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_PREV_SP;
65627fce14SJosh Poimboeuf 			break;
66627fce14SJosh Poimboeuf 		case CFI_BP:
67627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_BP;
68627fce14SJosh Poimboeuf 			break;
69627fce14SJosh Poimboeuf 		default:
70627fce14SJosh Poimboeuf 			WARN_FUNC("unknown BP base reg %d",
71627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, bp->base);
72627fce14SJosh Poimboeuf 			return -1;
73627fce14SJosh Poimboeuf 		}
74627fce14SJosh Poimboeuf 
75627fce14SJosh Poimboeuf 		orc->sp_offset = cfa->offset;
76627fce14SJosh Poimboeuf 		orc->bp_offset = bp->offset;
77e7c0219bSPeter Zijlstra 		orc->type = insn->cfi.type;
78627fce14SJosh Poimboeuf 	}
79627fce14SJosh Poimboeuf 
80627fce14SJosh Poimboeuf 	return 0;
81627fce14SJosh Poimboeuf }
82627fce14SJosh Poimboeuf 
83f1974222SMatt Helsley static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec,
84627fce14SJosh Poimboeuf 				unsigned int idx, struct section *insn_sec,
85627fce14SJosh Poimboeuf 				unsigned long insn_off, struct orc_entry *o)
86627fce14SJosh Poimboeuf {
87627fce14SJosh Poimboeuf 	struct orc_entry *orc;
88f1974222SMatt Helsley 	struct reloc *reloc;
89627fce14SJosh Poimboeuf 
90627fce14SJosh Poimboeuf 	/* populate ORC data */
91627fce14SJosh Poimboeuf 	orc = (struct orc_entry *)u_sec->data->d_buf + idx;
92627fce14SJosh Poimboeuf 	memcpy(orc, o, sizeof(*orc));
93627fce14SJosh Poimboeuf 
94f1974222SMatt Helsley 	/* populate reloc for ip */
95f1974222SMatt Helsley 	reloc = malloc(sizeof(*reloc));
96f1974222SMatt Helsley 	if (!reloc) {
97627fce14SJosh Poimboeuf 		perror("malloc");
98627fce14SJosh Poimboeuf 		return -1;
99627fce14SJosh Poimboeuf 	}
100f1974222SMatt Helsley 	memset(reloc, 0, sizeof(*reloc));
101627fce14SJosh Poimboeuf 
102e81e0724SJosh Poimboeuf 	if (insn_sec->sym) {
103f1974222SMatt Helsley 		reloc->sym = insn_sec->sym;
104f1974222SMatt Helsley 		reloc->addend = insn_off;
105e81e0724SJosh Poimboeuf 	} else {
106e81e0724SJosh Poimboeuf 		/*
107e81e0724SJosh Poimboeuf 		 * The Clang assembler doesn't produce section symbols, so we
108e81e0724SJosh Poimboeuf 		 * have to reference the function symbol instead:
109e81e0724SJosh Poimboeuf 		 */
110f1974222SMatt Helsley 		reloc->sym = find_symbol_containing(insn_sec, insn_off);
111f1974222SMatt Helsley 		if (!reloc->sym) {
112e81e0724SJosh Poimboeuf 			/*
113e81e0724SJosh Poimboeuf 			 * Hack alert.  This happens when we need to reference
114e81e0724SJosh Poimboeuf 			 * the NOP pad insn immediately after the function.
115e81e0724SJosh Poimboeuf 			 */
116f1974222SMatt Helsley 			reloc->sym = find_symbol_containing(insn_sec,
117e81e0724SJosh Poimboeuf 							   insn_off - 1);
118e81e0724SJosh Poimboeuf 		}
119f1974222SMatt Helsley 		if (!reloc->sym) {
120e81e0724SJosh Poimboeuf 			WARN("missing symbol for insn at offset 0x%lx\n",
121e81e0724SJosh Poimboeuf 			     insn_off);
122e81e0724SJosh Poimboeuf 			return -1;
123e81e0724SJosh Poimboeuf 		}
124e81e0724SJosh Poimboeuf 
125f1974222SMatt Helsley 		reloc->addend = insn_off - reloc->sym->offset;
126e81e0724SJosh Poimboeuf 	}
127e81e0724SJosh Poimboeuf 
128f1974222SMatt Helsley 	reloc->type = R_X86_64_PC32;
129f1974222SMatt Helsley 	reloc->offset = idx * sizeof(int);
130f1974222SMatt Helsley 	reloc->sec = ip_relocsec;
131627fce14SJosh Poimboeuf 
132f1974222SMatt Helsley 	elf_add_reloc(elf, reloc);
133627fce14SJosh Poimboeuf 
134627fce14SJosh Poimboeuf 	return 0;
135627fce14SJosh Poimboeuf }
136627fce14SJosh Poimboeuf 
137627fce14SJosh Poimboeuf int create_orc_sections(struct objtool_file *file)
138627fce14SJosh Poimboeuf {
139627fce14SJosh Poimboeuf 	struct instruction *insn, *prev_insn;
140f1974222SMatt Helsley 	struct section *sec, *u_sec, *ip_relocsec;
141627fce14SJosh Poimboeuf 	unsigned int idx;
142627fce14SJosh Poimboeuf 
143627fce14SJosh Poimboeuf 	struct orc_entry empty = {
144627fce14SJosh Poimboeuf 		.sp_reg = ORC_REG_UNDEFINED,
145627fce14SJosh Poimboeuf 		.bp_reg  = ORC_REG_UNDEFINED,
146627fce14SJosh Poimboeuf 		.type    = ORC_TYPE_CALL,
147627fce14SJosh Poimboeuf 	};
148627fce14SJosh Poimboeuf 
149627fce14SJosh Poimboeuf 	sec = find_section_by_name(file->elf, ".orc_unwind");
150627fce14SJosh Poimboeuf 	if (sec) {
151627fce14SJosh Poimboeuf 		WARN("file already has .orc_unwind section, skipping");
152627fce14SJosh Poimboeuf 		return -1;
153627fce14SJosh Poimboeuf 	}
154627fce14SJosh Poimboeuf 
155627fce14SJosh Poimboeuf 	/* count the number of needed orcs */
156627fce14SJosh Poimboeuf 	idx = 0;
157627fce14SJosh Poimboeuf 	for_each_sec(file, sec) {
158627fce14SJosh Poimboeuf 		if (!sec->text)
159627fce14SJosh Poimboeuf 			continue;
160627fce14SJosh Poimboeuf 
161627fce14SJosh Poimboeuf 		prev_insn = NULL;
162627fce14SJosh Poimboeuf 		sec_for_each_insn(file, sec, insn) {
163627fce14SJosh Poimboeuf 			if (!prev_insn ||
164627fce14SJosh Poimboeuf 			    memcmp(&insn->orc, &prev_insn->orc,
165627fce14SJosh Poimboeuf 				   sizeof(struct orc_entry))) {
166627fce14SJosh Poimboeuf 				idx++;
167627fce14SJosh Poimboeuf 			}
168627fce14SJosh Poimboeuf 			prev_insn = insn;
169627fce14SJosh Poimboeuf 		}
170627fce14SJosh Poimboeuf 
171627fce14SJosh Poimboeuf 		/* section terminator */
172627fce14SJosh Poimboeuf 		if (prev_insn)
173627fce14SJosh Poimboeuf 			idx++;
174627fce14SJosh Poimboeuf 	}
175627fce14SJosh Poimboeuf 	if (!idx)
176627fce14SJosh Poimboeuf 		return -1;
177627fce14SJosh Poimboeuf 
178627fce14SJosh Poimboeuf 
179627fce14SJosh Poimboeuf 	/* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
180627fce14SJosh Poimboeuf 	sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx);
181ce90aaf5SSimon Ser 	if (!sec)
182ce90aaf5SSimon Ser 		return -1;
183627fce14SJosh Poimboeuf 
184fb414783SMatt Helsley 	ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
185f1974222SMatt Helsley 	if (!ip_relocsec)
186627fce14SJosh Poimboeuf 		return -1;
187627fce14SJosh Poimboeuf 
188627fce14SJosh Poimboeuf 	/* create .orc_unwind section */
189627fce14SJosh Poimboeuf 	u_sec = elf_create_section(file->elf, ".orc_unwind",
190627fce14SJosh Poimboeuf 				   sizeof(struct orc_entry), idx);
191627fce14SJosh Poimboeuf 
192627fce14SJosh Poimboeuf 	/* populate sections */
193627fce14SJosh Poimboeuf 	idx = 0;
194627fce14SJosh Poimboeuf 	for_each_sec(file, sec) {
195627fce14SJosh Poimboeuf 		if (!sec->text)
196627fce14SJosh Poimboeuf 			continue;
197627fce14SJosh Poimboeuf 
198627fce14SJosh Poimboeuf 		prev_insn = NULL;
199627fce14SJosh Poimboeuf 		sec_for_each_insn(file, sec, insn) {
200627fce14SJosh Poimboeuf 			if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
201627fce14SJosh Poimboeuf 						 sizeof(struct orc_entry))) {
202627fce14SJosh Poimboeuf 
203f1974222SMatt Helsley 				if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
204627fce14SJosh Poimboeuf 						     insn->sec, insn->offset,
205627fce14SJosh Poimboeuf 						     &insn->orc))
206627fce14SJosh Poimboeuf 					return -1;
207627fce14SJosh Poimboeuf 
208627fce14SJosh Poimboeuf 				idx++;
209627fce14SJosh Poimboeuf 			}
210627fce14SJosh Poimboeuf 			prev_insn = insn;
211627fce14SJosh Poimboeuf 		}
212627fce14SJosh Poimboeuf 
213627fce14SJosh Poimboeuf 		/* section terminator */
214627fce14SJosh Poimboeuf 		if (prev_insn) {
215f1974222SMatt Helsley 			if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
216627fce14SJosh Poimboeuf 					     prev_insn->sec,
217627fce14SJosh Poimboeuf 					     prev_insn->offset + prev_insn->len,
218627fce14SJosh Poimboeuf 					     &empty))
219627fce14SJosh Poimboeuf 				return -1;
220627fce14SJosh Poimboeuf 
221627fce14SJosh Poimboeuf 			idx++;
222627fce14SJosh Poimboeuf 		}
223627fce14SJosh Poimboeuf 	}
224627fce14SJosh Poimboeuf 
225f1974222SMatt Helsley 	if (elf_rebuild_reloc_section(ip_relocsec))
226627fce14SJosh Poimboeuf 		return -1;
227627fce14SJosh Poimboeuf 
228627fce14SJosh Poimboeuf 	return 0;
229627fce14SJosh Poimboeuf }
230