| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339 |
- From a5a66a79aadeb0459a3a28adf04d41c42e053436 Mon Sep 17 00:00:00 2001
- From: Josh Poimboeuf <[email protected]>
- Date: Tue, 11 Jul 2017 10:33:42 -0500
- Subject: [PATCH 033/242] objtool: Add ORC unwind table generation
- MIME-Version: 1.0
- Content-Type: text/plain; charset=UTF-8
- Content-Transfer-Encoding: 8bit
- CVE-2017-5754
- Now that objtool knows the states of all registers on the stack for each
- instruction, it's straightforward to generate debuginfo for an unwinder
- to use.
- Instead of generating DWARF, generate a new format called ORC, which is
- more suitable for an in-kernel unwinder. See
- Documentation/x86/orc-unwinder.txt for a more detailed description of
- this new debuginfo format and why it's preferable to DWARF.
- Signed-off-by: Josh Poimboeuf <[email protected]>
- Cc: Andy Lutomirski <[email protected]>
- Cc: Borislav Petkov <[email protected]>
- Cc: Brian Gerst <[email protected]>
- Cc: Denys Vlasenko <[email protected]>
- Cc: H. Peter Anvin <[email protected]>
- Cc: Jiri Slaby <[email protected]>
- Cc: Linus Torvalds <[email protected]>
- Cc: Mike Galbraith <[email protected]>
- Cc: Peter Zijlstra <[email protected]>
- Cc: Thomas Gleixner <[email protected]>
- Cc: [email protected]
- Link: http://lkml.kernel.org/r/c9b9f01ba6c5ed2bdc9bb0957b78167fdbf9632e.1499786555.git.jpoimboe@redhat.com
- Signed-off-by: Ingo Molnar <[email protected]>
- (cherry picked from commit 627fce14809ba5610b0cb476cd0186d3fcedecfc)
- Signed-off-by: Andy Whitcroft <[email protected]>
- Signed-off-by: Kleber Sacilotto de Souza <[email protected]>
- (cherry picked from commit 9460f7766786ad0f8330f78f22b81842632a5398)
- Signed-off-by: Fabian Grünbichler <[email protected]>
- ---
- tools/objtool/Documentation/stack-validation.txt | 56 ++----
- tools/objtool/builtin.h | 1 +
- tools/objtool/check.h | 15 +-
- tools/objtool/elf.h | 15 +-
- tools/objtool/orc.h | 30 ++++
- tools/objtool/orc_types.h | 85 +++++++++
- tools/objtool/builtin-check.c | 2 +-
- tools/objtool/builtin-orc.c | 70 ++++++++
- tools/objtool/check.c | 58 +++++-
- tools/objtool/elf.c | 212 ++++++++++++++++++++--
- tools/objtool/objtool.c | 3 +-
- tools/objtool/orc_dump.c | 212 ++++++++++++++++++++++
- tools/objtool/orc_gen.c | 214 +++++++++++++++++++++++
- tools/objtool/Build | 3 +
- 14 files changed, 916 insertions(+), 60 deletions(-)
- create mode 100644 tools/objtool/orc.h
- create mode 100644 tools/objtool/orc_types.h
- create mode 100644 tools/objtool/builtin-orc.c
- create mode 100644 tools/objtool/orc_dump.c
- create mode 100644 tools/objtool/orc_gen.c
- diff --git a/tools/objtool/Documentation/stack-validation.txt b/tools/objtool/Documentation/stack-validation.txt
- index 17c1195f11f4..6a1af43862df 100644
- --- a/tools/objtool/Documentation/stack-validation.txt
- +++ b/tools/objtool/Documentation/stack-validation.txt
- @@ -11,9 +11,6 @@ analyzes every .o file and ensures the validity of its stack metadata.
- It enforces a set of rules on asm code and C inline assembly code so
- that stack traces can be reliable.
-
- -Currently it only checks frame pointer usage, but there are plans to add
- -CFI validation for C files and CFI generation for asm files.
- -
- For each function, it recursively follows all possible code paths and
- validates the correct frame pointer state at each instruction.
-
- @@ -23,6 +20,10 @@ alternative execution paths to a given instruction (or set of
- instructions). Similarly, it knows how to follow switch statements, for
- which gcc sometimes uses jump tables.
-
- +(Objtool also has an 'orc generate' subcommand which generates debuginfo
- +for the ORC unwinder. See Documentation/x86/orc-unwinder.txt in the
- +kernel tree for more details.)
- +
-
- Why do we need stack metadata validation?
- -----------------------------------------
- @@ -93,37 +94,14 @@ a) More reliable stack traces for frame pointer enabled kernels
- or at the very end of the function after the stack frame has been
- destroyed. This is an inherent limitation of frame pointers.
-
- -b) 100% reliable stack traces for DWARF enabled kernels
- -
- - (NOTE: This is not yet implemented)
- -
- - As an alternative to frame pointers, DWARF Call Frame Information
- - (CFI) metadata can be used to walk the stack. Unlike frame pointers,
- - CFI metadata is out of band. So it doesn't affect runtime
- - performance and it can be reliable even when interrupts or exceptions
- - are involved.
- -
- - For C code, gcc automatically generates DWARF CFI metadata. But for
- - asm code, generating CFI is a tedious manual approach which requires
- - manually placed .cfi assembler macros to be scattered throughout the
- - code. It's clumsy and very easy to get wrong, and it makes the real
- - code harder to read.
- -
- - Stacktool will improve this situation in several ways. For code
- - which already has CFI annotations, it will validate them. For code
- - which doesn't have CFI annotations, it will generate them. So an
- - architecture can opt to strip out all the manual .cfi annotations
- - from their asm code and have objtool generate them instead.
- +b) ORC (Oops Rewind Capability) unwind table generation
-
- - We might also add a runtime stack validation debug option where we
- - periodically walk the stack from schedule() and/or an NMI to ensure
- - that the stack metadata is sane and that we reach the bottom of the
- - stack.
- + An alternative to frame pointers and DWARF, ORC unwind data can be
- + used to walk the stack. Unlike frame pointers, ORC data is out of
- + band. So it doesn't affect runtime performance and it can be
- + reliable even when interrupts or exceptions are involved.
-
- - So the benefit of objtool here will be that external tooling should
- - always show perfect stack traces. And the same will be true for
- - kernel warning/oops traces if the architecture has a runtime DWARF
- - unwinder.
- + For more details, see Documentation/x86/orc-unwinder.txt.
-
- c) Higher live patching compatibility rate
-
- @@ -211,7 +189,7 @@ they mean, and suggestions for how to fix them.
- function, add proper frame pointer logic using the FRAME_BEGIN and
- FRAME_END macros. Otherwise, if it's not a callable function, remove
- its ELF function annotation by changing ENDPROC to END, and instead
- - use the manual CFI hint macros in asm/undwarf.h.
- + use the manual unwind hint macros in asm/unwind_hints.h.
-
- If it's a GCC-compiled .c file, the error may be because the function
- uses an inline asm() statement which has a "call" instruction. An
- @@ -231,8 +209,8 @@ they mean, and suggestions for how to fix them.
- If the error is for an asm file, and the instruction is inside (or
- reachable from) a callable function, the function should be annotated
- with the ENTRY/ENDPROC macros (ENDPROC is the important one).
- - Otherwise, the code should probably be annotated with the CFI hint
- - macros in asm/undwarf.h so objtool and the unwinder can know the
- + Otherwise, the code should probably be annotated with the unwind hint
- + macros in asm/unwind_hints.h so objtool and the unwinder can know the
- stack state associated with the code.
-
- If you're 100% sure the code won't affect stack traces, or if you're
- @@ -258,7 +236,7 @@ they mean, and suggestions for how to fix them.
- instructions aren't allowed in a callable function, and are most
- likely part of the kernel entry code. They should usually not have
- the callable function annotation (ENDPROC) and should always be
- - annotated with the CFI hint macros in asm/undwarf.h.
- + annotated with the unwind hint macros in asm/unwind_hints.h.
-
-
- 6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame
- @@ -272,7 +250,7 @@ they mean, and suggestions for how to fix them.
-
- If the instruction is not actually in a callable function (e.g.
- kernel entry code), change ENDPROC to END and annotate manually with
- - the CFI hint macros in asm/undwarf.h.
- + the unwind hint macros in asm/unwind_hints.h.
-
-
- 7. file: warning: objtool: func()+0x5c: stack state mismatch
- @@ -288,8 +266,8 @@ they mean, and suggestions for how to fix them.
-
- Another possibility is that the code has some asm or inline asm which
- does some unusual things to the stack or the frame pointer. In such
- - cases it's probably appropriate to use the CFI hint macros in
- - asm/undwarf.h.
- + cases it's probably appropriate to use the unwind hint macros in
- + asm/unwind_hints.h.
-
-
- 8. file.o: warning: objtool: funcA() falls through to next function funcB()
- diff --git a/tools/objtool/builtin.h b/tools/objtool/builtin.h
- index 34d2ba78a616..dd526067fed5 100644
- --- a/tools/objtool/builtin.h
- +++ b/tools/objtool/builtin.h
- @@ -18,5 +18,6 @@
- #define _BUILTIN_H
-
- extern int cmd_check(int argc, const char **argv);
- +extern int cmd_orc(int argc, const char **argv);
-
- #endif /* _BUILTIN_H */
- diff --git a/tools/objtool/check.h b/tools/objtool/check.h
- index da85f5b00ec6..046874bbe226 100644
- --- a/tools/objtool/check.h
- +++ b/tools/objtool/check.h
- @@ -22,12 +22,14 @@
- #include "elf.h"
- #include "cfi.h"
- #include "arch.h"
- +#include "orc.h"
- #include <linux/hashtable.h>
-
- struct insn_state {
- struct cfi_reg cfa;
- struct cfi_reg regs[CFI_NUM_REGS];
- int stack_size;
- + unsigned char type;
- bool bp_scratch;
- bool drap;
- int drap_reg;
- @@ -48,6 +50,7 @@ struct instruction {
- struct symbol *func;
- struct stack_op stack_op;
- struct insn_state state;
- + struct orc_entry orc;
- };
-
- struct objtool_file {
- @@ -58,9 +61,19 @@ struct objtool_file {
- bool ignore_unreachables, c_file;
- };
-
- -int check(const char *objname, bool nofp);
- +int check(const char *objname, bool nofp, bool orc);
- +
- +struct instruction *find_insn(struct objtool_file *file,
- + struct section *sec, unsigned long offset);
-
- #define for_each_insn(file, insn) \
- list_for_each_entry(insn, &file->insn_list, list)
-
- +#define sec_for_each_insn(file, sec, insn) \
- + for (insn = find_insn(file, sec, 0); \
- + insn && &insn->list != &file->insn_list && \
- + insn->sec == sec; \
- + insn = list_next_entry(insn, list))
- +
- +
- #endif /* _CHECK_H */
- diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h
- index 343968b778cb..d86e2ff14466 100644
- --- a/tools/objtool/elf.h
- +++ b/tools/objtool/elf.h
- @@ -28,6 +28,13 @@
- # define elf_getshdrstrndx elf_getshstrndx
- #endif
-
- +/*
- + * Fallback for systems without this "read, mmaping if possible" cmd.
- + */
- +#ifndef ELF_C_READ_MMAP
- +#define ELF_C_READ_MMAP ELF_C_READ
- +#endif
- +
- struct section {
- struct list_head list;
- GElf_Shdr sh;
- @@ -41,6 +48,7 @@ struct section {
- char *name;
- int idx;
- unsigned int len;
- + bool changed, text;
- };
-
- struct symbol {
- @@ -75,7 +83,7 @@ struct elf {
- };
-
-
- -struct elf *elf_open(const char *name);
- +struct elf *elf_open(const char *name, int flags);
- struct section *find_section_by_name(struct elf *elf, const char *name);
- struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
- struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
- @@ -83,6 +91,11 @@ struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
- struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
- unsigned int len);
- struct symbol *find_containing_func(struct section *sec, unsigned long offset);
- +struct section *elf_create_section(struct elf *elf, const char *name, size_t
- + entsize, int nr);
- +struct section *elf_create_rela_section(struct elf *elf, struct section *base);
- +int elf_rebuild_rela_section(struct section *sec);
- +int elf_write(struct elf *elf);
- void elf_close(struct elf *elf);
-
- #define for_each_sec(file, sec) \
- diff --git a/tools/objtool/orc.h b/tools/objtool/orc.h
- new file mode 100644
- index 000000000000..a4139e386ef3
- --- /dev/null
- +++ b/tools/objtool/orc.h
- @@ -0,0 +1,30 @@
- +/*
- + * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
- + *
- + * This program is free software; you can redistribute it and/or
- + * modify it under the terms of the GNU General Public License
- + * as published by the Free Software Foundation; either version 2
- + * of the License, or (at your option) any later version.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with this program; if not, see <http://www.gnu.org/licenses/>.
- + */
- +
- +#ifndef _ORC_H
- +#define _ORC_H
- +
- +#include "orc_types.h"
- +
- +struct objtool_file;
- +
- +int create_orc(struct objtool_file *file);
- +int create_orc_sections(struct objtool_file *file);
- +
- +int orc_dump(const char *objname);
- +
- +#endif /* _ORC_H */
- diff --git a/tools/objtool/orc_types.h b/tools/objtool/orc_types.h
- new file mode 100644
- index 000000000000..fc5cf6cffd9a
- --- /dev/null
- +++ b/tools/objtool/orc_types.h
- @@ -0,0 +1,85 @@
- +/*
- + * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
- + *
- + * This program is free software; you can redistribute it and/or
- + * modify it under the terms of the GNU General Public License
- + * as published by the Free Software Foundation; either version 2
- + * of the License, or (at your option) any later version.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with this program; if not, see <http://www.gnu.org/licenses/>.
- + */
- +
- +#ifndef _ORC_TYPES_H
- +#define _ORC_TYPES_H
- +
- +#include <linux/types.h>
- +#include <linux/compiler.h>
- +
- +/*
- + * The ORC_REG_* registers are base registers which are used to find other
- + * registers on the stack.
- + *
- + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
- + * address of the previous frame: the caller's SP before it called the current
- + * function.
- + *
- + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in
- + * the current frame.
- + *
- + * The most commonly used base registers are SP and BP -- which the previous SP
- + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is
- + * usually based on.
- + *
- + * The rest of the base registers are needed for special cases like entry code
- + * and GCC realigned stacks.
- + */
- +#define ORC_REG_UNDEFINED 0
- +#define ORC_REG_PREV_SP 1
- +#define ORC_REG_DX 2
- +#define ORC_REG_DI 3
- +#define ORC_REG_BP 4
- +#define ORC_REG_SP 5
- +#define ORC_REG_R10 6
- +#define ORC_REG_R13 7
- +#define ORC_REG_BP_INDIRECT 8
- +#define ORC_REG_SP_INDIRECT 9
- +#define ORC_REG_MAX 15
- +
- +/*
- + * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
- + * caller's SP right before it made the call). Used for all callable
- + * functions, i.e. all C code and all callable asm functions.
- + *
- + * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
- + * to a fully populated pt_regs from a syscall, interrupt, or exception.
- + *
- + * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
- + * points to the iret return frame.
- + */
- +#define ORC_TYPE_CALL 0
- +#define ORC_TYPE_REGS 1
- +#define ORC_TYPE_REGS_IRET 2
- +
- +/*
- + * This struct is more or less a vastly simplified version of the DWARF Call
- + * Frame Information standard. It contains only the necessary parts of DWARF
- + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the
- + * unwinder how to find the previous SP and BP (and sometimes entry regs) on
- + * the stack for a given code address. Each instance of the struct corresponds
- + * to one or more code locations.
- + */
- +struct orc_entry {
- + s16 sp_offset;
- + s16 bp_offset;
- + unsigned sp_reg:4;
- + unsigned bp_reg:4;
- + unsigned type:2;
- +} __packed;
- +
- +#endif /* _ORC_TYPES_H */
- diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
- index 365c34ecab26..eedf089b1495 100644
- --- a/tools/objtool/builtin-check.c
- +++ b/tools/objtool/builtin-check.c
- @@ -52,5 +52,5 @@ int cmd_check(int argc, const char **argv)
-
- objname = argv[0];
-
- - return check(objname, nofp);
- + return check(objname, nofp, false);
- }
- diff --git a/tools/objtool/builtin-orc.c b/tools/objtool/builtin-orc.c
- new file mode 100644
- index 000000000000..5ca41ab0df48
- --- /dev/null
- +++ b/tools/objtool/builtin-orc.c
- @@ -0,0 +1,70 @@
- +/*
- + * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
- + *
- + * This program is free software; you can redistribute it and/or
- + * modify it under the terms of the GNU General Public License
- + * as published by the Free Software Foundation; either version 2
- + * of the License, or (at your option) any later version.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with this program; if not, see <http://www.gnu.org/licenses/>.
- + */
- +
- +/*
- + * objtool orc:
- + *
- + * This command analyzes a .o file and adds .orc_unwind and .orc_unwind_ip
- + * sections to it, which is used by the in-kernel ORC unwinder.
- + *
- + * This command is a superset of "objtool check".
- + */
- +
- +#include <string.h>
- +#include <subcmd/parse-options.h>
- +#include "builtin.h"
- +#include "check.h"
- +
- +
- +static const char *orc_usage[] = {
- + "objtool orc generate [<options>] file.o",
- + "objtool orc dump file.o",
- + NULL,
- +};
- +
- +extern const struct option check_options[];
- +extern bool nofp;
- +
- +int cmd_orc(int argc, const char **argv)
- +{
- + const char *objname;
- +
- + argc--; argv++;
- + if (!strncmp(argv[0], "gen", 3)) {
- + argc = parse_options(argc, argv, check_options, orc_usage, 0);
- + if (argc != 1)
- + usage_with_options(orc_usage, check_options);
- +
- + objname = argv[0];
- +
- + return check(objname, nofp, true);
- +
- + }
- +
- + if (!strcmp(argv[0], "dump")) {
- + if (argc != 2)
- + usage_with_options(orc_usage, check_options);
- +
- + objname = argv[1];
- +
- + return orc_dump(objname);
- + }
- +
- + usage_with_options(orc_usage, check_options);
- +
- + return 0;
- +}
- diff --git a/tools/objtool/check.c b/tools/objtool/check.c
- index 2c6d74880403..cb57c526ba17 100644
- --- a/tools/objtool/check.c
- +++ b/tools/objtool/check.c
- @@ -36,8 +36,8 @@ const char *objname;
- static bool nofp;
- struct cfi_state initial_func_cfi;
-
- -static struct instruction *find_insn(struct objtool_file *file,
- - struct section *sec, unsigned long offset)
- +struct instruction *find_insn(struct objtool_file *file,
- + struct section *sec, unsigned long offset)
- {
- struct instruction *insn;
-
- @@ -259,6 +259,11 @@ static int decode_instructions(struct objtool_file *file)
- if (!(sec->sh.sh_flags & SHF_EXECINSTR))
- continue;
-
- + if (strcmp(sec->name, ".altinstr_replacement") &&
- + strcmp(sec->name, ".altinstr_aux") &&
- + strncmp(sec->name, ".discard.", 9))
- + sec->text = true;
- +
- for (offset = 0; offset < sec->len; offset += insn->len) {
- insn = malloc(sizeof(*insn));
- if (!insn) {
- @@ -947,6 +952,30 @@ static bool has_valid_stack_frame(struct insn_state *state)
- return false;
- }
-
- +static int update_insn_state_regs(struct instruction *insn, struct insn_state *state)
- +{
- + struct cfi_reg *cfa = &state->cfa;
- + struct stack_op *op = &insn->stack_op;
- +
- + if (cfa->base != CFI_SP)
- + return 0;
- +
- + /* push */
- + if (op->dest.type == OP_DEST_PUSH)
- + cfa->offset += 8;
- +
- + /* pop */
- + if (op->src.type == OP_SRC_POP)
- + cfa->offset -= 8;
- +
- + /* add immediate to sp */
- + if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
- + op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
- + cfa->offset -= op->src.offset;
- +
- + return 0;
- +}
- +
- static void save_reg(struct insn_state *state, unsigned char reg, int base,
- int offset)
- {
- @@ -1032,6 +1061,9 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state)
- return 0;
- }
-
- + if (state->type == ORC_TYPE_REGS || state->type == ORC_TYPE_REGS_IRET)
- + return update_insn_state_regs(insn, state);
- +
- switch (op->dest.type) {
-
- case OP_DEST_REG:
- @@ -1323,6 +1355,10 @@ static bool insn_state_match(struct instruction *insn, struct insn_state *state)
- break;
- }
-
- + } else if (state1->type != state2->type) {
- + WARN_FUNC("stack state mismatch: type1=%d type2=%d",
- + insn->sec, insn->offset, state1->type, state2->type);
- +
- } else if (state1->drap != state2->drap ||
- (state1->drap && state1->drap_reg != state2->drap_reg)) {
- WARN_FUNC("stack state mismatch: drap1=%d(%d) drap2=%d(%d)",
- @@ -1613,7 +1649,7 @@ static void cleanup(struct objtool_file *file)
- elf_close(file->elf);
- }
-
- -int check(const char *_objname, bool _nofp)
- +int check(const char *_objname, bool _nofp, bool orc)
- {
- struct objtool_file file;
- int ret, warnings = 0;
- @@ -1621,7 +1657,7 @@ int check(const char *_objname, bool _nofp)
- objname = _objname;
- nofp = _nofp;
-
- - file.elf = elf_open(objname);
- + file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY);
- if (!file.elf)
- return 1;
-
- @@ -1654,6 +1690,20 @@ int check(const char *_objname, bool _nofp)
- warnings += ret;
- }
-
- + if (orc) {
- + ret = create_orc(&file);
- + if (ret < 0)
- + goto out;
- +
- + ret = create_orc_sections(&file);
- + if (ret < 0)
- + goto out;
- +
- + ret = elf_write(file.elf);
- + if (ret < 0)
- + goto out;
- + }
- +
- out:
- cleanup(&file);
-
- diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
- index 1a7e8aa2af58..6e9f980a7d26 100644
- --- a/tools/objtool/elf.c
- +++ b/tools/objtool/elf.c
- @@ -30,16 +30,6 @@
- #include "elf.h"
- #include "warn.h"
-
- -/*
- - * Fallback for systems without this "read, mmaping if possible" cmd.
- - */
- -#ifndef ELF_C_READ_MMAP
- -#define ELF_C_READ_MMAP ELF_C_READ
- -#endif
- -
- -#define WARN_ELF(format, ...) \
- - WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1))
- -
- struct section *find_section_by_name(struct elf *elf, const char *name)
- {
- struct section *sec;
- @@ -349,9 +339,10 @@ static int read_relas(struct elf *elf)
- return 0;
- }
-
- -struct elf *elf_open(const char *name)
- +struct elf *elf_open(const char *name, int flags)
- {
- struct elf *elf;
- + Elf_Cmd cmd;
-
- elf_version(EV_CURRENT);
-
- @@ -364,13 +355,20 @@ struct elf *elf_open(const char *name)
-
- INIT_LIST_HEAD(&elf->sections);
-
- - elf->fd = open(name, O_RDONLY);
- + elf->fd = open(name, flags);
- if (elf->fd == -1) {
- perror("open");
- goto err;
- }
-
- - elf->elf = elf_begin(elf->fd, ELF_C_READ_MMAP, NULL);
- + if ((flags & O_ACCMODE) == O_RDONLY)
- + cmd = ELF_C_READ_MMAP;
- + else if ((flags & O_ACCMODE) == O_RDWR)
- + cmd = ELF_C_RDWR;
- + else /* O_WRONLY */
- + cmd = ELF_C_WRITE;
- +
- + elf->elf = elf_begin(elf->fd, cmd, NULL);
- if (!elf->elf) {
- WARN_ELF("elf_begin");
- goto err;
- @@ -397,6 +395,194 @@ struct elf *elf_open(const char *name)
- return NULL;
- }
-
- +struct section *elf_create_section(struct elf *elf, const char *name,
- + size_t entsize, int nr)
- +{
- + struct section *sec, *shstrtab;
- + size_t size = entsize * nr;
- + struct Elf_Scn *s;
- + Elf_Data *data;
- +
- + sec = malloc(sizeof(*sec));
- + if (!sec) {
- + perror("malloc");
- + return NULL;
- + }
- + memset(sec, 0, sizeof(*sec));
- +
- + INIT_LIST_HEAD(&sec->symbol_list);
- + INIT_LIST_HEAD(&sec->rela_list);
- + hash_init(sec->rela_hash);
- + hash_init(sec->symbol_hash);
- +
- + list_add_tail(&sec->list, &elf->sections);
- +
- + s = elf_newscn(elf->elf);
- + if (!s) {
- + WARN_ELF("elf_newscn");
- + return NULL;
- + }
- +
- + sec->name = strdup(name);
- + if (!sec->name) {
- + perror("strdup");
- + return NULL;
- + }
- +
- + sec->idx = elf_ndxscn(s);
- + sec->len = size;
- + sec->changed = true;
- +
- + sec->data = elf_newdata(s);
- + if (!sec->data) {
- + WARN_ELF("elf_newdata");
- + return NULL;
- + }
- +
- + sec->data->d_size = size;
- + sec->data->d_align = 1;
- +
- + if (size) {
- + sec->data->d_buf = malloc(size);
- + if (!sec->data->d_buf) {
- + perror("malloc");
- + return NULL;
- + }
- + memset(sec->data->d_buf, 0, size);
- + }
- +
- + if (!gelf_getshdr(s, &sec->sh)) {
- + WARN_ELF("gelf_getshdr");
- + return NULL;
- + }
- +
- + sec->sh.sh_size = size;
- + sec->sh.sh_entsize = entsize;
- + sec->sh.sh_type = SHT_PROGBITS;
- + sec->sh.sh_addralign = 1;
- + sec->sh.sh_flags = SHF_ALLOC;
- +
- +
- + /* Add section name to .shstrtab */
- + shstrtab = find_section_by_name(elf, ".shstrtab");
- + if (!shstrtab) {
- + WARN("can't find .shstrtab section");
- + return NULL;
- + }
- +
- + s = elf_getscn(elf->elf, shstrtab->idx);
- + if (!s) {
- + WARN_ELF("elf_getscn");
- + return NULL;
- + }
- +
- + data = elf_newdata(s);
- + if (!data) {
- + WARN_ELF("elf_newdata");
- + return NULL;
- + }
- +
- + data->d_buf = sec->name;
- + data->d_size = strlen(name) + 1;
- + data->d_align = 1;
- +
- + sec->sh.sh_name = shstrtab->len;
- +
- + shstrtab->len += strlen(name) + 1;
- + shstrtab->changed = true;
- +
- + return sec;
- +}
- +
- +struct section *elf_create_rela_section(struct elf *elf, struct section *base)
- +{
- + char *relaname;
- + struct section *sec;
- +
- + relaname = malloc(strlen(base->name) + strlen(".rela") + 1);
- + if (!relaname) {
- + perror("malloc");
- + return NULL;
- + }
- + strcpy(relaname, ".rela");
- + strcat(relaname, base->name);
- +
- + sec = elf_create_section(elf, relaname, sizeof(GElf_Rela), 0);
- + if (!sec)
- + return NULL;
- +
- + base->rela = sec;
- + sec->base = base;
- +
- + sec->sh.sh_type = SHT_RELA;
- + sec->sh.sh_addralign = 8;
- + sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
- + sec->sh.sh_info = base->idx;
- + sec->sh.sh_flags = SHF_INFO_LINK;
- +
- + return sec;
- +}
- +
- +int elf_rebuild_rela_section(struct section *sec)
- +{
- + struct rela *rela;
- + int nr, idx = 0, size;
- + GElf_Rela *relas;
- +
- + nr = 0;
- + list_for_each_entry(rela, &sec->rela_list, list)
- + nr++;
- +
- + size = nr * sizeof(*relas);
- + relas = malloc(size);
- + if (!relas) {
- + perror("malloc");
- + return -1;
- + }
- +
- + sec->data->d_buf = relas;
- + sec->data->d_size = size;
- +
- + sec->sh.sh_size = size;
- +
- + idx = 0;
- + list_for_each_entry(rela, &sec->rela_list, list) {
- + relas[idx].r_offset = rela->offset;
- + relas[idx].r_addend = rela->addend;
- + relas[idx].r_info = GELF_R_INFO(rela->sym->idx, rela->type);
- + idx++;
- + }
- +
- + return 0;
- +}
- +
- +int elf_write(struct elf *elf)
- +{
- + struct section *sec;
- + Elf_Scn *s;
- +
- + list_for_each_entry(sec, &elf->sections, list) {
- + if (sec->changed) {
- + s = elf_getscn(elf->elf, sec->idx);
- + if (!s) {
- + WARN_ELF("elf_getscn");
- + return -1;
- + }
- + if (!gelf_update_shdr (s, &sec->sh)) {
- + WARN_ELF("gelf_update_shdr");
- + return -1;
- + }
- + }
- + }
- +
- + if (elf_update(elf->elf, ELF_C_WRITE) < 0) {
- + WARN_ELF("elf_update");
- + return -1;
- + }
- +
- + return 0;
- +}
- +
- void elf_close(struct elf *elf)
- {
- struct section *sec, *tmpsec;
- diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c
- index ecc5b1b5d15d..31e0f9143840 100644
- --- a/tools/objtool/objtool.c
- +++ b/tools/objtool/objtool.c
- @@ -42,10 +42,11 @@ struct cmd_struct {
- };
-
- static const char objtool_usage_string[] =
- - "objtool [OPTIONS] COMMAND [ARGS]";
- + "objtool COMMAND [ARGS]";
-
- static struct cmd_struct objtool_cmds[] = {
- {"check", cmd_check, "Perform stack metadata validation on an object file" },
- + {"orc", cmd_orc, "Generate in-place ORC unwind tables for an object file" },
- };
-
- bool help;
- diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c
- new file mode 100644
- index 000000000000..36c5bf6a2675
- --- /dev/null
- +++ b/tools/objtool/orc_dump.c
- @@ -0,0 +1,212 @@
- +/*
- + * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
- + *
- + * This program is free software; you can redistribute it and/or
- + * modify it under the terms of the GNU General Public License
- + * as published by the Free Software Foundation; either version 2
- + * of the License, or (at your option) any later version.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with this program; if not, see <http://www.gnu.org/licenses/>.
- + */
- +
- +#include <unistd.h>
- +#include "orc.h"
- +#include "warn.h"
- +
- +static const char *reg_name(unsigned int reg)
- +{
- + switch (reg) {
- + case ORC_REG_PREV_SP:
- + return "prevsp";
- + case ORC_REG_DX:
- + return "dx";
- + case ORC_REG_DI:
- + return "di";
- + case ORC_REG_BP:
- + return "bp";
- + case ORC_REG_SP:
- + return "sp";
- + case ORC_REG_R10:
- + return "r10";
- + case ORC_REG_R13:
- + return "r13";
- + case ORC_REG_BP_INDIRECT:
- + return "bp(ind)";
- + case ORC_REG_SP_INDIRECT:
- + return "sp(ind)";
- + default:
- + return "?";
- + }
- +}
- +
- +static const char *orc_type_name(unsigned int type)
- +{
- + switch (type) {
- + case ORC_TYPE_CALL:
- + return "call";
- + case ORC_TYPE_REGS:
- + return "regs";
- + case ORC_TYPE_REGS_IRET:
- + return "iret";
- + default:
- + return "?";
- + }
- +}
- +
- +static void print_reg(unsigned int reg, int offset)
- +{
- + if (reg == ORC_REG_BP_INDIRECT)
- + printf("(bp%+d)", offset);
- + else if (reg == ORC_REG_SP_INDIRECT)
- + printf("(sp%+d)", offset);
- + else if (reg == ORC_REG_UNDEFINED)
- + printf("(und)");
- + else
- + printf("%s%+d", reg_name(reg), offset);
- +}
- +
- +int orc_dump(const char *_objname)
- +{
- + int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0;
- + struct orc_entry *orc = NULL;
- + char *name;
- + unsigned long nr_sections, orc_ip_addr = 0;
- + size_t shstrtab_idx;
- + Elf *elf;
- + Elf_Scn *scn;
- + GElf_Shdr sh;
- + GElf_Rela rela;
- + GElf_Sym sym;
- + Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL;
- +
- +
- + objname = _objname;
- +
- + elf_version(EV_CURRENT);
- +
- + fd = open(objname, O_RDONLY);
- + if (fd == -1) {
- + perror("open");
- + return -1;
- + }
- +
- + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
- + if (!elf) {
- + WARN_ELF("elf_begin");
- + return -1;
- + }
- +
- + if (elf_getshdrnum(elf, &nr_sections)) {
- + WARN_ELF("elf_getshdrnum");
- + return -1;
- + }
- +
- + if (elf_getshdrstrndx(elf, &shstrtab_idx)) {
- + WARN_ELF("elf_getshdrstrndx");
- + return -1;
- + }
- +
- + for (i = 0; i < nr_sections; i++) {
- + scn = elf_getscn(elf, i);
- + if (!scn) {
- + WARN_ELF("elf_getscn");
- + return -1;
- + }
- +
- + if (!gelf_getshdr(scn, &sh)) {
- + WARN_ELF("gelf_getshdr");
- + return -1;
- + }
- +
- + name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
- + if (!name) {
- + WARN_ELF("elf_strptr");
- + return -1;
- + }
- +
- + data = elf_getdata(scn, NULL);
- + if (!data) {
- + WARN_ELF("elf_getdata");
- + return -1;
- + }
- +
- + if (!strcmp(name, ".symtab")) {
- + symtab = data;
- + } else if (!strcmp(name, ".orc_unwind")) {
- + orc = data->d_buf;
- + orc_size = sh.sh_size;
- + } else if (!strcmp(name, ".orc_unwind_ip")) {
- + orc_ip = data->d_buf;
- + orc_ip_addr = sh.sh_addr;
- + } else if (!strcmp(name, ".rela.orc_unwind_ip")) {
- + rela_orc_ip = data;
- + }
- + }
- +
- + if (!symtab || !orc || !orc_ip)
- + return 0;
- +
- + if (orc_size % sizeof(*orc) != 0) {
- + WARN("bad .orc_unwind section size");
- + return -1;
- + }
- +
- + nr_entries = orc_size / sizeof(*orc);
- + for (i = 0; i < nr_entries; i++) {
- + if (rela_orc_ip) {
- + if (!gelf_getrela(rela_orc_ip, i, &rela)) {
- + WARN_ELF("gelf_getrela");
- + return -1;
- + }
- +
- + if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) {
- + WARN_ELF("gelf_getsym");
- + return -1;
- + }
- +
- + scn = elf_getscn(elf, sym.st_shndx);
- + if (!scn) {
- + WARN_ELF("elf_getscn");
- + return -1;
- + }
- +
- + if (!gelf_getshdr(scn, &sh)) {
- + WARN_ELF("gelf_getshdr");
- + return -1;
- + }
- +
- + name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
- + if (!name || !*name) {
- + WARN_ELF("elf_strptr");
- + return -1;
- + }
- +
- + printf("%s+%lx:", name, rela.r_addend);
- +
- + } else {
- + printf("%lx:", orc_ip_addr + (i * sizeof(int)) + orc_ip[i]);
- + }
- +
- +
- + printf(" sp:");
- +
- + print_reg(orc[i].sp_reg, orc[i].sp_offset);
- +
- + printf(" bp:");
- +
- + print_reg(orc[i].bp_reg, orc[i].bp_offset);
- +
- + printf(" type:%s\n", orc_type_name(orc[i].type));
- + }
- +
- + elf_end(elf);
- + close(fd);
- +
- + return 0;
- +}
- diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c
- new file mode 100644
- index 000000000000..e5ca31429c9b
- --- /dev/null
- +++ b/tools/objtool/orc_gen.c
- @@ -0,0 +1,214 @@
- +/*
- + * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
- + *
- + * This program is free software; you can redistribute it and/or
- + * modify it under the terms of the GNU General Public License
- + * as published by the Free Software Foundation; either version 2
- + * of the License, or (at your option) any later version.
- + *
- + * This program is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with this program; if not, see <http://www.gnu.org/licenses/>.
- + */
- +
- +#include <stdlib.h>
- +#include <string.h>
- +
- +#include "orc.h"
- +#include "check.h"
- +#include "warn.h"
- +
- +int create_orc(struct objtool_file *file)
- +{
- + struct instruction *insn;
- +
- + for_each_insn(file, insn) {
- + struct orc_entry *orc = &insn->orc;
- + struct cfi_reg *cfa = &insn->state.cfa;
- + struct cfi_reg *bp = &insn->state.regs[CFI_BP];
- +
- + if (cfa->base == CFI_UNDEFINED) {
- + orc->sp_reg = ORC_REG_UNDEFINED;
- + continue;
- + }
- +
- + switch (cfa->base) {
- + case CFI_SP:
- + orc->sp_reg = ORC_REG_SP;
- + break;
- + case CFI_SP_INDIRECT:
- + orc->sp_reg = ORC_REG_SP_INDIRECT;
- + break;
- + case CFI_BP:
- + orc->sp_reg = ORC_REG_BP;
- + break;
- + case CFI_BP_INDIRECT:
- + orc->sp_reg = ORC_REG_BP_INDIRECT;
- + break;
- + case CFI_R10:
- + orc->sp_reg = ORC_REG_R10;
- + break;
- + case CFI_R13:
- + orc->sp_reg = ORC_REG_R13;
- + break;
- + case CFI_DI:
- + orc->sp_reg = ORC_REG_DI;
- + break;
- + case CFI_DX:
- + orc->sp_reg = ORC_REG_DX;
- + break;
- + default:
- + WARN_FUNC("unknown CFA base reg %d",
- + insn->sec, insn->offset, cfa->base);
- + return -1;
- + }
- +
- + switch(bp->base) {
- + case CFI_UNDEFINED:
- + orc->bp_reg = ORC_REG_UNDEFINED;
- + break;
- + case CFI_CFA:
- + orc->bp_reg = ORC_REG_PREV_SP;
- + break;
- + case CFI_BP:
- + orc->bp_reg = ORC_REG_BP;
- + break;
- + default:
- + WARN_FUNC("unknown BP base reg %d",
- + insn->sec, insn->offset, bp->base);
- + return -1;
- + }
- +
- + orc->sp_offset = cfa->offset;
- + orc->bp_offset = bp->offset;
- + orc->type = insn->state.type;
- + }
- +
- + return 0;
- +}
- +
- +static int create_orc_entry(struct section *u_sec, struct section *ip_relasec,
- + unsigned int idx, struct section *insn_sec,
- + unsigned long insn_off, struct orc_entry *o)
- +{
- + struct orc_entry *orc;
- + struct rela *rela;
- +
- + /* populate ORC data */
- + orc = (struct orc_entry *)u_sec->data->d_buf + idx;
- + memcpy(orc, o, sizeof(*orc));
- +
- + /* populate rela for ip */
- + rela = malloc(sizeof(*rela));
- + if (!rela) {
- + perror("malloc");
- + return -1;
- + }
- + memset(rela, 0, sizeof(*rela));
- +
- + rela->sym = insn_sec->sym;
- + rela->addend = insn_off;
- + rela->type = R_X86_64_PC32;
- + rela->offset = idx * sizeof(int);
- +
- + list_add_tail(&rela->list, &ip_relasec->rela_list);
- + hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset);
- +
- + return 0;
- +}
- +
- +int create_orc_sections(struct objtool_file *file)
- +{
- + struct instruction *insn, *prev_insn;
- + struct section *sec, *u_sec, *ip_relasec;
- + unsigned int idx;
- +
- + struct orc_entry empty = {
- + .sp_reg = ORC_REG_UNDEFINED,
- + .bp_reg = ORC_REG_UNDEFINED,
- + .type = ORC_TYPE_CALL,
- + };
- +
- + sec = find_section_by_name(file->elf, ".orc_unwind");
- + if (sec) {
- + WARN("file already has .orc_unwind section, skipping");
- + return -1;
- + }
- +
- + /* count the number of needed orcs */
- + idx = 0;
- + for_each_sec(file, sec) {
- + if (!sec->text)
- + continue;
- +
- + prev_insn = NULL;
- + sec_for_each_insn(file, sec, insn) {
- + if (!prev_insn ||
- + memcmp(&insn->orc, &prev_insn->orc,
- + sizeof(struct orc_entry))) {
- + idx++;
- + }
- + prev_insn = insn;
- + }
- +
- + /* section terminator */
- + if (prev_insn)
- + idx++;
- + }
- + if (!idx)
- + return -1;
- +
- +
- + /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
- + sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx);
- +
- + ip_relasec = elf_create_rela_section(file->elf, sec);
- + if (!ip_relasec)
- + return -1;
- +
- + /* create .orc_unwind section */
- + u_sec = elf_create_section(file->elf, ".orc_unwind",
- + sizeof(struct orc_entry), idx);
- +
- + /* populate sections */
- + idx = 0;
- + for_each_sec(file, sec) {
- + if (!sec->text)
- + continue;
- +
- + prev_insn = NULL;
- + sec_for_each_insn(file, sec, insn) {
- + if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
- + sizeof(struct orc_entry))) {
- +
- + if (create_orc_entry(u_sec, ip_relasec, idx,
- + insn->sec, insn->offset,
- + &insn->orc))
- + return -1;
- +
- + idx++;
- + }
- + prev_insn = insn;
- + }
- +
- + /* section terminator */
- + if (prev_insn) {
- + if (create_orc_entry(u_sec, ip_relasec, idx,
- + prev_insn->sec,
- + prev_insn->offset + prev_insn->len,
- + &empty))
- + return -1;
- +
- + idx++;
- + }
- + }
- +
- + if (elf_rebuild_rela_section(ip_relasec))
- + return -1;
- +
- + return 0;
- +}
- diff --git a/tools/objtool/Build b/tools/objtool/Build
- index 6f2e1987c4d9..749becdf5b90 100644
- --- a/tools/objtool/Build
- +++ b/tools/objtool/Build
- @@ -1,6 +1,9 @@
- objtool-y += arch/$(SRCARCH)/
- objtool-y += builtin-check.o
- +objtool-y += builtin-orc.o
- objtool-y += check.o
- +objtool-y += orc_gen.o
- +objtool-y += orc_dump.o
- objtool-y += elf.o
- objtool-y += special.o
- objtool-y += objtool.o
- --
- 2.14.2
|