xref: /openbmc/linux/tools/objtool/orc_gen.c (revision 627fce14)
1627fce14SJosh Poimboeuf /*
2627fce14SJosh Poimboeuf  * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
3627fce14SJosh Poimboeuf  *
4627fce14SJosh Poimboeuf  * This program is free software; you can redistribute it and/or
5627fce14SJosh Poimboeuf  * modify it under the terms of the GNU General Public License
6627fce14SJosh Poimboeuf  * as published by the Free Software Foundation; either version 2
7627fce14SJosh Poimboeuf  * of the License, or (at your option) any later version.
8627fce14SJosh Poimboeuf  *
9627fce14SJosh Poimboeuf  * This program is distributed in the hope that it will be useful,
10627fce14SJosh Poimboeuf  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11627fce14SJosh Poimboeuf  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12627fce14SJosh Poimboeuf  * GNU General Public License for more details.
13627fce14SJosh Poimboeuf  *
14627fce14SJosh Poimboeuf  * You should have received a copy of the GNU General Public License
15627fce14SJosh Poimboeuf  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16627fce14SJosh Poimboeuf  */
17627fce14SJosh Poimboeuf 
18627fce14SJosh Poimboeuf #include <stdlib.h>
19627fce14SJosh Poimboeuf #include <string.h>
20627fce14SJosh Poimboeuf 
21627fce14SJosh Poimboeuf #include "orc.h"
22627fce14SJosh Poimboeuf #include "check.h"
23627fce14SJosh Poimboeuf #include "warn.h"
24627fce14SJosh Poimboeuf 
25627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file)
26627fce14SJosh Poimboeuf {
27627fce14SJosh Poimboeuf 	struct instruction *insn;
28627fce14SJosh Poimboeuf 
29627fce14SJosh Poimboeuf 	for_each_insn(file, insn) {
30627fce14SJosh Poimboeuf 		struct orc_entry *orc = &insn->orc;
31627fce14SJosh Poimboeuf 		struct cfi_reg *cfa = &insn->state.cfa;
32627fce14SJosh Poimboeuf 		struct cfi_reg *bp = &insn->state.regs[CFI_BP];
33627fce14SJosh Poimboeuf 
34627fce14SJosh Poimboeuf 		if (cfa->base == CFI_UNDEFINED) {
35627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_UNDEFINED;
36627fce14SJosh Poimboeuf 			continue;
37627fce14SJosh Poimboeuf 		}
38627fce14SJosh Poimboeuf 
39627fce14SJosh Poimboeuf 		switch (cfa->base) {
40627fce14SJosh Poimboeuf 		case CFI_SP:
41627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP;
42627fce14SJosh Poimboeuf 			break;
43627fce14SJosh Poimboeuf 		case CFI_SP_INDIRECT:
44627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_SP_INDIRECT;
45627fce14SJosh Poimboeuf 			break;
46627fce14SJosh Poimboeuf 		case CFI_BP:
47627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP;
48627fce14SJosh Poimboeuf 			break;
49627fce14SJosh Poimboeuf 		case CFI_BP_INDIRECT:
50627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_BP_INDIRECT;
51627fce14SJosh Poimboeuf 			break;
52627fce14SJosh Poimboeuf 		case CFI_R10:
53627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R10;
54627fce14SJosh Poimboeuf 			break;
55627fce14SJosh Poimboeuf 		case CFI_R13:
56627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_R13;
57627fce14SJosh Poimboeuf 			break;
58627fce14SJosh Poimboeuf 		case CFI_DI:
59627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DI;
60627fce14SJosh Poimboeuf 			break;
61627fce14SJosh Poimboeuf 		case CFI_DX:
62627fce14SJosh Poimboeuf 			orc->sp_reg = ORC_REG_DX;
63627fce14SJosh Poimboeuf 			break;
64627fce14SJosh Poimboeuf 		default:
65627fce14SJosh Poimboeuf 			WARN_FUNC("unknown CFA base reg %d",
66627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, cfa->base);
67627fce14SJosh Poimboeuf 			return -1;
68627fce14SJosh Poimboeuf 		}
69627fce14SJosh Poimboeuf 
70627fce14SJosh Poimboeuf 		switch(bp->base) {
71627fce14SJosh Poimboeuf 		case CFI_UNDEFINED:
72627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_UNDEFINED;
73627fce14SJosh Poimboeuf 			break;
74627fce14SJosh Poimboeuf 		case CFI_CFA:
75627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_PREV_SP;
76627fce14SJosh Poimboeuf 			break;
77627fce14SJosh Poimboeuf 		case CFI_BP:
78627fce14SJosh Poimboeuf 			orc->bp_reg = ORC_REG_BP;
79627fce14SJosh Poimboeuf 			break;
80627fce14SJosh Poimboeuf 		default:
81627fce14SJosh Poimboeuf 			WARN_FUNC("unknown BP base reg %d",
82627fce14SJosh Poimboeuf 				  insn->sec, insn->offset, bp->base);
83627fce14SJosh Poimboeuf 			return -1;
84627fce14SJosh Poimboeuf 		}
85627fce14SJosh Poimboeuf 
86627fce14SJosh Poimboeuf 		orc->sp_offset = cfa->offset;
87627fce14SJosh Poimboeuf 		orc->bp_offset = bp->offset;
88627fce14SJosh Poimboeuf 		orc->type = insn->state.type;
89627fce14SJosh Poimboeuf 	}
90627fce14SJosh Poimboeuf 
91627fce14SJosh Poimboeuf 	return 0;
92627fce14SJosh Poimboeuf }
93627fce14SJosh Poimboeuf 
94627fce14SJosh Poimboeuf static int create_orc_entry(struct section *u_sec, struct section *ip_relasec,
95627fce14SJosh Poimboeuf 				unsigned int idx, struct section *insn_sec,
96627fce14SJosh Poimboeuf 				unsigned long insn_off, struct orc_entry *o)
97627fce14SJosh Poimboeuf {
98627fce14SJosh Poimboeuf 	struct orc_entry *orc;
99627fce14SJosh Poimboeuf 	struct rela *rela;
100627fce14SJosh Poimboeuf 
101627fce14SJosh Poimboeuf 	/* populate ORC data */
102627fce14SJosh Poimboeuf 	orc = (struct orc_entry *)u_sec->data->d_buf + idx;
103627fce14SJosh Poimboeuf 	memcpy(orc, o, sizeof(*orc));
104627fce14SJosh Poimboeuf 
105627fce14SJosh Poimboeuf 	/* populate rela for ip */
106627fce14SJosh Poimboeuf 	rela = malloc(sizeof(*rela));
107627fce14SJosh Poimboeuf 	if (!rela) {
108627fce14SJosh Poimboeuf 		perror("malloc");
109627fce14SJosh Poimboeuf 		return -1;
110627fce14SJosh Poimboeuf 	}
111627fce14SJosh Poimboeuf 	memset(rela, 0, sizeof(*rela));
112627fce14SJosh Poimboeuf 
113627fce14SJosh Poimboeuf 	rela->sym = insn_sec->sym;
114627fce14SJosh Poimboeuf 	rela->addend = insn_off;
115627fce14SJosh Poimboeuf 	rela->type = R_X86_64_PC32;
116627fce14SJosh Poimboeuf 	rela->offset = idx * sizeof(int);
117627fce14SJosh Poimboeuf 
118627fce14SJosh Poimboeuf 	list_add_tail(&rela->list, &ip_relasec->rela_list);
119627fce14SJosh Poimboeuf 	hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset);
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;
127627fce14SJosh Poimboeuf 	struct section *sec, *u_sec, *ip_relasec;
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,
133627fce14SJosh Poimboeuf 		.type    = ORC_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 */
167627fce14SJosh Poimboeuf 	sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx);
168627fce14SJosh Poimboeuf 
169627fce14SJosh Poimboeuf 	ip_relasec = elf_create_rela_section(file->elf, sec);
170627fce14SJosh Poimboeuf 	if (!ip_relasec)
171627fce14SJosh Poimboeuf 		return -1;
172627fce14SJosh Poimboeuf 
173627fce14SJosh Poimboeuf 	/* create .orc_unwind section */
174627fce14SJosh Poimboeuf 	u_sec = elf_create_section(file->elf, ".orc_unwind",
175627fce14SJosh Poimboeuf 				   sizeof(struct orc_entry), idx);
176627fce14SJosh Poimboeuf 
177627fce14SJosh Poimboeuf 	/* populate sections */
178627fce14SJosh Poimboeuf 	idx = 0;
179627fce14SJosh Poimboeuf 	for_each_sec(file, sec) {
180627fce14SJosh Poimboeuf 		if (!sec->text)
181627fce14SJosh Poimboeuf 			continue;
182627fce14SJosh Poimboeuf 
183627fce14SJosh Poimboeuf 		prev_insn = NULL;
184627fce14SJosh Poimboeuf 		sec_for_each_insn(file, sec, insn) {
185627fce14SJosh Poimboeuf 			if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
186627fce14SJosh Poimboeuf 						 sizeof(struct orc_entry))) {
187627fce14SJosh Poimboeuf 
188627fce14SJosh Poimboeuf 				if (create_orc_entry(u_sec, ip_relasec, idx,
189627fce14SJosh Poimboeuf 						     insn->sec, insn->offset,
190627fce14SJosh Poimboeuf 						     &insn->orc))
191627fce14SJosh Poimboeuf 					return -1;
192627fce14SJosh Poimboeuf 
193627fce14SJosh Poimboeuf 				idx++;
194627fce14SJosh Poimboeuf 			}
195627fce14SJosh Poimboeuf 			prev_insn = insn;
196627fce14SJosh Poimboeuf 		}
197627fce14SJosh Poimboeuf 
198627fce14SJosh Poimboeuf 		/* section terminator */
199627fce14SJosh Poimboeuf 		if (prev_insn) {
200627fce14SJosh Poimboeuf 			if (create_orc_entry(u_sec, ip_relasec, idx,
201627fce14SJosh Poimboeuf 					     prev_insn->sec,
202627fce14SJosh Poimboeuf 					     prev_insn->offset + prev_insn->len,
203627fce14SJosh Poimboeuf 					     &empty))
204627fce14SJosh Poimboeuf 				return -1;
205627fce14SJosh Poimboeuf 
206627fce14SJosh Poimboeuf 			idx++;
207627fce14SJosh Poimboeuf 		}
208627fce14SJosh Poimboeuf 	}
209627fce14SJosh Poimboeuf 
210627fce14SJosh Poimboeuf 	if (elf_rebuild_rela_section(ip_relasec))
211627fce14SJosh Poimboeuf 		return -1;
212627fce14SJosh Poimboeuf 
213627fce14SJosh Poimboeuf 	return 0;
214627fce14SJosh Poimboeuf }
215