xref: /openbmc/linux/tools/objtool/orc_gen.c (revision 44f6a7c0)
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 
9ee819aedSJulien Thierry #include <linux/objtool.h>
10ee819aedSJulien Thierry #include <asm/orc_types.h>
11ee819aedSJulien Thierry 
12627fce14SJosh Poimboeuf #include "check.h"
13627fce14SJosh Poimboeuf #include "warn.h"
14627fce14SJosh Poimboeuf 
15627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file)
16627fce14SJosh Poimboeuf {
17627fce14SJosh Poimboeuf 	struct instruction *insn;
18627fce14SJosh Poimboeuf 
19627fce14SJosh Poimboeuf 	for_each_insn(file, insn) {
20627fce14SJosh Poimboeuf 		struct orc_entry *orc = &insn->orc;
21e7c0219bSPeter Zijlstra 		struct cfi_reg *cfa = &insn->cfi.cfa;
22e7c0219bSPeter Zijlstra 		struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
23627fce14SJosh Poimboeuf 
243eaecac8SJulien Thierry 		if (!insn->sec->text)
253eaecac8SJulien Thierry 			continue;
263eaecac8SJulien Thierry 
27e7c0219bSPeter Zijlstra 		orc->end = insn->cfi.end;
28d31a5802SJosh Poimboeuf 
29627fce14SJosh Poimboeuf 		if (cfa->base == CFI_UNDEFINED) {
30627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_UNDEFINED;
31627fce14SJosh Poimboeuf 			continue;
32627fce14SJosh Poimboeuf 		}
33627fce14SJosh Poimboeuf 
34627fce14SJosh Poimboeuf 		switch (cfa->base) {
35627fce14SJosh Poimboeuf 		case CFI_SP:
36627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP;
37627fce14SJosh Poimboeuf 			break;
38627fce14SJosh Poimboeuf 		case CFI_SP_INDIRECT:
39627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP_INDIRECT;
40627fce14SJosh Poimboeuf 			break;
41627fce14SJosh Poimboeuf 		case CFI_BP:
42627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP;
43627fce14SJosh Poimboeuf 			break;
44627fce14SJosh Poimboeuf 		case CFI_BP_INDIRECT:
45627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP_INDIRECT;
46627fce14SJosh Poimboeuf 			break;
47627fce14SJosh Poimboeuf 		case CFI_R10:
48627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R10;
49627fce14SJosh Poimboeuf 			break;
50627fce14SJosh Poimboeuf 		case CFI_R13:
51627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R13;
52627fce14SJosh Poimboeuf 			break;
53627fce14SJosh Poimboeuf 		case CFI_DI:
54627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DI;
55627fce14SJosh Poimboeuf 			break;
56627fce14SJosh Poimboeuf 		case CFI_DX:
57627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DX;
58627fce14SJosh Poimboeuf 			break;
59627fce14SJosh Poimboeuf 		default:
60627fce14SJosh Poimboeuf 			WARN_FUNC("unknown CFA base reg %d",
61627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, cfa->base);
62627fce14SJosh Poimboeuf 			return -1;
63627fce14SJosh Poimboeuf 		}
64627fce14SJosh Poimboeuf 
65627fce14SJosh Poimboeuf 		switch(bp->base) {
66627fce14SJosh Poimboeuf 		case CFI_UNDEFINED:
67627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_UNDEFINED;
68627fce14SJosh Poimboeuf 			break;
69627fce14SJosh Poimboeuf 		case CFI_CFA:
70627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_PREV_SP;
71627fce14SJosh Poimboeuf 			break;
72627fce14SJosh Poimboeuf 		case CFI_BP:
73627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_BP;
74627fce14SJosh Poimboeuf 			break;
75627fce14SJosh Poimboeuf 		default:
76627fce14SJosh Poimboeuf 			WARN_FUNC("unknown BP base reg %d",
77627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, bp->base);
78627fce14SJosh Poimboeuf 			return -1;
79627fce14SJosh Poimboeuf 		}
80627fce14SJosh Poimboeuf 
81627fce14SJosh Poimboeuf 		orc->sp_offset = cfa->offset;
82627fce14SJosh Poimboeuf 		orc->bp_offset = bp->offset;
83e7c0219bSPeter Zijlstra 		orc->type = insn->cfi.type;
84627fce14SJosh Poimboeuf 	}
85627fce14SJosh Poimboeuf 
86627fce14SJosh Poimboeuf 	return 0;
87627fce14SJosh Poimboeuf }
88627fce14SJosh Poimboeuf 
89f1974222SMatt Helsley static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec,
90627fce14SJosh Poimboeuf 				unsigned int idx, struct section *insn_sec,
91627fce14SJosh Poimboeuf 				unsigned long insn_off, struct orc_entry *o)
92627fce14SJosh Poimboeuf {
93627fce14SJosh Poimboeuf 	struct orc_entry *orc;
94f1974222SMatt Helsley 	struct reloc *reloc;
95627fce14SJosh Poimboeuf 
96627fce14SJosh Poimboeuf 	/* populate ORC data */
97627fce14SJosh Poimboeuf 	orc = (struct orc_entry *)u_sec->data->d_buf + idx;
98627fce14SJosh Poimboeuf 	memcpy(orc, o, sizeof(*orc));
99627fce14SJosh Poimboeuf 
100f1974222SMatt Helsley 	/* populate reloc for ip */
101f1974222SMatt Helsley 	reloc = malloc(sizeof(*reloc));
102f1974222SMatt Helsley 	if (!reloc) {
103627fce14SJosh Poimboeuf 		perror("malloc");
104627fce14SJosh Poimboeuf 		return -1;
105627fce14SJosh Poimboeuf 	}
106f1974222SMatt Helsley 	memset(reloc, 0, sizeof(*reloc));
107627fce14SJosh Poimboeuf 
108*44f6a7c0SJosh Poimboeuf 	insn_to_reloc_sym_addend(insn_sec, insn_off, reloc);
109f1974222SMatt Helsley 	if (!reloc->sym) {
110*44f6a7c0SJosh Poimboeuf 		WARN("missing symbol for insn at offset 0x%lx",
111e81e0724SJosh Poimboeuf 		     insn_off);
112e81e0724SJosh Poimboeuf 		return -1;
113e81e0724SJosh Poimboeuf 	}
114e81e0724SJosh Poimboeuf 
115f1974222SMatt Helsley 	reloc->type = R_X86_64_PC32;
116f1974222SMatt Helsley 	reloc->offset = idx * sizeof(int);
117f1974222SMatt Helsley 	reloc->sec = ip_relocsec;
118627fce14SJosh Poimboeuf 
119f1974222SMatt Helsley 	elf_add_reloc(elf, reloc);
120627fce14SJosh Poimboeuf 
121627fce14SJosh Poimboeuf 	return 0;
122627fce14SJosh Poimboeuf }
123627fce14SJosh Poimboeuf 
124627fce14SJosh Poimboeuf int create_orc_sections(struct objtool_file *file)
125627fce14SJosh Poimboeuf {
126627fce14SJosh Poimboeuf 	struct instruction *insn, *prev_insn;
127f1974222SMatt Helsley 	struct section *sec, *u_sec, *ip_relocsec;
128627fce14SJosh Poimboeuf 	unsigned int idx;
129627fce14SJosh Poimboeuf 
130627fce14SJosh Poimboeuf 	struct orc_entry empty = {
131627fce14SJosh Poimboeuf 		.sp_reg = ORC_REG_UNDEFINED,
132627fce14SJosh Poimboeuf 		.bp_reg  = ORC_REG_UNDEFINED,
133ee819aedSJulien Thierry 		.type    = UNWIND_HINT_TYPE_CALL,
134627fce14SJosh Poimboeuf 	};
135627fce14SJosh Poimboeuf 
136627fce14SJosh Poimboeuf 	sec = find_section_by_name(file->elf, ".orc_unwind");
137627fce14SJosh Poimboeuf 	if (sec) {
138627fce14SJosh Poimboeuf 		WARN("file already has .orc_unwind section, skipping");
139627fce14SJosh Poimboeuf 		return -1;
140627fce14SJosh Poimboeuf 	}
141627fce14SJosh Poimboeuf 
142627fce14SJosh Poimboeuf 	/* count the number of needed orcs */
143627fce14SJosh Poimboeuf 	idx = 0;
144627fce14SJosh Poimboeuf 	for_each_sec(file, sec) {
145627fce14SJosh Poimboeuf 		if (!sec->text)
146627fce14SJosh Poimboeuf 			continue;
147627fce14SJosh Poimboeuf 
148627fce14SJosh Poimboeuf 		prev_insn = NULL;
149627fce14SJosh Poimboeuf 		sec_for_each_insn(file, sec, insn) {
150627fce14SJosh Poimboeuf 			if (!prev_insn ||
151627fce14SJosh Poimboeuf 			    memcmp(&insn->orc, &prev_insn->orc,
152627fce14SJosh Poimboeuf 				   sizeof(struct orc_entry))) {
153627fce14SJosh Poimboeuf 				idx++;
154627fce14SJosh Poimboeuf 			}
155627fce14SJosh Poimboeuf 			prev_insn = insn;
156627fce14SJosh Poimboeuf 		}
157627fce14SJosh Poimboeuf 
158627fce14SJosh Poimboeuf 		/* section terminator */
159627fce14SJosh Poimboeuf 		if (prev_insn)
160627fce14SJosh Poimboeuf 			idx++;
161627fce14SJosh Poimboeuf 	}
162627fce14SJosh Poimboeuf 	if (!idx)
163627fce14SJosh Poimboeuf 		return -1;
164627fce14SJosh Poimboeuf 
165627fce14SJosh Poimboeuf 
166627fce14SJosh Poimboeuf 	/* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
1671e7e4788SJosh Poimboeuf 	sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), idx);
168ce90aaf5SSimon Ser 	if (!sec)
169ce90aaf5SSimon Ser 		return -1;
170627fce14SJosh Poimboeuf 
171fb414783SMatt Helsley 	ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
172f1974222SMatt Helsley 	if (!ip_relocsec)
173627fce14SJosh Poimboeuf 		return -1;
174627fce14SJosh Poimboeuf 
175627fce14SJosh Poimboeuf 	/* create .orc_unwind section */
1761e7e4788SJosh Poimboeuf 	u_sec = elf_create_section(file->elf, ".orc_unwind", 0,
177627fce14SJosh Poimboeuf 				   sizeof(struct orc_entry), idx);
178627fce14SJosh Poimboeuf 
179627fce14SJosh Poimboeuf 	/* populate sections */
180627fce14SJosh Poimboeuf 	idx = 0;
181627fce14SJosh Poimboeuf 	for_each_sec(file, sec) {
182627fce14SJosh Poimboeuf 		if (!sec->text)
183627fce14SJosh Poimboeuf 			continue;
184627fce14SJosh Poimboeuf 
185627fce14SJosh Poimboeuf 		prev_insn = NULL;
186627fce14SJosh Poimboeuf 		sec_for_each_insn(file, sec, insn) {
187627fce14SJosh Poimboeuf 			if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
188627fce14SJosh Poimboeuf 						 sizeof(struct orc_entry))) {
189627fce14SJosh Poimboeuf 
190f1974222SMatt Helsley 				if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
191627fce14SJosh Poimboeuf 						     insn->sec, insn->offset,
192627fce14SJosh Poimboeuf 						     &insn->orc))
193627fce14SJosh Poimboeuf 					return -1;
194627fce14SJosh Poimboeuf 
195627fce14SJosh Poimboeuf 				idx++;
196627fce14SJosh Poimboeuf 			}
197627fce14SJosh Poimboeuf 			prev_insn = insn;
198627fce14SJosh Poimboeuf 		}
199627fce14SJosh Poimboeuf 
200627fce14SJosh Poimboeuf 		/* section terminator */
201627fce14SJosh Poimboeuf 		if (prev_insn) {
202f1974222SMatt Helsley 			if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
203627fce14SJosh Poimboeuf 					     prev_insn->sec,
204627fce14SJosh Poimboeuf 					     prev_insn->offset + prev_insn->len,
205627fce14SJosh Poimboeuf 					     &empty))
206627fce14SJosh Poimboeuf 				return -1;
207627fce14SJosh Poimboeuf 
208627fce14SJosh Poimboeuf 			idx++;
209627fce14SJosh Poimboeuf 		}
210627fce14SJosh Poimboeuf 	}
211627fce14SJosh Poimboeuf 
212d832c005SPeter Zijlstra 	if (elf_rebuild_reloc_section(file->elf, ip_relocsec))
213627fce14SJosh Poimboeuf 		return -1;
214627fce14SJosh Poimboeuf 
215627fce14SJosh Poimboeuf 	return 0;
216627fce14SJosh Poimboeuf }
217