]>
Commit | Line | Data |
---|---|---|
59d5af67 | 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
321d628a FG |
2 | From: Josh Poimboeuf <jpoimboe@redhat.com> |
3 | Date: Tue, 11 Jul 2017 10:33:42 -0500 | |
59d5af67 | 4 | Subject: [PATCH] objtool: Add ORC unwind table generation |
321d628a FG |
5 | MIME-Version: 1.0 |
6 | Content-Type: text/plain; charset=UTF-8 | |
7 | Content-Transfer-Encoding: 8bit | |
8 | ||
9 | CVE-2017-5754 | |
10 | ||
11 | Now that objtool knows the states of all registers on the stack for each | |
12 | instruction, it's straightforward to generate debuginfo for an unwinder | |
13 | to use. | |
14 | ||
15 | Instead of generating DWARF, generate a new format called ORC, which is | |
16 | more suitable for an in-kernel unwinder. See | |
17 | Documentation/x86/orc-unwinder.txt for a more detailed description of | |
18 | this new debuginfo format and why it's preferable to DWARF. | |
19 | ||
20 | Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> | |
21 | Cc: Andy Lutomirski <luto@kernel.org> | |
22 | Cc: Borislav Petkov <bp@alien8.de> | |
23 | Cc: Brian Gerst <brgerst@gmail.com> | |
24 | Cc: Denys Vlasenko <dvlasenk@redhat.com> | |
25 | Cc: H. Peter Anvin <hpa@zytor.com> | |
26 | Cc: Jiri Slaby <jslaby@suse.cz> | |
27 | Cc: Linus Torvalds <torvalds@linux-foundation.org> | |
28 | Cc: Mike Galbraith <efault@gmx.de> | |
29 | Cc: Peter Zijlstra <peterz@infradead.org> | |
30 | Cc: Thomas Gleixner <tglx@linutronix.de> | |
31 | Cc: live-patching@vger.kernel.org | |
32 | Link: http://lkml.kernel.org/r/c9b9f01ba6c5ed2bdc9bb0957b78167fdbf9632e.1499786555.git.jpoimboe@redhat.com | |
33 | Signed-off-by: Ingo Molnar <mingo@kernel.org> | |
34 | (cherry picked from commit 627fce14809ba5610b0cb476cd0186d3fcedecfc) | |
35 | Signed-off-by: Andy Whitcroft <apw@canonical.com> | |
36 | Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com> | |
37 | (cherry picked from commit 9460f7766786ad0f8330f78f22b81842632a5398) | |
38 | Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> | |
39 | --- | |
40 | tools/objtool/Documentation/stack-validation.txt | 56 ++---- | |
41 | tools/objtool/builtin.h | 1 + | |
42 | tools/objtool/check.h | 15 +- | |
43 | tools/objtool/elf.h | 15 +- | |
44 | tools/objtool/orc.h | 30 ++++ | |
45 | tools/objtool/orc_types.h | 85 +++++++++ | |
46 | tools/objtool/builtin-check.c | 2 +- | |
47 | tools/objtool/builtin-orc.c | 70 ++++++++ | |
48 | tools/objtool/check.c | 58 +++++- | |
49 | tools/objtool/elf.c | 212 ++++++++++++++++++++-- | |
50 | tools/objtool/objtool.c | 3 +- | |
51 | tools/objtool/orc_dump.c | 212 ++++++++++++++++++++++ | |
52 | tools/objtool/orc_gen.c | 214 +++++++++++++++++++++++ | |
53 | tools/objtool/Build | 3 + | |
54 | 14 files changed, 916 insertions(+), 60 deletions(-) | |
55 | create mode 100644 tools/objtool/orc.h | |
56 | create mode 100644 tools/objtool/orc_types.h | |
57 | create mode 100644 tools/objtool/builtin-orc.c | |
58 | create mode 100644 tools/objtool/orc_dump.c | |
59 | create mode 100644 tools/objtool/orc_gen.c | |
60 | ||
61 | diff --git a/tools/objtool/Documentation/stack-validation.txt b/tools/objtool/Documentation/stack-validation.txt | |
62 | index 17c1195f11f4..6a1af43862df 100644 | |
63 | --- a/tools/objtool/Documentation/stack-validation.txt | |
64 | +++ b/tools/objtool/Documentation/stack-validation.txt | |
65 | @@ -11,9 +11,6 @@ analyzes every .o file and ensures the validity of its stack metadata. | |
66 | It enforces a set of rules on asm code and C inline assembly code so | |
67 | that stack traces can be reliable. | |
68 | ||
69 | -Currently it only checks frame pointer usage, but there are plans to add | |
70 | -CFI validation for C files and CFI generation for asm files. | |
71 | - | |
72 | For each function, it recursively follows all possible code paths and | |
73 | validates the correct frame pointer state at each instruction. | |
74 | ||
75 | @@ -23,6 +20,10 @@ alternative execution paths to a given instruction (or set of | |
76 | instructions). Similarly, it knows how to follow switch statements, for | |
77 | which gcc sometimes uses jump tables. | |
78 | ||
79 | +(Objtool also has an 'orc generate' subcommand which generates debuginfo | |
80 | +for the ORC unwinder. See Documentation/x86/orc-unwinder.txt in the | |
81 | +kernel tree for more details.) | |
82 | + | |
83 | ||
84 | Why do we need stack metadata validation? | |
85 | ----------------------------------------- | |
86 | @@ -93,37 +94,14 @@ a) More reliable stack traces for frame pointer enabled kernels | |
87 | or at the very end of the function after the stack frame has been | |
88 | destroyed. This is an inherent limitation of frame pointers. | |
89 | ||
90 | -b) 100% reliable stack traces for DWARF enabled kernels | |
91 | - | |
92 | - (NOTE: This is not yet implemented) | |
93 | - | |
94 | - As an alternative to frame pointers, DWARF Call Frame Information | |
95 | - (CFI) metadata can be used to walk the stack. Unlike frame pointers, | |
96 | - CFI metadata is out of band. So it doesn't affect runtime | |
97 | - performance and it can be reliable even when interrupts or exceptions | |
98 | - are involved. | |
99 | - | |
100 | - For C code, gcc automatically generates DWARF CFI metadata. But for | |
101 | - asm code, generating CFI is a tedious manual approach which requires | |
102 | - manually placed .cfi assembler macros to be scattered throughout the | |
103 | - code. It's clumsy and very easy to get wrong, and it makes the real | |
104 | - code harder to read. | |
105 | - | |
106 | - Stacktool will improve this situation in several ways. For code | |
107 | - which already has CFI annotations, it will validate them. For code | |
108 | - which doesn't have CFI annotations, it will generate them. So an | |
109 | - architecture can opt to strip out all the manual .cfi annotations | |
110 | - from their asm code and have objtool generate them instead. | |
111 | +b) ORC (Oops Rewind Capability) unwind table generation | |
112 | ||
113 | - We might also add a runtime stack validation debug option where we | |
114 | - periodically walk the stack from schedule() and/or an NMI to ensure | |
115 | - that the stack metadata is sane and that we reach the bottom of the | |
116 | - stack. | |
117 | + An alternative to frame pointers and DWARF, ORC unwind data can be | |
118 | + used to walk the stack. Unlike frame pointers, ORC data is out of | |
119 | + band. So it doesn't affect runtime performance and it can be | |
120 | + reliable even when interrupts or exceptions are involved. | |
121 | ||
122 | - So the benefit of objtool here will be that external tooling should | |
123 | - always show perfect stack traces. And the same will be true for | |
124 | - kernel warning/oops traces if the architecture has a runtime DWARF | |
125 | - unwinder. | |
126 | + For more details, see Documentation/x86/orc-unwinder.txt. | |
127 | ||
128 | c) Higher live patching compatibility rate | |
129 | ||
130 | @@ -211,7 +189,7 @@ they mean, and suggestions for how to fix them. | |
131 | function, add proper frame pointer logic using the FRAME_BEGIN and | |
132 | FRAME_END macros. Otherwise, if it's not a callable function, remove | |
133 | its ELF function annotation by changing ENDPROC to END, and instead | |
134 | - use the manual CFI hint macros in asm/undwarf.h. | |
135 | + use the manual unwind hint macros in asm/unwind_hints.h. | |
136 | ||
137 | If it's a GCC-compiled .c file, the error may be because the function | |
138 | uses an inline asm() statement which has a "call" instruction. An | |
139 | @@ -231,8 +209,8 @@ they mean, and suggestions for how to fix them. | |
140 | If the error is for an asm file, and the instruction is inside (or | |
141 | reachable from) a callable function, the function should be annotated | |
142 | with the ENTRY/ENDPROC macros (ENDPROC is the important one). | |
143 | - Otherwise, the code should probably be annotated with the CFI hint | |
144 | - macros in asm/undwarf.h so objtool and the unwinder can know the | |
145 | + Otherwise, the code should probably be annotated with the unwind hint | |
146 | + macros in asm/unwind_hints.h so objtool and the unwinder can know the | |
147 | stack state associated with the code. | |
148 | ||
149 | If you're 100% sure the code won't affect stack traces, or if you're | |
150 | @@ -258,7 +236,7 @@ they mean, and suggestions for how to fix them. | |
151 | instructions aren't allowed in a callable function, and are most | |
152 | likely part of the kernel entry code. They should usually not have | |
153 | the callable function annotation (ENDPROC) and should always be | |
154 | - annotated with the CFI hint macros in asm/undwarf.h. | |
155 | + annotated with the unwind hint macros in asm/unwind_hints.h. | |
156 | ||
157 | ||
158 | 6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame | |
159 | @@ -272,7 +250,7 @@ they mean, and suggestions for how to fix them. | |
160 | ||
161 | If the instruction is not actually in a callable function (e.g. | |
162 | kernel entry code), change ENDPROC to END and annotate manually with | |
163 | - the CFI hint macros in asm/undwarf.h. | |
164 | + the unwind hint macros in asm/unwind_hints.h. | |
165 | ||
166 | ||
167 | 7. file: warning: objtool: func()+0x5c: stack state mismatch | |
168 | @@ -288,8 +266,8 @@ they mean, and suggestions for how to fix them. | |
169 | ||
170 | Another possibility is that the code has some asm or inline asm which | |
171 | does some unusual things to the stack or the frame pointer. In such | |
172 | - cases it's probably appropriate to use the CFI hint macros in | |
173 | - asm/undwarf.h. | |
174 | + cases it's probably appropriate to use the unwind hint macros in | |
175 | + asm/unwind_hints.h. | |
176 | ||
177 | ||
178 | 8. file.o: warning: objtool: funcA() falls through to next function funcB() | |
179 | diff --git a/tools/objtool/builtin.h b/tools/objtool/builtin.h | |
180 | index 34d2ba78a616..dd526067fed5 100644 | |
181 | --- a/tools/objtool/builtin.h | |
182 | +++ b/tools/objtool/builtin.h | |
183 | @@ -18,5 +18,6 @@ | |
184 | #define _BUILTIN_H | |
185 | ||
186 | extern int cmd_check(int argc, const char **argv); | |
187 | +extern int cmd_orc(int argc, const char **argv); | |
188 | ||
189 | #endif /* _BUILTIN_H */ | |
190 | diff --git a/tools/objtool/check.h b/tools/objtool/check.h | |
191 | index da85f5b00ec6..046874bbe226 100644 | |
192 | --- a/tools/objtool/check.h | |
193 | +++ b/tools/objtool/check.h | |
194 | @@ -22,12 +22,14 @@ | |
195 | #include "elf.h" | |
196 | #include "cfi.h" | |
197 | #include "arch.h" | |
198 | +#include "orc.h" | |
199 | #include <linux/hashtable.h> | |
200 | ||
201 | struct insn_state { | |
202 | struct cfi_reg cfa; | |
203 | struct cfi_reg regs[CFI_NUM_REGS]; | |
204 | int stack_size; | |
205 | + unsigned char type; | |
206 | bool bp_scratch; | |
207 | bool drap; | |
208 | int drap_reg; | |
209 | @@ -48,6 +50,7 @@ struct instruction { | |
210 | struct symbol *func; | |
211 | struct stack_op stack_op; | |
212 | struct insn_state state; | |
213 | + struct orc_entry orc; | |
214 | }; | |
215 | ||
216 | struct objtool_file { | |
217 | @@ -58,9 +61,19 @@ struct objtool_file { | |
218 | bool ignore_unreachables, c_file; | |
219 | }; | |
220 | ||
221 | -int check(const char *objname, bool nofp); | |
222 | +int check(const char *objname, bool nofp, bool orc); | |
223 | + | |
224 | +struct instruction *find_insn(struct objtool_file *file, | |
225 | + struct section *sec, unsigned long offset); | |
226 | ||
227 | #define for_each_insn(file, insn) \ | |
228 | list_for_each_entry(insn, &file->insn_list, list) | |
229 | ||
230 | +#define sec_for_each_insn(file, sec, insn) \ | |
231 | + for (insn = find_insn(file, sec, 0); \ | |
232 | + insn && &insn->list != &file->insn_list && \ | |
233 | + insn->sec == sec; \ | |
234 | + insn = list_next_entry(insn, list)) | |
235 | + | |
236 | + | |
237 | #endif /* _CHECK_H */ | |
238 | diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h | |
239 | index 343968b778cb..d86e2ff14466 100644 | |
240 | --- a/tools/objtool/elf.h | |
241 | +++ b/tools/objtool/elf.h | |
242 | @@ -28,6 +28,13 @@ | |
243 | # define elf_getshdrstrndx elf_getshstrndx | |
244 | #endif | |
245 | ||
246 | +/* | |
247 | + * Fallback for systems without this "read, mmaping if possible" cmd. | |
248 | + */ | |
249 | +#ifndef ELF_C_READ_MMAP | |
250 | +#define ELF_C_READ_MMAP ELF_C_READ | |
251 | +#endif | |
252 | + | |
253 | struct section { | |
254 | struct list_head list; | |
255 | GElf_Shdr sh; | |
256 | @@ -41,6 +48,7 @@ struct section { | |
257 | char *name; | |
258 | int idx; | |
259 | unsigned int len; | |
260 | + bool changed, text; | |
261 | }; | |
262 | ||
263 | struct symbol { | |
264 | @@ -75,7 +83,7 @@ struct elf { | |
265 | }; | |
266 | ||
267 | ||
268 | -struct elf *elf_open(const char *name); | |
269 | +struct elf *elf_open(const char *name, int flags); | |
270 | struct section *find_section_by_name(struct elf *elf, const char *name); | |
271 | struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); | |
272 | struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); | |
273 | @@ -83,6 +91,11 @@ struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); | |
274 | struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, | |
275 | unsigned int len); | |
276 | struct symbol *find_containing_func(struct section *sec, unsigned long offset); | |
277 | +struct section *elf_create_section(struct elf *elf, const char *name, size_t | |
278 | + entsize, int nr); | |
279 | +struct section *elf_create_rela_section(struct elf *elf, struct section *base); | |
280 | +int elf_rebuild_rela_section(struct section *sec); | |
281 | +int elf_write(struct elf *elf); | |
282 | void elf_close(struct elf *elf); | |
283 | ||
284 | #define for_each_sec(file, sec) \ | |
285 | diff --git a/tools/objtool/orc.h b/tools/objtool/orc.h | |
286 | new file mode 100644 | |
287 | index 000000000000..a4139e386ef3 | |
288 | --- /dev/null | |
289 | +++ b/tools/objtool/orc.h | |
290 | @@ -0,0 +1,30 @@ | |
291 | +/* | |
292 | + * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> | |
293 | + * | |
294 | + * This program is free software; you can redistribute it and/or | |
295 | + * modify it under the terms of the GNU General Public License | |
296 | + * as published by the Free Software Foundation; either version 2 | |
297 | + * of the License, or (at your option) any later version. | |
298 | + * | |
299 | + * This program is distributed in the hope that it will be useful, | |
300 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
301 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
302 | + * GNU General Public License for more details. | |
303 | + * | |
304 | + * You should have received a copy of the GNU General Public License | |
305 | + * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
306 | + */ | |
307 | + | |
308 | +#ifndef _ORC_H | |
309 | +#define _ORC_H | |
310 | + | |
311 | +#include "orc_types.h" | |
312 | + | |
313 | +struct objtool_file; | |
314 | + | |
315 | +int create_orc(struct objtool_file *file); | |
316 | +int create_orc_sections(struct objtool_file *file); | |
317 | + | |
318 | +int orc_dump(const char *objname); | |
319 | + | |
320 | +#endif /* _ORC_H */ | |
321 | diff --git a/tools/objtool/orc_types.h b/tools/objtool/orc_types.h | |
322 | new file mode 100644 | |
323 | index 000000000000..fc5cf6cffd9a | |
324 | --- /dev/null | |
325 | +++ b/tools/objtool/orc_types.h | |
326 | @@ -0,0 +1,85 @@ | |
327 | +/* | |
328 | + * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> | |
329 | + * | |
330 | + * This program is free software; you can redistribute it and/or | |
331 | + * modify it under the terms of the GNU General Public License | |
332 | + * as published by the Free Software Foundation; either version 2 | |
333 | + * of the License, or (at your option) any later version. | |
334 | + * | |
335 | + * This program is distributed in the hope that it will be useful, | |
336 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
337 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
338 | + * GNU General Public License for more details. | |
339 | + * | |
340 | + * You should have received a copy of the GNU General Public License | |
341 | + * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
342 | + */ | |
343 | + | |
344 | +#ifndef _ORC_TYPES_H | |
345 | +#define _ORC_TYPES_H | |
346 | + | |
347 | +#include <linux/types.h> | |
348 | +#include <linux/compiler.h> | |
349 | + | |
350 | +/* | |
351 | + * The ORC_REG_* registers are base registers which are used to find other | |
352 | + * registers on the stack. | |
353 | + * | |
354 | + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the | |
355 | + * address of the previous frame: the caller's SP before it called the current | |
356 | + * function. | |
357 | + * | |
358 | + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in | |
359 | + * the current frame. | |
360 | + * | |
361 | + * The most commonly used base registers are SP and BP -- which the previous SP | |
362 | + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is | |
363 | + * usually based on. | |
364 | + * | |
365 | + * The rest of the base registers are needed for special cases like entry code | |
366 | + * and GCC realigned stacks. | |
367 | + */ | |
368 | +#define ORC_REG_UNDEFINED 0 | |
369 | +#define ORC_REG_PREV_SP 1 | |
370 | +#define ORC_REG_DX 2 | |
371 | +#define ORC_REG_DI 3 | |
372 | +#define ORC_REG_BP 4 | |
373 | +#define ORC_REG_SP 5 | |
374 | +#define ORC_REG_R10 6 | |
375 | +#define ORC_REG_R13 7 | |
376 | +#define ORC_REG_BP_INDIRECT 8 | |
377 | +#define ORC_REG_SP_INDIRECT 9 | |
378 | +#define ORC_REG_MAX 15 | |
379 | + | |
380 | +/* | |
381 | + * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the | |
382 | + * caller's SP right before it made the call). Used for all callable | |
383 | + * functions, i.e. all C code and all callable asm functions. | |
384 | + * | |
385 | + * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points | |
386 | + * to a fully populated pt_regs from a syscall, interrupt, or exception. | |
387 | + * | |
388 | + * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset | |
389 | + * points to the iret return frame. | |
390 | + */ | |
391 | +#define ORC_TYPE_CALL 0 | |
392 | +#define ORC_TYPE_REGS 1 | |
393 | +#define ORC_TYPE_REGS_IRET 2 | |
394 | + | |
395 | +/* | |
396 | + * This struct is more or less a vastly simplified version of the DWARF Call | |
397 | + * Frame Information standard. It contains only the necessary parts of DWARF | |
398 | + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the | |
399 | + * unwinder how to find the previous SP and BP (and sometimes entry regs) on | |
400 | + * the stack for a given code address. Each instance of the struct corresponds | |
401 | + * to one or more code locations. | |
402 | + */ | |
403 | +struct orc_entry { | |
404 | + s16 sp_offset; | |
405 | + s16 bp_offset; | |
406 | + unsigned sp_reg:4; | |
407 | + unsigned bp_reg:4; | |
408 | + unsigned type:2; | |
409 | +} __packed; | |
410 | + | |
411 | +#endif /* _ORC_TYPES_H */ | |
412 | diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c | |
413 | index 365c34ecab26..eedf089b1495 100644 | |
414 | --- a/tools/objtool/builtin-check.c | |
415 | +++ b/tools/objtool/builtin-check.c | |
416 | @@ -52,5 +52,5 @@ int cmd_check(int argc, const char **argv) | |
417 | ||
418 | objname = argv[0]; | |
419 | ||
420 | - return check(objname, nofp); | |
421 | + return check(objname, nofp, false); | |
422 | } | |
423 | diff --git a/tools/objtool/builtin-orc.c b/tools/objtool/builtin-orc.c | |
424 | new file mode 100644 | |
425 | index 000000000000..5ca41ab0df48 | |
426 | --- /dev/null | |
427 | +++ b/tools/objtool/builtin-orc.c | |
428 | @@ -0,0 +1,70 @@ | |
429 | +/* | |
430 | + * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> | |
431 | + * | |
432 | + * This program is free software; you can redistribute it and/or | |
433 | + * modify it under the terms of the GNU General Public License | |
434 | + * as published by the Free Software Foundation; either version 2 | |
435 | + * of the License, or (at your option) any later version. | |
436 | + * | |
437 | + * This program is distributed in the hope that it will be useful, | |
438 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
439 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
440 | + * GNU General Public License for more details. | |
441 | + * | |
442 | + * You should have received a copy of the GNU General Public License | |
443 | + * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
444 | + */ | |
445 | + | |
446 | +/* | |
447 | + * objtool orc: | |
448 | + * | |
449 | + * This command analyzes a .o file and adds .orc_unwind and .orc_unwind_ip | |
450 | + * sections to it, which is used by the in-kernel ORC unwinder. | |
451 | + * | |
452 | + * This command is a superset of "objtool check". | |
453 | + */ | |
454 | + | |
455 | +#include <string.h> | |
456 | +#include <subcmd/parse-options.h> | |
457 | +#include "builtin.h" | |
458 | +#include "check.h" | |
459 | + | |
460 | + | |
461 | +static const char *orc_usage[] = { | |
462 | + "objtool orc generate [<options>] file.o", | |
463 | + "objtool orc dump file.o", | |
464 | + NULL, | |
465 | +}; | |
466 | + | |
467 | +extern const struct option check_options[]; | |
468 | +extern bool nofp; | |
469 | + | |
470 | +int cmd_orc(int argc, const char **argv) | |
471 | +{ | |
472 | + const char *objname; | |
473 | + | |
474 | + argc--; argv++; | |
475 | + if (!strncmp(argv[0], "gen", 3)) { | |
476 | + argc = parse_options(argc, argv, check_options, orc_usage, 0); | |
477 | + if (argc != 1) | |
478 | + usage_with_options(orc_usage, check_options); | |
479 | + | |
480 | + objname = argv[0]; | |
481 | + | |
482 | + return check(objname, nofp, true); | |
483 | + | |
484 | + } | |
485 | + | |
486 | + if (!strcmp(argv[0], "dump")) { | |
487 | + if (argc != 2) | |
488 | + usage_with_options(orc_usage, check_options); | |
489 | + | |
490 | + objname = argv[1]; | |
491 | + | |
492 | + return orc_dump(objname); | |
493 | + } | |
494 | + | |
495 | + usage_with_options(orc_usage, check_options); | |
496 | + | |
497 | + return 0; | |
498 | +} | |
499 | diff --git a/tools/objtool/check.c b/tools/objtool/check.c | |
500 | index 2c6d74880403..cb57c526ba17 100644 | |
501 | --- a/tools/objtool/check.c | |
502 | +++ b/tools/objtool/check.c | |
503 | @@ -36,8 +36,8 @@ const char *objname; | |
504 | static bool nofp; | |
505 | struct cfi_state initial_func_cfi; | |
506 | ||
507 | -static struct instruction *find_insn(struct objtool_file *file, | |
508 | - struct section *sec, unsigned long offset) | |
509 | +struct instruction *find_insn(struct objtool_file *file, | |
510 | + struct section *sec, unsigned long offset) | |
511 | { | |
512 | struct instruction *insn; | |
513 | ||
514 | @@ -259,6 +259,11 @@ static int decode_instructions(struct objtool_file *file) | |
515 | if (!(sec->sh.sh_flags & SHF_EXECINSTR)) | |
516 | continue; | |
517 | ||
518 | + if (strcmp(sec->name, ".altinstr_replacement") && | |
519 | + strcmp(sec->name, ".altinstr_aux") && | |
520 | + strncmp(sec->name, ".discard.", 9)) | |
521 | + sec->text = true; | |
522 | + | |
523 | for (offset = 0; offset < sec->len; offset += insn->len) { | |
524 | insn = malloc(sizeof(*insn)); | |
525 | if (!insn) { | |
526 | @@ -947,6 +952,30 @@ static bool has_valid_stack_frame(struct insn_state *state) | |
527 | return false; | |
528 | } | |
529 | ||
530 | +static int update_insn_state_regs(struct instruction *insn, struct insn_state *state) | |
531 | +{ | |
532 | + struct cfi_reg *cfa = &state->cfa; | |
533 | + struct stack_op *op = &insn->stack_op; | |
534 | + | |
535 | + if (cfa->base != CFI_SP) | |
536 | + return 0; | |
537 | + | |
538 | + /* push */ | |
539 | + if (op->dest.type == OP_DEST_PUSH) | |
540 | + cfa->offset += 8; | |
541 | + | |
542 | + /* pop */ | |
543 | + if (op->src.type == OP_SRC_POP) | |
544 | + cfa->offset -= 8; | |
545 | + | |
546 | + /* add immediate to sp */ | |
547 | + if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD && | |
548 | + op->dest.reg == CFI_SP && op->src.reg == CFI_SP) | |
549 | + cfa->offset -= op->src.offset; | |
550 | + | |
551 | + return 0; | |
552 | +} | |
553 | + | |
554 | static void save_reg(struct insn_state *state, unsigned char reg, int base, | |
555 | int offset) | |
556 | { | |
557 | @@ -1032,6 +1061,9 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state) | |
558 | return 0; | |
559 | } | |
560 | ||
561 | + if (state->type == ORC_TYPE_REGS || state->type == ORC_TYPE_REGS_IRET) | |
562 | + return update_insn_state_regs(insn, state); | |
563 | + | |
564 | switch (op->dest.type) { | |
565 | ||
566 | case OP_DEST_REG: | |
567 | @@ -1323,6 +1355,10 @@ static bool insn_state_match(struct instruction *insn, struct insn_state *state) | |
568 | break; | |
569 | } | |
570 | ||
571 | + } else if (state1->type != state2->type) { | |
572 | + WARN_FUNC("stack state mismatch: type1=%d type2=%d", | |
573 | + insn->sec, insn->offset, state1->type, state2->type); | |
574 | + | |
575 | } else if (state1->drap != state2->drap || | |
576 | (state1->drap && state1->drap_reg != state2->drap_reg)) { | |
577 | WARN_FUNC("stack state mismatch: drap1=%d(%d) drap2=%d(%d)", | |
578 | @@ -1613,7 +1649,7 @@ static void cleanup(struct objtool_file *file) | |
579 | elf_close(file->elf); | |
580 | } | |
581 | ||
582 | -int check(const char *_objname, bool _nofp) | |
583 | +int check(const char *_objname, bool _nofp, bool orc) | |
584 | { | |
585 | struct objtool_file file; | |
586 | int ret, warnings = 0; | |
587 | @@ -1621,7 +1657,7 @@ int check(const char *_objname, bool _nofp) | |
588 | objname = _objname; | |
589 | nofp = _nofp; | |
590 | ||
591 | - file.elf = elf_open(objname); | |
592 | + file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY); | |
593 | if (!file.elf) | |
594 | return 1; | |
595 | ||
596 | @@ -1654,6 +1690,20 @@ int check(const char *_objname, bool _nofp) | |
597 | warnings += ret; | |
598 | } | |
599 | ||
600 | + if (orc) { | |
601 | + ret = create_orc(&file); | |
602 | + if (ret < 0) | |
603 | + goto out; | |
604 | + | |
605 | + ret = create_orc_sections(&file); | |
606 | + if (ret < 0) | |
607 | + goto out; | |
608 | + | |
609 | + ret = elf_write(file.elf); | |
610 | + if (ret < 0) | |
611 | + goto out; | |
612 | + } | |
613 | + | |
614 | out: | |
615 | cleanup(&file); | |
616 | ||
617 | diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c | |
618 | index 1a7e8aa2af58..6e9f980a7d26 100644 | |
619 | --- a/tools/objtool/elf.c | |
620 | +++ b/tools/objtool/elf.c | |
621 | @@ -30,16 +30,6 @@ | |
622 | #include "elf.h" | |
623 | #include "warn.h" | |
624 | ||
625 | -/* | |
626 | - * Fallback for systems without this "read, mmaping if possible" cmd. | |
627 | - */ | |
628 | -#ifndef ELF_C_READ_MMAP | |
629 | -#define ELF_C_READ_MMAP ELF_C_READ | |
630 | -#endif | |
631 | - | |
632 | -#define WARN_ELF(format, ...) \ | |
633 | - WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1)) | |
634 | - | |
635 | struct section *find_section_by_name(struct elf *elf, const char *name) | |
636 | { | |
637 | struct section *sec; | |
638 | @@ -349,9 +339,10 @@ static int read_relas(struct elf *elf) | |
639 | return 0; | |
640 | } | |
641 | ||
642 | -struct elf *elf_open(const char *name) | |
643 | +struct elf *elf_open(const char *name, int flags) | |
644 | { | |
645 | struct elf *elf; | |
646 | + Elf_Cmd cmd; | |
647 | ||
648 | elf_version(EV_CURRENT); | |
649 | ||
650 | @@ -364,13 +355,20 @@ struct elf *elf_open(const char *name) | |
651 | ||
652 | INIT_LIST_HEAD(&elf->sections); | |
653 | ||
654 | - elf->fd = open(name, O_RDONLY); | |
655 | + elf->fd = open(name, flags); | |
656 | if (elf->fd == -1) { | |
657 | perror("open"); | |
658 | goto err; | |
659 | } | |
660 | ||
661 | - elf->elf = elf_begin(elf->fd, ELF_C_READ_MMAP, NULL); | |
662 | + if ((flags & O_ACCMODE) == O_RDONLY) | |
663 | + cmd = ELF_C_READ_MMAP; | |
664 | + else if ((flags & O_ACCMODE) == O_RDWR) | |
665 | + cmd = ELF_C_RDWR; | |
666 | + else /* O_WRONLY */ | |
667 | + cmd = ELF_C_WRITE; | |
668 | + | |
669 | + elf->elf = elf_begin(elf->fd, cmd, NULL); | |
670 | if (!elf->elf) { | |
671 | WARN_ELF("elf_begin"); | |
672 | goto err; | |
673 | @@ -397,6 +395,194 @@ struct elf *elf_open(const char *name) | |
674 | return NULL; | |
675 | } | |
676 | ||
677 | +struct section *elf_create_section(struct elf *elf, const char *name, | |
678 | + size_t entsize, int nr) | |
679 | +{ | |
680 | + struct section *sec, *shstrtab; | |
681 | + size_t size = entsize * nr; | |
682 | + struct Elf_Scn *s; | |
683 | + Elf_Data *data; | |
684 | + | |
685 | + sec = malloc(sizeof(*sec)); | |
686 | + if (!sec) { | |
687 | + perror("malloc"); | |
688 | + return NULL; | |
689 | + } | |
690 | + memset(sec, 0, sizeof(*sec)); | |
691 | + | |
692 | + INIT_LIST_HEAD(&sec->symbol_list); | |
693 | + INIT_LIST_HEAD(&sec->rela_list); | |
694 | + hash_init(sec->rela_hash); | |
695 | + hash_init(sec->symbol_hash); | |
696 | + | |
697 | + list_add_tail(&sec->list, &elf->sections); | |
698 | + | |
699 | + s = elf_newscn(elf->elf); | |
700 | + if (!s) { | |
701 | + WARN_ELF("elf_newscn"); | |
702 | + return NULL; | |
703 | + } | |
704 | + | |
705 | + sec->name = strdup(name); | |
706 | + if (!sec->name) { | |
707 | + perror("strdup"); | |
708 | + return NULL; | |
709 | + } | |
710 | + | |
711 | + sec->idx = elf_ndxscn(s); | |
712 | + sec->len = size; | |
713 | + sec->changed = true; | |
714 | + | |
715 | + sec->data = elf_newdata(s); | |
716 | + if (!sec->data) { | |
717 | + WARN_ELF("elf_newdata"); | |
718 | + return NULL; | |
719 | + } | |
720 | + | |
721 | + sec->data->d_size = size; | |
722 | + sec->data->d_align = 1; | |
723 | + | |
724 | + if (size) { | |
725 | + sec->data->d_buf = malloc(size); | |
726 | + if (!sec->data->d_buf) { | |
727 | + perror("malloc"); | |
728 | + return NULL; | |
729 | + } | |
730 | + memset(sec->data->d_buf, 0, size); | |
731 | + } | |
732 | + | |
733 | + if (!gelf_getshdr(s, &sec->sh)) { | |
734 | + WARN_ELF("gelf_getshdr"); | |
735 | + return NULL; | |
736 | + } | |
737 | + | |
738 | + sec->sh.sh_size = size; | |
739 | + sec->sh.sh_entsize = entsize; | |
740 | + sec->sh.sh_type = SHT_PROGBITS; | |
741 | + sec->sh.sh_addralign = 1; | |
742 | + sec->sh.sh_flags = SHF_ALLOC; | |
743 | + | |
744 | + | |
745 | + /* Add section name to .shstrtab */ | |
746 | + shstrtab = find_section_by_name(elf, ".shstrtab"); | |
747 | + if (!shstrtab) { | |
748 | + WARN("can't find .shstrtab section"); | |
749 | + return NULL; | |
750 | + } | |
751 | + | |
752 | + s = elf_getscn(elf->elf, shstrtab->idx); | |
753 | + if (!s) { | |
754 | + WARN_ELF("elf_getscn"); | |
755 | + return NULL; | |
756 | + } | |
757 | + | |
758 | + data = elf_newdata(s); | |
759 | + if (!data) { | |
760 | + WARN_ELF("elf_newdata"); | |
761 | + return NULL; | |
762 | + } | |
763 | + | |
764 | + data->d_buf = sec->name; | |
765 | + data->d_size = strlen(name) + 1; | |
766 | + data->d_align = 1; | |
767 | + | |
768 | + sec->sh.sh_name = shstrtab->len; | |
769 | + | |
770 | + shstrtab->len += strlen(name) + 1; | |
771 | + shstrtab->changed = true; | |
772 | + | |
773 | + return sec; | |
774 | +} | |
775 | + | |
776 | +struct section *elf_create_rela_section(struct elf *elf, struct section *base) | |
777 | +{ | |
778 | + char *relaname; | |
779 | + struct section *sec; | |
780 | + | |
781 | + relaname = malloc(strlen(base->name) + strlen(".rela") + 1); | |
782 | + if (!relaname) { | |
783 | + perror("malloc"); | |
784 | + return NULL; | |
785 | + } | |
786 | + strcpy(relaname, ".rela"); | |
787 | + strcat(relaname, base->name); | |
788 | + | |
789 | + sec = elf_create_section(elf, relaname, sizeof(GElf_Rela), 0); | |
790 | + if (!sec) | |
791 | + return NULL; | |
792 | + | |
793 | + base->rela = sec; | |
794 | + sec->base = base; | |
795 | + | |
796 | + sec->sh.sh_type = SHT_RELA; | |
797 | + sec->sh.sh_addralign = 8; | |
798 | + sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; | |
799 | + sec->sh.sh_info = base->idx; | |
800 | + sec->sh.sh_flags = SHF_INFO_LINK; | |
801 | + | |
802 | + return sec; | |
803 | +} | |
804 | + | |
805 | +int elf_rebuild_rela_section(struct section *sec) | |
806 | +{ | |
807 | + struct rela *rela; | |
808 | + int nr, idx = 0, size; | |
809 | + GElf_Rela *relas; | |
810 | + | |
811 | + nr = 0; | |
812 | + list_for_each_entry(rela, &sec->rela_list, list) | |
813 | + nr++; | |
814 | + | |
815 | + size = nr * sizeof(*relas); | |
816 | + relas = malloc(size); | |
817 | + if (!relas) { | |
818 | + perror("malloc"); | |
819 | + return -1; | |
820 | + } | |
821 | + | |
822 | + sec->data->d_buf = relas; | |
823 | + sec->data->d_size = size; | |
824 | + | |
825 | + sec->sh.sh_size = size; | |
826 | + | |
827 | + idx = 0; | |
828 | + list_for_each_entry(rela, &sec->rela_list, list) { | |
829 | + relas[idx].r_offset = rela->offset; | |
830 | + relas[idx].r_addend = rela->addend; | |
831 | + relas[idx].r_info = GELF_R_INFO(rela->sym->idx, rela->type); | |
832 | + idx++; | |
833 | + } | |
834 | + | |
835 | + return 0; | |
836 | +} | |
837 | + | |
838 | +int elf_write(struct elf *elf) | |
839 | +{ | |
840 | + struct section *sec; | |
841 | + Elf_Scn *s; | |
842 | + | |
843 | + list_for_each_entry(sec, &elf->sections, list) { | |
844 | + if (sec->changed) { | |
845 | + s = elf_getscn(elf->elf, sec->idx); | |
846 | + if (!s) { | |
847 | + WARN_ELF("elf_getscn"); | |
848 | + return -1; | |
849 | + } | |
850 | + if (!gelf_update_shdr (s, &sec->sh)) { | |
851 | + WARN_ELF("gelf_update_shdr"); | |
852 | + return -1; | |
853 | + } | |
854 | + } | |
855 | + } | |
856 | + | |
857 | + if (elf_update(elf->elf, ELF_C_WRITE) < 0) { | |
858 | + WARN_ELF("elf_update"); | |
859 | + return -1; | |
860 | + } | |
861 | + | |
862 | + return 0; | |
863 | +} | |
864 | + | |
865 | void elf_close(struct elf *elf) | |
866 | { | |
867 | struct section *sec, *tmpsec; | |
868 | diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c | |
869 | index ecc5b1b5d15d..31e0f9143840 100644 | |
870 | --- a/tools/objtool/objtool.c | |
871 | +++ b/tools/objtool/objtool.c | |
872 | @@ -42,10 +42,11 @@ struct cmd_struct { | |
873 | }; | |
874 | ||
875 | static const char objtool_usage_string[] = | |
876 | - "objtool [OPTIONS] COMMAND [ARGS]"; | |
877 | + "objtool COMMAND [ARGS]"; | |
878 | ||
879 | static struct cmd_struct objtool_cmds[] = { | |
880 | {"check", cmd_check, "Perform stack metadata validation on an object file" }, | |
881 | + {"orc", cmd_orc, "Generate in-place ORC unwind tables for an object file" }, | |
882 | }; | |
883 | ||
884 | bool help; | |
885 | diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c | |
886 | new file mode 100644 | |
887 | index 000000000000..36c5bf6a2675 | |
888 | --- /dev/null | |
889 | +++ b/tools/objtool/orc_dump.c | |
890 | @@ -0,0 +1,212 @@ | |
891 | +/* | |
892 | + * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> | |
893 | + * | |
894 | + * This program is free software; you can redistribute it and/or | |
895 | + * modify it under the terms of the GNU General Public License | |
896 | + * as published by the Free Software Foundation; either version 2 | |
897 | + * of the License, or (at your option) any later version. | |
898 | + * | |
899 | + * This program is distributed in the hope that it will be useful, | |
900 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
901 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
902 | + * GNU General Public License for more details. | |
903 | + * | |
904 | + * You should have received a copy of the GNU General Public License | |
905 | + * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
906 | + */ | |
907 | + | |
908 | +#include <unistd.h> | |
909 | +#include "orc.h" | |
910 | +#include "warn.h" | |
911 | + | |
912 | +static const char *reg_name(unsigned int reg) | |
913 | +{ | |
914 | + switch (reg) { | |
915 | + case ORC_REG_PREV_SP: | |
916 | + return "prevsp"; | |
917 | + case ORC_REG_DX: | |
918 | + return "dx"; | |
919 | + case ORC_REG_DI: | |
920 | + return "di"; | |
921 | + case ORC_REG_BP: | |
922 | + return "bp"; | |
923 | + case ORC_REG_SP: | |
924 | + return "sp"; | |
925 | + case ORC_REG_R10: | |
926 | + return "r10"; | |
927 | + case ORC_REG_R13: | |
928 | + return "r13"; | |
929 | + case ORC_REG_BP_INDIRECT: | |
930 | + return "bp(ind)"; | |
931 | + case ORC_REG_SP_INDIRECT: | |
932 | + return "sp(ind)"; | |
933 | + default: | |
934 | + return "?"; | |
935 | + } | |
936 | +} | |
937 | + | |
938 | +static const char *orc_type_name(unsigned int type) | |
939 | +{ | |
940 | + switch (type) { | |
941 | + case ORC_TYPE_CALL: | |
942 | + return "call"; | |
943 | + case ORC_TYPE_REGS: | |
944 | + return "regs"; | |
945 | + case ORC_TYPE_REGS_IRET: | |
946 | + return "iret"; | |
947 | + default: | |
948 | + return "?"; | |
949 | + } | |
950 | +} | |
951 | + | |
952 | +static void print_reg(unsigned int reg, int offset) | |
953 | +{ | |
954 | + if (reg == ORC_REG_BP_INDIRECT) | |
955 | + printf("(bp%+d)", offset); | |
956 | + else if (reg == ORC_REG_SP_INDIRECT) | |
957 | + printf("(sp%+d)", offset); | |
958 | + else if (reg == ORC_REG_UNDEFINED) | |
959 | + printf("(und)"); | |
960 | + else | |
961 | + printf("%s%+d", reg_name(reg), offset); | |
962 | +} | |
963 | + | |
964 | +int orc_dump(const char *_objname) | |
965 | +{ | |
966 | + int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0; | |
967 | + struct orc_entry *orc = NULL; | |
968 | + char *name; | |
969 | + unsigned long nr_sections, orc_ip_addr = 0; | |
970 | + size_t shstrtab_idx; | |
971 | + Elf *elf; | |
972 | + Elf_Scn *scn; | |
973 | + GElf_Shdr sh; | |
974 | + GElf_Rela rela; | |
975 | + GElf_Sym sym; | |
976 | + Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL; | |
977 | + | |
978 | + | |
979 | + objname = _objname; | |
980 | + | |
981 | + elf_version(EV_CURRENT); | |
982 | + | |
983 | + fd = open(objname, O_RDONLY); | |
984 | + if (fd == -1) { | |
985 | + perror("open"); | |
986 | + return -1; | |
987 | + } | |
988 | + | |
989 | + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | |
990 | + if (!elf) { | |
991 | + WARN_ELF("elf_begin"); | |
992 | + return -1; | |
993 | + } | |
994 | + | |
995 | + if (elf_getshdrnum(elf, &nr_sections)) { | |
996 | + WARN_ELF("elf_getshdrnum"); | |
997 | + return -1; | |
998 | + } | |
999 | + | |
1000 | + if (elf_getshdrstrndx(elf, &shstrtab_idx)) { | |
1001 | + WARN_ELF("elf_getshdrstrndx"); | |
1002 | + return -1; | |
1003 | + } | |
1004 | + | |
1005 | + for (i = 0; i < nr_sections; i++) { | |
1006 | + scn = elf_getscn(elf, i); | |
1007 | + if (!scn) { | |
1008 | + WARN_ELF("elf_getscn"); | |
1009 | + return -1; | |
1010 | + } | |
1011 | + | |
1012 | + if (!gelf_getshdr(scn, &sh)) { | |
1013 | + WARN_ELF("gelf_getshdr"); | |
1014 | + return -1; | |
1015 | + } | |
1016 | + | |
1017 | + name = elf_strptr(elf, shstrtab_idx, sh.sh_name); | |
1018 | + if (!name) { | |
1019 | + WARN_ELF("elf_strptr"); | |
1020 | + return -1; | |
1021 | + } | |
1022 | + | |
1023 | + data = elf_getdata(scn, NULL); | |
1024 | + if (!data) { | |
1025 | + WARN_ELF("elf_getdata"); | |
1026 | + return -1; | |
1027 | + } | |
1028 | + | |
1029 | + if (!strcmp(name, ".symtab")) { | |
1030 | + symtab = data; | |
1031 | + } else if (!strcmp(name, ".orc_unwind")) { | |
1032 | + orc = data->d_buf; | |
1033 | + orc_size = sh.sh_size; | |
1034 | + } else if (!strcmp(name, ".orc_unwind_ip")) { | |
1035 | + orc_ip = data->d_buf; | |
1036 | + orc_ip_addr = sh.sh_addr; | |
1037 | + } else if (!strcmp(name, ".rela.orc_unwind_ip")) { | |
1038 | + rela_orc_ip = data; | |
1039 | + } | |
1040 | + } | |
1041 | + | |
1042 | + if (!symtab || !orc || !orc_ip) | |
1043 | + return 0; | |
1044 | + | |
1045 | + if (orc_size % sizeof(*orc) != 0) { | |
1046 | + WARN("bad .orc_unwind section size"); | |
1047 | + return -1; | |
1048 | + } | |
1049 | + | |
1050 | + nr_entries = orc_size / sizeof(*orc); | |
1051 | + for (i = 0; i < nr_entries; i++) { | |
1052 | + if (rela_orc_ip) { | |
1053 | + if (!gelf_getrela(rela_orc_ip, i, &rela)) { | |
1054 | + WARN_ELF("gelf_getrela"); | |
1055 | + return -1; | |
1056 | + } | |
1057 | + | |
1058 | + if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) { | |
1059 | + WARN_ELF("gelf_getsym"); | |
1060 | + return -1; | |
1061 | + } | |
1062 | + | |
1063 | + scn = elf_getscn(elf, sym.st_shndx); | |
1064 | + if (!scn) { | |
1065 | + WARN_ELF("elf_getscn"); | |
1066 | + return -1; | |
1067 | + } | |
1068 | + | |
1069 | + if (!gelf_getshdr(scn, &sh)) { | |
1070 | + WARN_ELF("gelf_getshdr"); | |
1071 | + return -1; | |
1072 | + } | |
1073 | + | |
1074 | + name = elf_strptr(elf, shstrtab_idx, sh.sh_name); | |
1075 | + if (!name || !*name) { | |
1076 | + WARN_ELF("elf_strptr"); | |
1077 | + return -1; | |
1078 | + } | |
1079 | + | |
1080 | + printf("%s+%lx:", name, rela.r_addend); | |
1081 | + | |
1082 | + } else { | |
1083 | + printf("%lx:", orc_ip_addr + (i * sizeof(int)) + orc_ip[i]); | |
1084 | + } | |
1085 | + | |
1086 | + | |
1087 | + printf(" sp:"); | |
1088 | + | |
1089 | + print_reg(orc[i].sp_reg, orc[i].sp_offset); | |
1090 | + | |
1091 | + printf(" bp:"); | |
1092 | + | |
1093 | + print_reg(orc[i].bp_reg, orc[i].bp_offset); | |
1094 | + | |
1095 | + printf(" type:%s\n", orc_type_name(orc[i].type)); | |
1096 | + } | |
1097 | + | |
1098 | + elf_end(elf); | |
1099 | + close(fd); | |
1100 | + | |
1101 | + return 0; | |
1102 | +} | |
1103 | diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c | |
1104 | new file mode 100644 | |
1105 | index 000000000000..e5ca31429c9b | |
1106 | --- /dev/null | |
1107 | +++ b/tools/objtool/orc_gen.c | |
1108 | @@ -0,0 +1,214 @@ | |
1109 | +/* | |
1110 | + * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> | |
1111 | + * | |
1112 | + * This program is free software; you can redistribute it and/or | |
1113 | + * modify it under the terms of the GNU General Public License | |
1114 | + * as published by the Free Software Foundation; either version 2 | |
1115 | + * of the License, or (at your option) any later version. | |
1116 | + * | |
1117 | + * This program is distributed in the hope that it will be useful, | |
1118 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
1119 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
1120 | + * GNU General Public License for more details. | |
1121 | + * | |
1122 | + * You should have received a copy of the GNU General Public License | |
1123 | + * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
1124 | + */ | |
1125 | + | |
1126 | +#include <stdlib.h> | |
1127 | +#include <string.h> | |
1128 | + | |
1129 | +#include "orc.h" | |
1130 | +#include "check.h" | |
1131 | +#include "warn.h" | |
1132 | + | |
1133 | +int create_orc(struct objtool_file *file) | |
1134 | +{ | |
1135 | + struct instruction *insn; | |
1136 | + | |
1137 | + for_each_insn(file, insn) { | |
1138 | + struct orc_entry *orc = &insn->orc; | |
1139 | + struct cfi_reg *cfa = &insn->state.cfa; | |
1140 | + struct cfi_reg *bp = &insn->state.regs[CFI_BP]; | |
1141 | + | |
1142 | + if (cfa->base == CFI_UNDEFINED) { | |
1143 | + orc->sp_reg = ORC_REG_UNDEFINED; | |
1144 | + continue; | |
1145 | + } | |
1146 | + | |
1147 | + switch (cfa->base) { | |
1148 | + case CFI_SP: | |
1149 | + orc->sp_reg = ORC_REG_SP; | |
1150 | + break; | |
1151 | + case CFI_SP_INDIRECT: | |
1152 | + orc->sp_reg = ORC_REG_SP_INDIRECT; | |
1153 | + break; | |
1154 | + case CFI_BP: | |
1155 | + orc->sp_reg = ORC_REG_BP; | |
1156 | + break; | |
1157 | + case CFI_BP_INDIRECT: | |
1158 | + orc->sp_reg = ORC_REG_BP_INDIRECT; | |
1159 | + break; | |
1160 | + case CFI_R10: | |
1161 | + orc->sp_reg = ORC_REG_R10; | |
1162 | + break; | |
1163 | + case CFI_R13: | |
1164 | + orc->sp_reg = ORC_REG_R13; | |
1165 | + break; | |
1166 | + case CFI_DI: | |
1167 | + orc->sp_reg = ORC_REG_DI; | |
1168 | + break; | |
1169 | + case CFI_DX: | |
1170 | + orc->sp_reg = ORC_REG_DX; | |
1171 | + break; | |
1172 | + default: | |
1173 | + WARN_FUNC("unknown CFA base reg %d", | |
1174 | + insn->sec, insn->offset, cfa->base); | |
1175 | + return -1; | |
1176 | + } | |
1177 | + | |
1178 | + switch(bp->base) { | |
1179 | + case CFI_UNDEFINED: | |
1180 | + orc->bp_reg = ORC_REG_UNDEFINED; | |
1181 | + break; | |
1182 | + case CFI_CFA: | |
1183 | + orc->bp_reg = ORC_REG_PREV_SP; | |
1184 | + break; | |
1185 | + case CFI_BP: | |
1186 | + orc->bp_reg = ORC_REG_BP; | |
1187 | + break; | |
1188 | + default: | |
1189 | + WARN_FUNC("unknown BP base reg %d", | |
1190 | + insn->sec, insn->offset, bp->base); | |
1191 | + return -1; | |
1192 | + } | |
1193 | + | |
1194 | + orc->sp_offset = cfa->offset; | |
1195 | + orc->bp_offset = bp->offset; | |
1196 | + orc->type = insn->state.type; | |
1197 | + } | |
1198 | + | |
1199 | + return 0; | |
1200 | +} | |
1201 | + | |
1202 | +static int create_orc_entry(struct section *u_sec, struct section *ip_relasec, | |
1203 | + unsigned int idx, struct section *insn_sec, | |
1204 | + unsigned long insn_off, struct orc_entry *o) | |
1205 | +{ | |
1206 | + struct orc_entry *orc; | |
1207 | + struct rela *rela; | |
1208 | + | |
1209 | + /* populate ORC data */ | |
1210 | + orc = (struct orc_entry *)u_sec->data->d_buf + idx; | |
1211 | + memcpy(orc, o, sizeof(*orc)); | |
1212 | + | |
1213 | + /* populate rela for ip */ | |
1214 | + rela = malloc(sizeof(*rela)); | |
1215 | + if (!rela) { | |
1216 | + perror("malloc"); | |
1217 | + return -1; | |
1218 | + } | |
1219 | + memset(rela, 0, sizeof(*rela)); | |
1220 | + | |
1221 | + rela->sym = insn_sec->sym; | |
1222 | + rela->addend = insn_off; | |
1223 | + rela->type = R_X86_64_PC32; | |
1224 | + rela->offset = idx * sizeof(int); | |
1225 | + | |
1226 | + list_add_tail(&rela->list, &ip_relasec->rela_list); | |
1227 | + hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset); | |
1228 | + | |
1229 | + return 0; | |
1230 | +} | |
1231 | + | |
1232 | +int create_orc_sections(struct objtool_file *file) | |
1233 | +{ | |
1234 | + struct instruction *insn, *prev_insn; | |
1235 | + struct section *sec, *u_sec, *ip_relasec; | |
1236 | + unsigned int idx; | |
1237 | + | |
1238 | + struct orc_entry empty = { | |
1239 | + .sp_reg = ORC_REG_UNDEFINED, | |
1240 | + .bp_reg = ORC_REG_UNDEFINED, | |
1241 | + .type = ORC_TYPE_CALL, | |
1242 | + }; | |
1243 | + | |
1244 | + sec = find_section_by_name(file->elf, ".orc_unwind"); | |
1245 | + if (sec) { | |
1246 | + WARN("file already has .orc_unwind section, skipping"); | |
1247 | + return -1; | |
1248 | + } | |
1249 | + | |
1250 | + /* count the number of needed orcs */ | |
1251 | + idx = 0; | |
1252 | + for_each_sec(file, sec) { | |
1253 | + if (!sec->text) | |
1254 | + continue; | |
1255 | + | |
1256 | + prev_insn = NULL; | |
1257 | + sec_for_each_insn(file, sec, insn) { | |
1258 | + if (!prev_insn || | |
1259 | + memcmp(&insn->orc, &prev_insn->orc, | |
1260 | + sizeof(struct orc_entry))) { | |
1261 | + idx++; | |
1262 | + } | |
1263 | + prev_insn = insn; | |
1264 | + } | |
1265 | + | |
1266 | + /* section terminator */ | |
1267 | + if (prev_insn) | |
1268 | + idx++; | |
1269 | + } | |
1270 | + if (!idx) | |
1271 | + return -1; | |
1272 | + | |
1273 | + | |
1274 | + /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ | |
1275 | + sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx); | |
1276 | + | |
1277 | + ip_relasec = elf_create_rela_section(file->elf, sec); | |
1278 | + if (!ip_relasec) | |
1279 | + return -1; | |
1280 | + | |
1281 | + /* create .orc_unwind section */ | |
1282 | + u_sec = elf_create_section(file->elf, ".orc_unwind", | |
1283 | + sizeof(struct orc_entry), idx); | |
1284 | + | |
1285 | + /* populate sections */ | |
1286 | + idx = 0; | |
1287 | + for_each_sec(file, sec) { | |
1288 | + if (!sec->text) | |
1289 | + continue; | |
1290 | + | |
1291 | + prev_insn = NULL; | |
1292 | + sec_for_each_insn(file, sec, insn) { | |
1293 | + if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, | |
1294 | + sizeof(struct orc_entry))) { | |
1295 | + | |
1296 | + if (create_orc_entry(u_sec, ip_relasec, idx, | |
1297 | + insn->sec, insn->offset, | |
1298 | + &insn->orc)) | |
1299 | + return -1; | |
1300 | + | |
1301 | + idx++; | |
1302 | + } | |
1303 | + prev_insn = insn; | |
1304 | + } | |
1305 | + | |
1306 | + /* section terminator */ | |
1307 | + if (prev_insn) { | |
1308 | + if (create_orc_entry(u_sec, ip_relasec, idx, | |
1309 | + prev_insn->sec, | |
1310 | + prev_insn->offset + prev_insn->len, | |
1311 | + &empty)) | |
1312 | + return -1; | |
1313 | + | |
1314 | + idx++; | |
1315 | + } | |
1316 | + } | |
1317 | + | |
1318 | + if (elf_rebuild_rela_section(ip_relasec)) | |
1319 | + return -1; | |
1320 | + | |
1321 | + return 0; | |
1322 | +} | |
1323 | diff --git a/tools/objtool/Build b/tools/objtool/Build | |
1324 | index 6f2e1987c4d9..749becdf5b90 100644 | |
1325 | --- a/tools/objtool/Build | |
1326 | +++ b/tools/objtool/Build | |
1327 | @@ -1,6 +1,9 @@ | |
1328 | objtool-y += arch/$(SRCARCH)/ | |
1329 | objtool-y += builtin-check.o | |
1330 | +objtool-y += builtin-orc.o | |
1331 | objtool-y += check.o | |
1332 | +objtool-y += orc_gen.o | |
1333 | +objtool-y += orc_dump.o | |
1334 | objtool-y += elf.o | |
1335 | objtool-y += special.o | |
1336 | objtool-y += objtool.o | |
1337 | -- | |
1338 | 2.14.2 | |
1339 |