xref: /openbmc/linux/tools/objtool/orc_gen.c (revision 8bfe2732)
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"
14*8bfe2732SVasily Gorbik #include "endianness.h"
15627fce14SJosh Poimboeuf 
16627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file)
17627fce14SJosh Poimboeuf {
18627fce14SJosh Poimboeuf 	struct instruction *insn;
19627fce14SJosh Poimboeuf 
20627fce14SJosh Poimboeuf 	for_each_insn(file, insn) {
21627fce14SJosh Poimboeuf 		struct orc_entry *orc = &insn->orc;
22e7c0219bSPeter Zijlstra 		struct cfi_reg *cfa = &insn->cfi.cfa;
23e7c0219bSPeter Zijlstra 		struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
24627fce14SJosh Poimboeuf 
253eaecac8SJulien Thierry 		if (!insn->sec->text)
263eaecac8SJulien Thierry 			continue;
273eaecac8SJulien Thierry 
28e7c0219bSPeter Zijlstra 		orc->end = insn->cfi.end;
29d31a5802SJosh Poimboeuf 
30627fce14SJosh Poimboeuf 		if (cfa->base == CFI_UNDEFINED) {
31627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_UNDEFINED;
32627fce14SJosh Poimboeuf 			continue;
33627fce14SJosh Poimboeuf 		}
34627fce14SJosh Poimboeuf 
35627fce14SJosh Poimboeuf 		switch (cfa->base) {
36627fce14SJosh Poimboeuf 		case CFI_SP:
37627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP;
38627fce14SJosh Poimboeuf 			break;
39627fce14SJosh Poimboeuf 		case CFI_SP_INDIRECT:
40627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP_INDIRECT;
41627fce14SJosh Poimboeuf 			break;
42627fce14SJosh Poimboeuf 		case CFI_BP:
43627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP;
44627fce14SJosh Poimboeuf 			break;
45627fce14SJosh Poimboeuf 		case CFI_BP_INDIRECT:
46627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP_INDIRECT;
47627fce14SJosh Poimboeuf 			break;
48627fce14SJosh Poimboeuf 		case CFI_R10:
49627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R10;
50627fce14SJosh Poimboeuf 			break;
51627fce14SJosh Poimboeuf 		case CFI_R13:
52627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R13;
53627fce14SJosh Poimboeuf 			break;
54627fce14SJosh Poimboeuf 		case CFI_DI:
55627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DI;
56627fce14SJosh Poimboeuf 			break;
57627fce14SJosh Poimboeuf 		case CFI_DX:
58627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DX;
59627fce14SJosh Poimboeuf 			break;
60627fce14SJosh Poimboeuf 		default:
61627fce14SJosh Poimboeuf 			WARN_FUNC("unknown CFA base reg %d",
62627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, cfa->base);
63627fce14SJosh Poimboeuf 			return -1;
64627fce14SJosh Poimboeuf 		}
65627fce14SJosh Poimboeuf 
66627fce14SJosh Poimboeuf 		switch(bp->base) {
67627fce14SJosh Poimboeuf 		case CFI_UNDEFINED:
68627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_UNDEFINED;
69627fce14SJosh Poimboeuf 			break;
70627fce14SJosh Poimboeuf 		case CFI_CFA:
71627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_PREV_SP;
72627fce14SJosh Poimboeuf 			break;
73627fce14SJosh Poimboeuf 		case CFI_BP:
74627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_BP;
75627fce14SJosh Poimboeuf 			break;
76627fce14SJosh Poimboeuf 		default:
77627fce14SJosh Poimboeuf 			WARN_FUNC("unknown BP base reg %d",
78627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, bp->base);
79627fce14SJosh Poimboeuf 			return -1;
80627fce14SJosh Poimboeuf 		}
81627fce14SJosh Poimboeuf 
82627fce14SJosh Poimboeuf 		orc->sp_offset = cfa->offset;
83627fce14SJosh Poimboeuf 		orc->bp_offset = bp->offset;
84e7c0219bSPeter Zijlstra 		orc->type = insn->cfi.type;
85627fce14SJosh Poimboeuf 	}
86627fce14SJosh Poimboeuf 
87627fce14SJosh Poimboeuf 	return 0;
88627fce14SJosh Poimboeuf }
89627fce14SJosh Poimboeuf 
90f1974222SMatt Helsley static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec,
91627fce14SJosh Poimboeuf 				unsigned int idx, struct section *insn_sec,
92627fce14SJosh Poimboeuf 				unsigned long insn_off, struct orc_entry *o)
93627fce14SJosh Poimboeuf {
94627fce14SJosh Poimboeuf 	struct orc_entry *orc;
95f1974222SMatt Helsley 	struct reloc *reloc;
96627fce14SJosh Poimboeuf 
97627fce14SJosh Poimboeuf 	/* populate ORC data */
98627fce14SJosh Poimboeuf 	orc = (struct orc_entry *)u_sec->data->d_buf + idx;
99627fce14SJosh Poimboeuf 	memcpy(orc, o, sizeof(*orc));
100*8bfe2732SVasily Gorbik 	orc->sp_offset = bswap_if_needed(orc->sp_offset);
101*8bfe2732SVasily Gorbik 	orc->bp_offset = bswap_if_needed(orc->bp_offset);
102627fce14SJosh Poimboeuf 
103f1974222SMatt Helsley 	/* populate reloc for ip */
104f1974222SMatt Helsley 	reloc = malloc(sizeof(*reloc));
105f1974222SMatt Helsley 	if (!reloc) {
106627fce14SJosh Poimboeuf 		perror("malloc");
107627fce14SJosh Poimboeuf 		return -1;
108627fce14SJosh Poimboeuf 	}
109f1974222SMatt Helsley 	memset(reloc, 0, sizeof(*reloc));
110627fce14SJosh Poimboeuf 
11144f6a7c0SJosh Poimboeuf 	insn_to_reloc_sym_addend(insn_sec, insn_off, reloc);
112f1974222SMatt Helsley 	if (!reloc->sym) {
11344f6a7c0SJosh Poimboeuf 		WARN("missing symbol for insn at offset 0x%lx",
114e81e0724SJosh Poimboeuf 		     insn_off);
115e81e0724SJosh Poimboeuf 		return -1;
116e81e0724SJosh Poimboeuf 	}
117e81e0724SJosh Poimboeuf 
118f1974222SMatt Helsley 	reloc->type = R_X86_64_PC32;
119f1974222SMatt Helsley 	reloc->offset = idx * sizeof(int);
120f1974222SMatt Helsley 	reloc->sec = ip_relocsec;
121627fce14SJosh Poimboeuf 
122f1974222SMatt Helsley 	elf_add_reloc(elf, reloc);
123627fce14SJosh Poimboeuf 
124627fce14SJosh Poimboeuf 	return 0;
125627fce14SJosh Poimboeuf }
126627fce14SJosh Poimboeuf 
127627fce14SJosh Poimboeuf int create_orc_sections(struct objtool_file *file)
128627fce14SJosh Poimboeuf {
129627fce14SJosh Poimboeuf 	struct instruction *insn, *prev_insn;
130f1974222SMatt Helsley 	struct section *sec, *u_sec, *ip_relocsec;
131627fce14SJosh Poimboeuf 	unsigned int idx;
132627fce14SJosh Poimboeuf 
133627fce14SJosh Poimboeuf 	struct orc_entry empty = {
134627fce14SJosh Poimboeuf 		.sp_reg = ORC_REG_UNDEFINED,
135627fce14SJosh Poimboeuf 		.bp_reg  = ORC_REG_UNDEFINED,
136ee819aedSJulien Thierry 		.type    = UNWIND_HINT_TYPE_CALL,
137627fce14SJosh Poimboeuf 	};
138627fce14SJosh Poimboeuf 
139627fce14SJosh Poimboeuf 	sec = find_section_by_name(file->elf, ".orc_unwind");
140627fce14SJosh Poimboeuf 	if (sec) {
141627fce14SJosh Poimboeuf 		WARN("file already has .orc_unwind section, skipping");
142627fce14SJosh Poimboeuf 		return -1;
143627fce14SJosh Poimboeuf 	}
144627fce14SJosh Poimboeuf 
145627fce14SJosh Poimboeuf 	/* count the number of needed orcs */
146627fce14SJosh Poimboeuf 	idx = 0;
147627fce14SJosh Poimboeuf 	for_each_sec(file, sec) {
148627fce14SJosh Poimboeuf 		if (!sec->text)
149627fce14SJosh Poimboeuf 			continue;
150627fce14SJosh Poimboeuf 
151627fce14SJosh Poimboeuf 		prev_insn = NULL;
152627fce14SJosh Poimboeuf 		sec_for_each_insn(file, sec, insn) {
153627fce14SJosh Poimboeuf 			if (!prev_insn ||
154627fce14SJosh Poimboeuf 			    memcmp(&insn->orc, &prev_insn->orc,
155627fce14SJosh Poimboeuf 				   sizeof(struct orc_entry))) {
156627fce14SJosh Poimboeuf 				idx++;
157627fce14SJosh Poimboeuf 			}
158627fce14SJosh Poimboeuf 			prev_insn = insn;
159627fce14SJosh Poimboeuf 		}
160627fce14SJosh Poimboeuf 
161627fce14SJosh Poimboeuf 		/* section terminator */
162627fce14SJosh Poimboeuf 		if (prev_insn)
163627fce14SJosh Poimboeuf 			idx++;
164627fce14SJosh Poimboeuf 	}
165627fce14SJosh Poimboeuf 	if (!idx)
166627fce14SJosh Poimboeuf 		return -1;
167627fce14SJosh Poimboeuf 
168627fce14SJosh Poimboeuf 
169627fce14SJosh Poimboeuf 	/* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
1701e7e4788SJosh Poimboeuf 	sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), idx);
171ce90aaf5SSimon Ser 	if (!sec)
172ce90aaf5SSimon Ser 		return -1;
173627fce14SJosh Poimboeuf 
174fb414783SMatt Helsley 	ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
175f1974222SMatt Helsley 	if (!ip_relocsec)
176627fce14SJosh Poimboeuf 		return -1;
177627fce14SJosh Poimboeuf 
178627fce14SJosh Poimboeuf 	/* create .orc_unwind section */
1791e7e4788SJosh Poimboeuf 	u_sec = elf_create_section(file->elf, ".orc_unwind", 0,
180627fce14SJosh Poimboeuf 				   sizeof(struct orc_entry), idx);
181627fce14SJosh Poimboeuf 
182627fce14SJosh Poimboeuf 	/* populate sections */
183627fce14SJosh Poimboeuf 	idx = 0;
184627fce14SJosh Poimboeuf 	for_each_sec(file, sec) {
185627fce14SJosh Poimboeuf 		if (!sec->text)
186627fce14SJosh Poimboeuf 			continue;
187627fce14SJosh Poimboeuf 
188627fce14SJosh Poimboeuf 		prev_insn = NULL;
189627fce14SJosh Poimboeuf 		sec_for_each_insn(file, sec, insn) {
190627fce14SJosh Poimboeuf 			if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
191627fce14SJosh Poimboeuf 						 sizeof(struct orc_entry))) {
192627fce14SJosh Poimboeuf 
193f1974222SMatt Helsley 				if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
194627fce14SJosh Poimboeuf 						     insn->sec, insn->offset,
195627fce14SJosh Poimboeuf 						     &insn->orc))
196627fce14SJosh Poimboeuf 					return -1;
197627fce14SJosh Poimboeuf 
198627fce14SJosh Poimboeuf 				idx++;
199627fce14SJosh Poimboeuf 			}
200627fce14SJosh Poimboeuf 			prev_insn = insn;
201627fce14SJosh Poimboeuf 		}
202627fce14SJosh Poimboeuf 
203627fce14SJosh Poimboeuf 		/* section terminator */
204627fce14SJosh Poimboeuf 		if (prev_insn) {
205f1974222SMatt Helsley 			if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
206627fce14SJosh Poimboeuf 					     prev_insn->sec,
207627fce14SJosh Poimboeuf 					     prev_insn->offset + prev_insn->len,
208627fce14SJosh Poimboeuf 					     &empty))
209627fce14SJosh Poimboeuf 				return -1;
210627fce14SJosh Poimboeuf 
211627fce14SJosh Poimboeuf 			idx++;
212627fce14SJosh Poimboeuf 		}
213627fce14SJosh Poimboeuf 	}
214627fce14SJosh Poimboeuf 
215d832c005SPeter Zijlstra 	if (elf_rebuild_reloc_section(file->elf, ip_relocsec))
216627fce14SJosh Poimboeuf 		return -1;
217627fce14SJosh Poimboeuf 
218627fce14SJosh Poimboeuf 	return 0;
219627fce14SJosh Poimboeuf }
220