objtool updates:
- Make objtool work for big-endian cross compiles
- Make stack tracking via stack pointer memory operations match push/pop
semantics to prepare for architectures w/o PUSH/POP instructions.
- Add support for analyzing alternatives
- Improve retpoline detection and handling
- Improve assembly code coverage on x86
- Provide support for inlined stack switching
-----BEGIN PGP SIGNATURE-----
iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmA1FUcTHHRnbHhAbGlu
dXRyb25peC5kZQAKCRCmGPVMDXSYoe+0D/9ytW3AfQUOGlVHVPTwCAd2LSCL2kQR
zrUAyUEwEXDuZi2vOcmgndr9AToszdBnAlxSOStJYE1/ia/ptbYjj9eFOWkCwPw2
R0DSjTHh+Ui2yPjcbYvOcMphc7DTT1ssMvRWzw0I3fjfJaYBJjNx1qdseN2yhFrL
BNhdh4B4StEfCbNBMhnzKTZNM1yXNN93ojot9suxnqPIAV6ruc5SUrd9Pmii2odX
gRHQthGSPMR9nJYWrT2QzbDrM2DWkKIGUol0Xr1LTFYWNFsK3sTQkFiMevTP5Msw
qO01lw4IKCMKMonaE0t/vxFBz5vhIyivxLQMI3LBixmf2dbE9UbZqW0ONPYoZJgf
MrYyz4Tdv2u/MklTPM263cbTsdtmGEuW2iVRqaDDWP/Py1A187bUaVkw8p/9O/9V
CBl8dMF3ag1FquxnsyHDowHKu8DaIZyeBHu69aNfAlcOrtn8ZtY4MwQbQkL9cNYe
ywLEmCm8zdYNrXlVOuMX/0AAWnSpqCgDYUmKhOLW4W1r4ewNpAUCmvIL8cpLtko0
FDbMTdKU2pd5SQv5YX6Bvvra483DvP9rNAuQGHpxZ7ubSlj8cFOT9UmjuuOb4fxQ
EFj8JrF9KEN5sxGUu4tjg0D0Ee3wDdSTGs0cUN5FBMXelQOM7U4n4Y7n/Pas/LMa
B5TVW3JiDcMcPg==
=0AHf
-----END PGP SIGNATURE-----
Merge tag 'objtool-core-2021-02-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull objtool updates from Thomas Gleixner:
- Make objtool work for big-endian cross compiles
- Make stack tracking via stack pointer memory operations match
push/pop semantics to prepare for architectures w/o PUSH/POP
instructions.
- Add support for analyzing alternatives
- Improve retpoline detection and handling
- Improve assembly code coverage on x86
- Provide support for inlined stack switching
* tag 'objtool-core-2021-02-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (33 commits)
objtool: Support stack-swizzle
objtool,x86: Additionally decode: mov %rsp, (%reg)
x86/unwind/orc: Change REG_SP_INDIRECT
x86/power: Support objtool validation in hibernate_asm_64.S
x86/power: Move restore_registers() to top of the file
x86/power: Annotate indirect branches as safe
x86/acpi: Support objtool validation in wakeup_64.S
x86/acpi: Annotate indirect branch as safe
x86/ftrace: Support objtool vmlinux.o validation in ftrace_64.S
x86/xen/pvh: Annotate indirect branch as safe
x86/xen: Support objtool vmlinux.o validation in xen-head.S
x86/xen: Support objtool validation in xen-asm.S
objtool: Add xen_start_kernel() to noreturn list
objtool: Combine UNWIND_HINT_RET_OFFSET and UNWIND_HINT_FUNC
objtool: Add asm version of STACK_FRAME_NON_STANDARD
objtool: Assume only ELF functions do sibling calls
x86/ftrace: Add UNWIND_HINT_FUNC annotation for ftrace_stub
objtool: Support retpoline jump detection for vmlinux.o
objtool: Fix ".cold" section suffix check for newer versions of GCC
objtool: Fix retpoline detection in asm code
...
This commit is contained in:
commit
a56ff24efb
49 changed files with 997 additions and 632 deletions
2
tools/objtool/.gitignore
vendored
2
tools/objtool/.gitignore
vendored
|
|
@ -1,4 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
arch/x86/lib/inat-tables.c
|
||||
objtool
|
||||
/objtool
|
||||
fixdep
|
||||
|
|
|
|||
|
|
@ -315,13 +315,15 @@ they mean, and suggestions for how to fix them.
|
|||
function tracing inserts additional calls, which is not obvious from the
|
||||
sources).
|
||||
|
||||
10. file.o: warning: func()+0x5c: alternative modifies stack
|
||||
10. file.o: warning: func()+0x5c: stack layout conflict in alternatives
|
||||
|
||||
This means that an alternative includes instructions that modify the
|
||||
stack. The problem is that there is only one ORC unwind table, this means
|
||||
that the ORC unwind entries must be valid for each of the alternatives.
|
||||
The easiest way to enforce this is to ensure alternatives do not contain
|
||||
any ORC entries, which in turn implies the above constraint.
|
||||
This means that in the use of the alternative() or ALTERNATIVE()
|
||||
macro, the code paths have conflicting modifications to the stack.
|
||||
The problem is that there is only one ORC unwind table, which means
|
||||
that the ORC unwind entries must be consistent for all possible
|
||||
instruction boundaries regardless of which code has been patched.
|
||||
This limitation can be overcome by massaging the alternatives with
|
||||
NOPs to shift the stack changes around so they no longer conflict.
|
||||
|
||||
11. file.o: warning: unannotated intra-function call
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ all: $(OBJTOOL)
|
|||
INCLUDES := -I$(srctree)/tools/include \
|
||||
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
|
||||
-I$(srctree)/tools/arch/$(SRCARCH)/include \
|
||||
-I$(srctree)/tools/objtool/include \
|
||||
-I$(srctree)/tools/objtool/arch/$(SRCARCH)/include
|
||||
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs
|
||||
CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
|
||||
|
|
@ -46,10 +47,6 @@ ifeq ($(SRCARCH),x86)
|
|||
SUBCMD_ORC := y
|
||||
endif
|
||||
|
||||
ifeq ($(SUBCMD_ORC),y)
|
||||
CFLAGS += -DINSN_USE_ORC
|
||||
endif
|
||||
|
||||
export SUBCMD_CHECK SUBCMD_ORC
|
||||
export srctree OUTPUT CFLAGS SRCARCH AWK
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@
|
|||
#include "../../../arch/x86/lib/inat.c"
|
||||
#include "../../../arch/x86/lib/insn.c"
|
||||
|
||||
#include "../../check.h"
|
||||
#include "../../elf.h"
|
||||
#include "../../arch.h"
|
||||
#include "../../warn.h"
|
||||
#include <asm/orc_types.h>
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/elf.h>
|
||||
#include <objtool/arch.h>
|
||||
#include <objtool/warn.h>
|
||||
|
||||
static unsigned char op_to_cfi_reg[][2] = {
|
||||
{CFI_AX, CFI_R8},
|
||||
|
|
@ -222,15 +222,38 @@ int arch_decode_instruction(const struct elf *elf, const struct section *sec,
|
|||
break;
|
||||
|
||||
case 0x89:
|
||||
if (rex_w && !rex_r && modrm_mod == 3 && modrm_reg == 4) {
|
||||
if (rex_w && !rex_r && modrm_reg == 4) {
|
||||
|
||||
/* mov %rsp, reg */
|
||||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_REG;
|
||||
op->src.reg = CFI_SP;
|
||||
op->dest.type = OP_DEST_REG;
|
||||
op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b];
|
||||
if (modrm_mod == 3) {
|
||||
/* mov %rsp, reg */
|
||||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_REG;
|
||||
op->src.reg = CFI_SP;
|
||||
op->dest.type = OP_DEST_REG;
|
||||
op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b];
|
||||
}
|
||||
break;
|
||||
|
||||
} else {
|
||||
/* skip nontrivial SIB */
|
||||
if (modrm_rm == 4 && !(sib == 0x24 && rex_b == rex_x))
|
||||
break;
|
||||
|
||||
/* skip RIP relative displacement */
|
||||
if (modrm_rm == 5 && modrm_mod == 0)
|
||||
break;
|
||||
|
||||
/* mov %rsp, disp(%reg) */
|
||||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_REG;
|
||||
op->src.reg = CFI_SP;
|
||||
op->dest.type = OP_DEST_REG_INDIRECT;
|
||||
op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b];
|
||||
op->dest.offset = insn.displacement.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -259,8 +282,10 @@ int arch_decode_instruction(const struct elf *elf, const struct section *sec,
|
|||
op->dest.reg = CFI_BP;
|
||||
op->dest.offset = insn.displacement.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) {
|
||||
if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) {
|
||||
|
||||
/* mov reg, disp(%rsp) */
|
||||
ADD_OP(op) {
|
||||
|
|
@ -270,6 +295,7 @@ int arch_decode_instruction(const struct elf *elf, const struct section *sec,
|
|||
op->dest.reg = CFI_SP;
|
||||
op->dest.offset = insn.displacement.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -563,8 +589,8 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state)
|
|||
state->cfa.offset = 8;
|
||||
|
||||
/* initial RA (return address) */
|
||||
state->regs[16].base = CFI_CFA;
|
||||
state->regs[16].offset = -8;
|
||||
state->regs[CFI_RA].base = CFI_CFA;
|
||||
state->regs[CFI_RA].offset = -8;
|
||||
}
|
||||
|
||||
const char *arch_nop_insn(int len)
|
||||
|
|
|
|||
9
tools/objtool/arch/x86/include/arch/endianness.h
Normal file
9
tools/objtool/arch/x86/include/arch/endianness.h
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _ARCH_ENDIANNESS_H
|
||||
#define _ARCH_ENDIANNESS_H
|
||||
|
||||
#include <endian.h>
|
||||
|
||||
#define __TARGET_BYTE_ORDER __LITTLE_ENDIAN
|
||||
|
||||
#endif /* _ARCH_ENDIANNESS_H */
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <string.h>
|
||||
|
||||
#include "../../special.h"
|
||||
#include "../../builtin.h"
|
||||
#include <objtool/special.h>
|
||||
#include <objtool/builtin.h>
|
||||
|
||||
#define X86_FEATURE_POPCNT (4 * 32 + 23)
|
||||
#define X86_FEATURE_SMAP (9 * 32 + 20)
|
||||
|
|
@ -48,7 +48,7 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
|
|||
* replacement group.
|
||||
*/
|
||||
return insn->offset == special_alt->new_off &&
|
||||
(insn->type == INSN_CALL || is_static_jump(insn));
|
||||
(insn->type == INSN_CALL || is_jump(insn));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
|
||||
#include <subcmd/parse-options.h>
|
||||
#include <string.h>
|
||||
#include "builtin.h"
|
||||
#include "objtool.h"
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/objtool.h>
|
||||
|
||||
bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "builtin.h"
|
||||
#include "objtool.h"
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/objtool.h>
|
||||
|
||||
static const char *orc_usage[] = {
|
||||
"objtool orc generate [<options>] file.o",
|
||||
|
|
@ -51,11 +51,7 @@ int cmd_orc(int argc, const char **argv)
|
|||
if (list_empty(&file->insn_list))
|
||||
return 0;
|
||||
|
||||
ret = create_orc(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = create_orc_sections(file);
|
||||
ret = orc_create(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,21 +6,20 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "builtin.h"
|
||||
#include "cfi.h"
|
||||
#include "arch.h"
|
||||
#include "check.h"
|
||||
#include "special.h"
|
||||
#include "warn.h"
|
||||
#include "arch_elf.h"
|
||||
#include <arch/elf.h>
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/cfi.h>
|
||||
#include <objtool/arch.h>
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/special.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/static_call_types.h>
|
||||
|
||||
#define FAKE_JUMP_OFFSET -1
|
||||
|
||||
struct alternative {
|
||||
struct list_head list;
|
||||
struct instruction *insn;
|
||||
|
|
@ -111,15 +110,20 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
|
|||
|
||||
static bool is_sibling_call(struct instruction *insn)
|
||||
{
|
||||
/*
|
||||
* Assume only ELF functions can make sibling calls. This ensures
|
||||
* sibling call detection consistency between vmlinux.o and individual
|
||||
* objects.
|
||||
*/
|
||||
if (!insn->func)
|
||||
return false;
|
||||
|
||||
/* An indirect jump is either a sibling call or a jump to a table. */
|
||||
if (insn->type == INSN_JUMP_DYNAMIC)
|
||||
return list_empty(&insn->alts);
|
||||
|
||||
if (!is_static_jump(insn))
|
||||
return false;
|
||||
|
||||
/* add_jump_destinations() sets insn->call_dest for sibling calls. */
|
||||
return !!insn->call_dest;
|
||||
return (is_static_jump(insn) && insn->call_dest);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -156,6 +160,7 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
|
|||
"machine_real_restart",
|
||||
"rewind_stack_do_exit",
|
||||
"kunit_try_catch_throw",
|
||||
"xen_start_kernel",
|
||||
};
|
||||
|
||||
if (!func)
|
||||
|
|
@ -787,22 +792,16 @@ static int add_jump_destinations(struct objtool_file *file)
|
|||
if (!is_static_jump(insn))
|
||||
continue;
|
||||
|
||||
if (insn->offset == FAKE_JUMP_OFFSET)
|
||||
continue;
|
||||
|
||||
reloc = find_reloc_by_dest_range(file->elf, insn->sec,
|
||||
insn->offset, insn->len);
|
||||
insn->offset, insn->len);
|
||||
if (!reloc) {
|
||||
dest_sec = insn->sec;
|
||||
dest_off = arch_jump_destination(insn);
|
||||
} else if (reloc->sym->type == STT_SECTION) {
|
||||
dest_sec = reloc->sym->sec;
|
||||
dest_off = arch_dest_reloc_offset(reloc->addend);
|
||||
} else if (reloc->sym->sec->idx) {
|
||||
dest_sec = reloc->sym->sec;
|
||||
dest_off = reloc->sym->sym.st_value +
|
||||
arch_dest_reloc_offset(reloc->addend);
|
||||
} else if (strstr(reloc->sym->name, "_indirect_thunk_")) {
|
||||
} else if (!strncmp(reloc->sym->name, "__x86_indirect_thunk_", 21) ||
|
||||
!strncmp(reloc->sym->name, "__x86_retpoline_", 16)) {
|
||||
/*
|
||||
* Retpoline jumps are really dynamic jumps in
|
||||
* disguise, so convert them accordingly.
|
||||
|
|
@ -814,14 +813,21 @@ static int add_jump_destinations(struct objtool_file *file)
|
|||
|
||||
insn->retpoline_safe = true;
|
||||
continue;
|
||||
} else {
|
||||
/* external sibling call */
|
||||
} else if (insn->func) {
|
||||
/* internal or external sibling call (with reloc) */
|
||||
insn->call_dest = reloc->sym;
|
||||
if (insn->call_dest->static_call_tramp) {
|
||||
list_add_tail(&insn->static_call_node,
|
||||
&file->static_call_list);
|
||||
}
|
||||
continue;
|
||||
} else if (reloc->sym->sec->idx) {
|
||||
dest_sec = reloc->sym->sec;
|
||||
dest_off = reloc->sym->sym.st_value +
|
||||
arch_dest_reloc_offset(reloc->addend);
|
||||
} else {
|
||||
/* non-func asm code jumping to another file */
|
||||
continue;
|
||||
}
|
||||
|
||||
insn->jump_dest = find_insn(file, dest_sec, dest_off);
|
||||
|
|
@ -862,15 +868,15 @@ static int add_jump_destinations(struct objtool_file *file)
|
|||
* case where the parent function's only reference to a
|
||||
* subfunction is through a jump table.
|
||||
*/
|
||||
if (!strstr(insn->func->name, ".cold.") &&
|
||||
strstr(insn->jump_dest->func->name, ".cold.")) {
|
||||
if (!strstr(insn->func->name, ".cold") &&
|
||||
strstr(insn->jump_dest->func->name, ".cold")) {
|
||||
insn->func->cfunc = insn->jump_dest->func;
|
||||
insn->jump_dest->func->pfunc = insn->func;
|
||||
|
||||
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
|
||||
insn->jump_dest->offset == insn->jump_dest->func->offset) {
|
||||
|
||||
/* internal sibling call */
|
||||
/* internal sibling call (without reloc) */
|
||||
insn->call_dest = insn->jump_dest->func;
|
||||
if (insn->call_dest->static_call_tramp) {
|
||||
list_add_tail(&insn->static_call_node,
|
||||
|
|
@ -983,73 +989,83 @@ static int add_call_destinations(struct objtool_file *file)
|
|||
}
|
||||
|
||||
/*
|
||||
* The .alternatives section requires some extra special care, over and above
|
||||
* what other special sections require:
|
||||
*
|
||||
* 1. Because alternatives are patched in-place, we need to insert a fake jump
|
||||
* instruction at the end so that validate_branch() skips all the original
|
||||
* replaced instructions when validating the new instruction path.
|
||||
*
|
||||
* 2. An added wrinkle is that the new instruction length might be zero. In
|
||||
* that case the old instructions are replaced with noops. We simulate that
|
||||
* by creating a fake jump as the only new instruction.
|
||||
*
|
||||
* 3. In some cases, the alternative section includes an instruction which
|
||||
* conditionally jumps to the _end_ of the entry. We have to modify these
|
||||
* jumps' destinations to point back to .text rather than the end of the
|
||||
* entry in .altinstr_replacement.
|
||||
* The .alternatives section requires some extra special care over and above
|
||||
* other special sections because alternatives are patched in place.
|
||||
*/
|
||||
static int handle_group_alt(struct objtool_file *file,
|
||||
struct special_alt *special_alt,
|
||||
struct instruction *orig_insn,
|
||||
struct instruction **new_insn)
|
||||
{
|
||||
static unsigned int alt_group_next_index = 1;
|
||||
struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump = NULL;
|
||||
unsigned int alt_group = alt_group_next_index++;
|
||||
struct instruction *last_orig_insn, *last_new_insn = NULL, *insn, *nop = NULL;
|
||||
struct alt_group *orig_alt_group, *new_alt_group;
|
||||
unsigned long dest_off;
|
||||
|
||||
|
||||
orig_alt_group = malloc(sizeof(*orig_alt_group));
|
||||
if (!orig_alt_group) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
orig_alt_group->cfi = calloc(special_alt->orig_len,
|
||||
sizeof(struct cfi_state *));
|
||||
if (!orig_alt_group->cfi) {
|
||||
WARN("calloc failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
last_orig_insn = NULL;
|
||||
insn = orig_insn;
|
||||
sec_for_each_insn_from(file, insn) {
|
||||
if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
|
||||
break;
|
||||
|
||||
insn->alt_group = alt_group;
|
||||
insn->alt_group = orig_alt_group;
|
||||
last_orig_insn = insn;
|
||||
}
|
||||
orig_alt_group->orig_group = NULL;
|
||||
orig_alt_group->first_insn = orig_insn;
|
||||
orig_alt_group->last_insn = last_orig_insn;
|
||||
|
||||
if (next_insn_same_sec(file, last_orig_insn)) {
|
||||
fake_jump = malloc(sizeof(*fake_jump));
|
||||
if (!fake_jump) {
|
||||
|
||||
new_alt_group = malloc(sizeof(*new_alt_group));
|
||||
if (!new_alt_group) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (special_alt->new_len < special_alt->orig_len) {
|
||||
/*
|
||||
* Insert a fake nop at the end to make the replacement
|
||||
* alt_group the same size as the original. This is needed to
|
||||
* allow propagate_alt_cfi() to do its magic. When the last
|
||||
* instruction affects the stack, the instruction after it (the
|
||||
* nop) will propagate the new state to the shared CFI array.
|
||||
*/
|
||||
nop = malloc(sizeof(*nop));
|
||||
if (!nop) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
memset(fake_jump, 0, sizeof(*fake_jump));
|
||||
INIT_LIST_HEAD(&fake_jump->alts);
|
||||
INIT_LIST_HEAD(&fake_jump->stack_ops);
|
||||
init_cfi_state(&fake_jump->cfi);
|
||||
memset(nop, 0, sizeof(*nop));
|
||||
INIT_LIST_HEAD(&nop->alts);
|
||||
INIT_LIST_HEAD(&nop->stack_ops);
|
||||
init_cfi_state(&nop->cfi);
|
||||
|
||||
fake_jump->sec = special_alt->new_sec;
|
||||
fake_jump->offset = FAKE_JUMP_OFFSET;
|
||||
fake_jump->type = INSN_JUMP_UNCONDITIONAL;
|
||||
fake_jump->jump_dest = list_next_entry(last_orig_insn, list);
|
||||
fake_jump->func = orig_insn->func;
|
||||
nop->sec = special_alt->new_sec;
|
||||
nop->offset = special_alt->new_off + special_alt->new_len;
|
||||
nop->len = special_alt->orig_len - special_alt->new_len;
|
||||
nop->type = INSN_NOP;
|
||||
nop->func = orig_insn->func;
|
||||
nop->alt_group = new_alt_group;
|
||||
nop->ignore = orig_insn->ignore_alts;
|
||||
}
|
||||
|
||||
if (!special_alt->new_len) {
|
||||
if (!fake_jump) {
|
||||
WARN("%s: empty alternative at end of section",
|
||||
special_alt->orig_sec->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*new_insn = fake_jump;
|
||||
return 0;
|
||||
*new_insn = nop;
|
||||
goto end;
|
||||
}
|
||||
|
||||
last_new_insn = NULL;
|
||||
alt_group = alt_group_next_index++;
|
||||
insn = *new_insn;
|
||||
sec_for_each_insn_from(file, insn) {
|
||||
struct reloc *alt_reloc;
|
||||
|
|
@ -1061,7 +1077,7 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
|
||||
insn->ignore = orig_insn->ignore_alts;
|
||||
insn->func = orig_insn->func;
|
||||
insn->alt_group = alt_group;
|
||||
insn->alt_group = new_alt_group;
|
||||
|
||||
/*
|
||||
* Since alternative replacement code is copy/pasted by the
|
||||
|
|
@ -1088,14 +1104,8 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
continue;
|
||||
|
||||
dest_off = arch_jump_destination(insn);
|
||||
if (dest_off == special_alt->new_off + special_alt->new_len) {
|
||||
if (!fake_jump) {
|
||||
WARN("%s: alternative jump to end of section",
|
||||
special_alt->orig_sec->name);
|
||||
return -1;
|
||||
}
|
||||
insn->jump_dest = fake_jump;
|
||||
}
|
||||
if (dest_off == special_alt->new_off + special_alt->new_len)
|
||||
insn->jump_dest = next_insn_same_sec(file, last_orig_insn);
|
||||
|
||||
if (!insn->jump_dest) {
|
||||
WARN_FUNC("can't find alternative jump destination",
|
||||
|
|
@ -1110,9 +1120,13 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (fake_jump)
|
||||
list_add(&fake_jump->list, &last_new_insn->list);
|
||||
|
||||
if (nop)
|
||||
list_add(&nop->list, &last_new_insn->list);
|
||||
end:
|
||||
new_alt_group->orig_group = orig_alt_group;
|
||||
new_alt_group->first_insn = *new_insn;
|
||||
new_alt_group->last_insn = nop ? : last_new_insn;
|
||||
new_alt_group->cfi = orig_alt_group->cfi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1404,13 +1418,20 @@ static int add_jump_table_alts(struct objtool_file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void set_func_state(struct cfi_state *state)
|
||||
{
|
||||
state->cfa = initial_func_cfi.cfa;
|
||||
memcpy(&state->regs, &initial_func_cfi.regs,
|
||||
CFI_NUM_REGS * sizeof(struct cfi_reg));
|
||||
state->stack_size = initial_func_cfi.cfa.offset;
|
||||
}
|
||||
|
||||
static int read_unwind_hints(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec, *relocsec;
|
||||
struct reloc *reloc;
|
||||
struct unwind_hint *hint;
|
||||
struct instruction *insn;
|
||||
struct cfi_reg *cfa;
|
||||
int i;
|
||||
|
||||
sec = find_section_by_name(file->elf, ".discard.unwind_hints");
|
||||
|
|
@ -1445,22 +1466,20 @@ static int read_unwind_hints(struct objtool_file *file)
|
|||
return -1;
|
||||
}
|
||||
|
||||
cfa = &insn->cfi.cfa;
|
||||
insn->hint = true;
|
||||
|
||||
if (hint->type == UNWIND_HINT_TYPE_RET_OFFSET) {
|
||||
insn->ret_offset = hint->sp_offset;
|
||||
if (hint->type == UNWIND_HINT_TYPE_FUNC) {
|
||||
set_func_state(&insn->cfi);
|
||||
continue;
|
||||
}
|
||||
|
||||
insn->hint = true;
|
||||
|
||||
if (arch_decode_hint_reg(insn, hint->sp_reg)) {
|
||||
WARN_FUNC("unsupported unwind_hint sp base reg %d",
|
||||
insn->sec, insn->offset, hint->sp_reg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfa->offset = hint->sp_offset;
|
||||
insn->cfi.cfa.offset = bswap_if_needed(hint->sp_offset);
|
||||
insn->cfi.type = hint->type;
|
||||
insn->cfi.end = hint->end;
|
||||
}
|
||||
|
|
@ -1716,27 +1735,18 @@ static bool is_fentry_call(struct instruction *insn)
|
|||
|
||||
static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
|
||||
{
|
||||
u8 ret_offset = insn->ret_offset;
|
||||
struct cfi_state *cfi = &state->cfi;
|
||||
int i;
|
||||
|
||||
if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
|
||||
return true;
|
||||
|
||||
if (cfi->cfa.offset != initial_func_cfi.cfa.offset + ret_offset)
|
||||
if (cfi->cfa.offset != initial_func_cfi.cfa.offset)
|
||||
return true;
|
||||
|
||||
if (cfi->stack_size != initial_func_cfi.cfa.offset + ret_offset)
|
||||
if (cfi->stack_size != initial_func_cfi.cfa.offset)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* If there is a ret offset hint then don't check registers
|
||||
* because a callee-saved register might have been pushed on
|
||||
* the stack.
|
||||
*/
|
||||
if (ret_offset)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < CFI_NUM_REGS; i++) {
|
||||
if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
|
||||
cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
|
||||
|
|
@ -1746,12 +1756,20 @@ static bool has_modified_stack_frame(struct instruction *insn, struct insn_state
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool check_reg_frame_pos(const struct cfi_reg *reg,
|
||||
int expected_offset)
|
||||
{
|
||||
return reg->base == CFI_CFA &&
|
||||
reg->offset == expected_offset;
|
||||
}
|
||||
|
||||
static bool has_valid_stack_frame(struct insn_state *state)
|
||||
{
|
||||
struct cfi_state *cfi = &state->cfi;
|
||||
|
||||
if (cfi->cfa.base == CFI_BP && cfi->regs[CFI_BP].base == CFI_CFA &&
|
||||
cfi->regs[CFI_BP].offset == -16)
|
||||
if (cfi->cfa.base == CFI_BP &&
|
||||
check_reg_frame_pos(&cfi->regs[CFI_BP], -cfi->cfa.offset) &&
|
||||
check_reg_frame_pos(&cfi->regs[CFI_RA], -cfi->cfa.offset + 8))
|
||||
return true;
|
||||
|
||||
if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
|
||||
|
|
@ -1880,8 +1898,7 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
case OP_SRC_REG:
|
||||
if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP &&
|
||||
cfa->base == CFI_SP &&
|
||||
regs[CFI_BP].base == CFI_CFA &&
|
||||
regs[CFI_BP].offset == -cfa->offset) {
|
||||
check_reg_frame_pos(®s[CFI_BP], -cfa->offset)) {
|
||||
|
||||
/* mov %rsp, %rbp */
|
||||
cfa->base = op->dest.reg;
|
||||
|
|
@ -1941,6 +1958,38 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
cfa->offset = -cfi->vals[op->src.reg].offset;
|
||||
cfi->stack_size = cfa->offset;
|
||||
|
||||
} else if (cfa->base == CFI_SP &&
|
||||
cfi->vals[op->src.reg].base == CFI_SP_INDIRECT &&
|
||||
cfi->vals[op->src.reg].offset == cfa->offset) {
|
||||
|
||||
/*
|
||||
* Stack swizzle:
|
||||
*
|
||||
* 1: mov %rsp, (%[tos])
|
||||
* 2: mov %[tos], %rsp
|
||||
* ...
|
||||
* 3: pop %rsp
|
||||
*
|
||||
* Where:
|
||||
*
|
||||
* 1 - places a pointer to the previous
|
||||
* stack at the Top-of-Stack of the
|
||||
* new stack.
|
||||
*
|
||||
* 2 - switches to the new stack.
|
||||
*
|
||||
* 3 - pops the Top-of-Stack to restore
|
||||
* the original stack.
|
||||
*
|
||||
* Note: we set base to SP_INDIRECT
|
||||
* here and preserve offset. Therefore
|
||||
* when the unwinder reaches ToS it
|
||||
* will dereference SP and then add the
|
||||
* offset to find the next frame, IOW:
|
||||
* (%rsp) + offset.
|
||||
*/
|
||||
cfa->base = CFI_SP_INDIRECT;
|
||||
|
||||
} else {
|
||||
cfa->base = CFI_UNDEFINED;
|
||||
cfa->offset = 0;
|
||||
|
|
@ -1966,6 +2015,17 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
break;
|
||||
}
|
||||
|
||||
if (!cfi->drap && op->src.reg == CFI_SP &&
|
||||
op->dest.reg == CFI_BP && cfa->base == CFI_SP &&
|
||||
check_reg_frame_pos(®s[CFI_BP], -cfa->offset + op->src.offset)) {
|
||||
|
||||
/* lea disp(%rsp), %rbp */
|
||||
cfa->base = CFI_BP;
|
||||
cfa->offset -= op->src.offset;
|
||||
cfi->bp_scratch = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
|
||||
|
||||
/* drap: lea disp(%rsp), %drap */
|
||||
|
|
@ -2032,6 +2092,13 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
|
||||
case OP_SRC_POP:
|
||||
case OP_SRC_POPF:
|
||||
if (op->dest.reg == CFI_SP && cfa->base == CFI_SP_INDIRECT) {
|
||||
|
||||
/* pop %rsp; # restore from a stack swizzle */
|
||||
cfa->base = CFI_SP;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cfi->drap && op->dest.reg == cfa->base) {
|
||||
|
||||
/* pop %rbp */
|
||||
|
|
@ -2060,6 +2127,14 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
break;
|
||||
|
||||
case OP_SRC_REG_INDIRECT:
|
||||
if (!cfi->drap && op->dest.reg == cfa->base &&
|
||||
op->dest.reg == CFI_BP) {
|
||||
|
||||
/* mov disp(%rsp), %rbp */
|
||||
cfa->base = CFI_SP;
|
||||
cfa->offset = cfi->stack_size;
|
||||
}
|
||||
|
||||
if (cfi->drap && op->src.reg == CFI_BP &&
|
||||
op->src.offset == cfi->drap_offset) {
|
||||
|
||||
|
|
@ -2081,6 +2156,12 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
/* mov disp(%rbp), %reg */
|
||||
/* mov disp(%rsp), %reg */
|
||||
restore_reg(cfi, op->dest.reg);
|
||||
|
||||
} else if (op->src.reg == CFI_SP &&
|
||||
op->src.offset == regs[op->dest.reg].offset + cfi->stack_size) {
|
||||
|
||||
/* mov disp(%rsp), %reg */
|
||||
restore_reg(cfi, op->dest.reg);
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -2158,6 +2239,18 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
/* mov reg, disp(%rsp) */
|
||||
save_reg(cfi, op->src.reg, CFI_CFA,
|
||||
op->dest.offset - cfi->cfa.offset);
|
||||
|
||||
} else if (op->dest.reg == CFI_SP) {
|
||||
|
||||
/* mov reg, disp(%rsp) */
|
||||
save_reg(cfi, op->src.reg, CFI_CFA,
|
||||
op->dest.offset - cfi->stack_size);
|
||||
|
||||
} else if (op->src.reg == CFI_SP && op->dest.offset == 0) {
|
||||
|
||||
/* mov %rsp, (%reg); # setup a stack swizzle. */
|
||||
cfi->vals[op->dest.reg].base = CFI_SP_INDIRECT;
|
||||
cfi->vals[op->dest.reg].offset = cfa->offset;
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -2205,22 +2298,47 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The stack layouts of alternatives instructions can sometimes diverge when
|
||||
* they have stack modifications. That's fine as long as the potential stack
|
||||
* layouts don't conflict at any given potential instruction boundary.
|
||||
*
|
||||
* Flatten the CFIs of the different alternative code streams (both original
|
||||
* and replacement) into a single shared CFI array which can be used to detect
|
||||
* conflicts and nicely feed a linear array of ORC entries to the unwinder.
|
||||
*/
|
||||
static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn)
|
||||
{
|
||||
struct cfi_state **alt_cfi;
|
||||
int group_off;
|
||||
|
||||
if (!insn->alt_group)
|
||||
return 0;
|
||||
|
||||
alt_cfi = insn->alt_group->cfi;
|
||||
group_off = insn->offset - insn->alt_group->first_insn->offset;
|
||||
|
||||
if (!alt_cfi[group_off]) {
|
||||
alt_cfi[group_off] = &insn->cfi;
|
||||
} else {
|
||||
if (memcmp(alt_cfi[group_off], &insn->cfi, sizeof(struct cfi_state))) {
|
||||
WARN_FUNC("stack layout conflict in alternatives",
|
||||
insn->sec, insn->offset);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_insn_ops(struct instruction *insn, struct insn_state *state)
|
||||
{
|
||||
struct stack_op *op;
|
||||
|
||||
list_for_each_entry(op, &insn->stack_ops, list) {
|
||||
struct cfi_state old_cfi = state->cfi;
|
||||
int res;
|
||||
|
||||
res = update_cfi_state(insn, &state->cfi, op);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
if (insn->alt_group && memcmp(&state->cfi, &old_cfi, sizeof(struct cfi_state))) {
|
||||
WARN_FUNC("alternative modifies stack", insn->sec, insn->offset);
|
||||
return -1;
|
||||
}
|
||||
if (update_cfi_state(insn, &state->cfi, op))
|
||||
return 1;
|
||||
|
||||
if (op->dest.type == OP_DEST_PUSHF) {
|
||||
if (!state->uaccess_stack) {
|
||||
|
|
@ -2410,28 +2528,20 @@ static int validate_return(struct symbol *func, struct instruction *insn, struct
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Alternatives should not contain any ORC entries, this in turn means they
|
||||
* should not contain any CFI ops, which implies all instructions should have
|
||||
* the same same CFI state.
|
||||
*
|
||||
* It is possible to constuct alternatives that have unreachable holes that go
|
||||
* unreported (because they're NOPs), such holes would result in CFI_UNDEFINED
|
||||
* states which then results in ORC entries, which we just said we didn't want.
|
||||
*
|
||||
* Avoid them by copying the CFI entry of the first instruction into the whole
|
||||
* alternative.
|
||||
*/
|
||||
static void fill_alternative_cfi(struct objtool_file *file, struct instruction *insn)
|
||||
static struct instruction *next_insn_to_validate(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct instruction *first_insn = insn;
|
||||
int alt_group = insn->alt_group;
|
||||
struct alt_group *alt_group = insn->alt_group;
|
||||
|
||||
sec_for_each_insn_continue(file, insn) {
|
||||
if (insn->alt_group != alt_group)
|
||||
break;
|
||||
insn->cfi = first_insn->cfi;
|
||||
}
|
||||
/*
|
||||
* Simulate the fact that alternatives are patched in-place. When the
|
||||
* end of a replacement alt_group is reached, redirect objtool flow to
|
||||
* the end of the original alt_group.
|
||||
*/
|
||||
if (alt_group && insn == alt_group->last_insn && alt_group->orig_group)
|
||||
return next_insn_same_sec(file, alt_group->orig_group->last_insn);
|
||||
|
||||
return next_insn_same_sec(file, insn);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2452,7 +2562,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
sec = insn->sec;
|
||||
|
||||
while (1) {
|
||||
next_insn = next_insn_same_sec(file, insn);
|
||||
next_insn = next_insn_to_validate(file, insn);
|
||||
|
||||
if (file->c_file && func && insn->func && func != insn->func->pfunc) {
|
||||
WARN("%s() falls through to next function %s()",
|
||||
|
|
@ -2485,6 +2595,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
|
||||
insn->visited |= visited;
|
||||
|
||||
if (propagate_alt_cfi(file, insn))
|
||||
return 1;
|
||||
|
||||
if (!insn->ignore_alts && !list_empty(&insn->alts)) {
|
||||
bool skip_orig = false;
|
||||
|
||||
|
|
@ -2500,9 +2613,6 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
}
|
||||
}
|
||||
|
||||
if (insn->alt_group)
|
||||
fill_alternative_cfi(file, insn);
|
||||
|
||||
if (skip_orig)
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2540,7 +2650,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
|
||||
case INSN_JUMP_CONDITIONAL:
|
||||
case INSN_JUMP_UNCONDITIONAL:
|
||||
if (func && is_sibling_call(insn)) {
|
||||
if (is_sibling_call(insn)) {
|
||||
ret = validate_sibling_call(insn, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -2562,7 +2672,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
|
||||
case INSN_JUMP_DYNAMIC:
|
||||
case INSN_JUMP_DYNAMIC_CONDITIONAL:
|
||||
if (func && is_sibling_call(insn)) {
|
||||
if (is_sibling_call(insn)) {
|
||||
ret = validate_sibling_call(insn, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -2605,15 +2715,19 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
break;
|
||||
|
||||
case INSN_STD:
|
||||
if (state.df)
|
||||
if (state.df) {
|
||||
WARN_FUNC("recursive STD", sec, insn->offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
state.df = true;
|
||||
break;
|
||||
|
||||
case INSN_CLD:
|
||||
if (!state.df && func)
|
||||
if (!state.df && func) {
|
||||
WARN_FUNC("redundant CLD", sec, insn->offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
state.df = false;
|
||||
break;
|
||||
|
|
@ -2736,9 +2850,6 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
|
|||
!strcmp(insn->sec->name, ".altinstr_aux"))
|
||||
return true;
|
||||
|
||||
if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->offset == FAKE_JUMP_OFFSET)
|
||||
return true;
|
||||
|
||||
if (!insn->func)
|
||||
return false;
|
||||
|
||||
|
|
@ -2824,10 +2935,7 @@ static int validate_section(struct objtool_file *file, struct section *sec)
|
|||
continue;
|
||||
|
||||
init_insn_state(&state, sec);
|
||||
state.cfi.cfa = initial_func_cfi.cfa;
|
||||
memcpy(&state.cfi.regs, &initial_func_cfi.regs,
|
||||
CFI_NUM_REGS * sizeof(struct cfi_reg));
|
||||
state.cfi.stack_size = initial_func_cfi.cfa.offset;
|
||||
set_func_state(&state.cfi);
|
||||
|
||||
warnings += validate_symbol(file, sec, func, &state);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "builtin.h"
|
||||
#include <objtool/builtin.h>
|
||||
|
||||
#include "elf.h"
|
||||
#include "warn.h"
|
||||
#include <objtool/elf.h>
|
||||
#include <objtool/warn.h>
|
||||
|
||||
#define MAX_NAME_LEN 128
|
||||
|
||||
|
|
@ -814,25 +814,27 @@ static int elf_rebuild_rel_reloc_section(struct section *sec, int nr)
|
|||
{
|
||||
struct reloc *reloc;
|
||||
int idx = 0, size;
|
||||
GElf_Rel *relocs;
|
||||
void *buf;
|
||||
|
||||
/* Allocate a buffer for relocations */
|
||||
size = nr * sizeof(*relocs);
|
||||
relocs = malloc(size);
|
||||
if (!relocs) {
|
||||
size = nr * sizeof(GElf_Rel);
|
||||
buf = malloc(size);
|
||||
if (!buf) {
|
||||
perror("malloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sec->data->d_buf = relocs;
|
||||
sec->data->d_buf = buf;
|
||||
sec->data->d_size = size;
|
||||
sec->data->d_type = ELF_T_REL;
|
||||
|
||||
sec->sh.sh_size = size;
|
||||
|
||||
idx = 0;
|
||||
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
||||
relocs[idx].r_offset = reloc->offset;
|
||||
relocs[idx].r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
|
||||
reloc->rel.r_offset = reloc->offset;
|
||||
reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
|
||||
gelf_update_rel(sec->data, idx, &reloc->rel);
|
||||
idx++;
|
||||
}
|
||||
|
||||
|
|
@ -843,26 +845,28 @@ static int elf_rebuild_rela_reloc_section(struct section *sec, int nr)
|
|||
{
|
||||
struct reloc *reloc;
|
||||
int idx = 0, size;
|
||||
GElf_Rela *relocs;
|
||||
void *buf;
|
||||
|
||||
/* Allocate a buffer for relocations with addends */
|
||||
size = nr * sizeof(*relocs);
|
||||
relocs = malloc(size);
|
||||
if (!relocs) {
|
||||
size = nr * sizeof(GElf_Rela);
|
||||
buf = malloc(size);
|
||||
if (!buf) {
|
||||
perror("malloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sec->data->d_buf = relocs;
|
||||
sec->data->d_buf = buf;
|
||||
sec->data->d_size = size;
|
||||
sec->data->d_type = ELF_T_RELA;
|
||||
|
||||
sec->sh.sh_size = size;
|
||||
|
||||
idx = 0;
|
||||
list_for_each_entry(reloc, &sec->reloc_list, list) {
|
||||
relocs[idx].r_offset = reloc->offset;
|
||||
relocs[idx].r_addend = reloc->addend;
|
||||
relocs[idx].r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
|
||||
reloc->rela.r_offset = reloc->offset;
|
||||
reloc->rela.r_addend = reloc->addend;
|
||||
reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
|
||||
gelf_update_rela(sec->data, idx, &reloc->rela);
|
||||
idx++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,12 +8,8 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
#include <linux/list.h>
|
||||
#include "objtool.h"
|
||||
#include "cfi.h"
|
||||
|
||||
#ifdef INSN_USE_ORC
|
||||
#include <asm/orc_types.h>
|
||||
#endif
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/cfi.h>
|
||||
|
||||
enum insn_type {
|
||||
INSN_JUMP_CONDITIONAL,
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
#ifndef _OBJTOOL_CFI_H
|
||||
#define _OBJTOOL_CFI_H
|
||||
|
||||
#include "cfi_regs.h"
|
||||
#include <arch/cfi_regs.h>
|
||||
|
||||
#define CFI_UNDEFINED -1
|
||||
#define CFI_CFA -2
|
||||
|
|
@ -7,8 +7,8 @@
|
|||
#define _CHECK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "cfi.h"
|
||||
#include "arch.h"
|
||||
#include <objtool/cfi.h>
|
||||
#include <objtool/arch.h>
|
||||
|
||||
struct insn_state {
|
||||
struct cfi_state cfi;
|
||||
|
|
@ -19,6 +19,23 @@ struct insn_state {
|
|||
s8 instr;
|
||||
};
|
||||
|
||||
struct alt_group {
|
||||
/*
|
||||
* Pointer from a replacement group to the original group. NULL if it
|
||||
* *is* the original group.
|
||||
*/
|
||||
struct alt_group *orig_group;
|
||||
|
||||
/* First and last instructions in the group */
|
||||
struct instruction *first_insn, *last_insn;
|
||||
|
||||
/*
|
||||
* Byte-offset-addressed len-sized array of pointers to CFI structs.
|
||||
* This is shared with the other alt_groups in the same alternative.
|
||||
*/
|
||||
struct cfi_state **cfi;
|
||||
};
|
||||
|
||||
struct instruction {
|
||||
struct list_head list;
|
||||
struct hlist_node hash;
|
||||
|
|
@ -33,8 +50,7 @@ struct instruction {
|
|||
bool retpoline_safe;
|
||||
s8 instr;
|
||||
u8 visited;
|
||||
u8 ret_offset;
|
||||
int alt_group;
|
||||
struct alt_group *alt_group;
|
||||
struct symbol *call_dest;
|
||||
struct instruction *jump_dest;
|
||||
struct instruction *first_jump_src;
|
||||
|
|
@ -43,9 +59,6 @@ struct instruction {
|
|||
struct symbol *func;
|
||||
struct list_head stack_ops;
|
||||
struct cfi_state cfi;
|
||||
#ifdef INSN_USE_ORC
|
||||
struct orc_entry orc;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline bool is_static_jump(struct instruction *insn)
|
||||
|
|
@ -54,6 +67,17 @@ static inline bool is_static_jump(struct instruction *insn)
|
|||
insn->type == INSN_JUMP_UNCONDITIONAL;
|
||||
}
|
||||
|
||||
static inline bool is_dynamic_jump(struct instruction *insn)
|
||||
{
|
||||
return insn->type == INSN_JUMP_DYNAMIC ||
|
||||
insn->type == INSN_JUMP_DYNAMIC_CONDITIONAL;
|
||||
}
|
||||
|
||||
static inline bool is_jump(struct instruction *insn)
|
||||
{
|
||||
return is_static_jump(insn) || is_dynamic_jump(insn);
|
||||
}
|
||||
|
||||
struct instruction *find_insn(struct objtool_file *file,
|
||||
struct section *sec, unsigned long offset);
|
||||
|
||||
38
tools/objtool/include/objtool/endianness.h
Normal file
38
tools/objtool/include/objtool/endianness.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _OBJTOOL_ENDIANNESS_H
|
||||
#define _OBJTOOL_ENDIANNESS_H
|
||||
|
||||
#include <arch/endianness.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <endian.h>
|
||||
|
||||
#ifndef __TARGET_BYTE_ORDER
|
||||
#error undefined arch __TARGET_BYTE_ORDER
|
||||
#endif
|
||||
|
||||
#if __BYTE_ORDER != __TARGET_BYTE_ORDER
|
||||
#define __NEED_BSWAP 1
|
||||
#else
|
||||
#define __NEED_BSWAP 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Does a byte swap if target endianness doesn't match the host, i.e. cross
|
||||
* compilation for little endian on big endian and vice versa.
|
||||
* To be used for multi-byte values conversion, which are read from / about
|
||||
* to be written to a target native endianness ELF file.
|
||||
*/
|
||||
#define bswap_if_needed(val) \
|
||||
({ \
|
||||
__typeof__(val) __ret; \
|
||||
switch (sizeof(val)) { \
|
||||
case 8: __ret = __NEED_BSWAP ? bswap_64(val) : (val); break; \
|
||||
case 4: __ret = __NEED_BSWAP ? bswap_32(val) : (val); break; \
|
||||
case 2: __ret = __NEED_BSWAP ? bswap_16(val) : (val); break; \
|
||||
default: \
|
||||
BUILD_BUG(); break; \
|
||||
} \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#endif /* _OBJTOOL_ENDIANNESS_H */
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#include <linux/list.h>
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "elf.h"
|
||||
#include <objtool/elf.h>
|
||||
|
||||
#define __weak __attribute__((weak))
|
||||
|
||||
|
|
@ -26,7 +26,6 @@ struct objtool_file *objtool_open_read(const char *_objname);
|
|||
|
||||
int check(struct objtool_file *file);
|
||||
int orc_dump(const char *objname);
|
||||
int create_orc(struct objtool_file *file);
|
||||
int create_orc_sections(struct objtool_file *file);
|
||||
int orc_create(struct objtool_file *file);
|
||||
|
||||
#endif /* _OBJTOOL_H */
|
||||
|
|
@ -7,8 +7,8 @@
|
|||
#define _SPECIAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "check.h"
|
||||
#include "elf.h"
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/elf.h>
|
||||
|
||||
#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
|
||||
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "elf.h"
|
||||
#include <objtool/elf.h>
|
||||
|
||||
extern const char *objname;
|
||||
|
||||
|
|
@ -21,9 +21,9 @@
|
|||
#include <subcmd/pager.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "builtin.h"
|
||||
#include "objtool.h"
|
||||
#include "warn.h"
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/warn.h>
|
||||
|
||||
struct cmd_struct {
|
||||
const char *name;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@
|
|||
#include <unistd.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <asm/orc_types.h>
|
||||
#include "objtool.h"
|
||||
#include "warn.h"
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
static const char *reg_name(unsigned int reg)
|
||||
{
|
||||
|
|
@ -54,7 +55,7 @@ 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);
|
||||
printf("(sp)%+d", offset);
|
||||
else if (reg == ORC_REG_UNDEFINED)
|
||||
printf("(und)");
|
||||
else
|
||||
|
|
@ -197,11 +198,11 @@ int orc_dump(const char *_objname)
|
|||
|
||||
printf(" sp:");
|
||||
|
||||
print_reg(orc[i].sp_reg, orc[i].sp_offset);
|
||||
print_reg(orc[i].sp_reg, bswap_if_needed(orc[i].sp_offset));
|
||||
|
||||
printf(" bp:");
|
||||
|
||||
print_reg(orc[i].bp_reg, orc[i].bp_offset);
|
||||
print_reg(orc[i].bp_reg, bswap_if_needed(orc[i].bp_offset));
|
||||
|
||||
printf(" type:%s end:%d\n",
|
||||
orc_type_name(orc[i].type), orc[i].end);
|
||||
|
|
|
|||
|
|
@ -9,93 +9,91 @@
|
|||
#include <linux/objtool.h>
|
||||
#include <asm/orc_types.h>
|
||||
|
||||
#include "check.h"
|
||||
#include "warn.h"
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
int create_orc(struct objtool_file *file)
|
||||
static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi)
|
||||
{
|
||||
struct instruction *insn;
|
||||
struct instruction *insn = container_of(cfi, struct instruction, cfi);
|
||||
struct cfi_reg *bp = &cfi->regs[CFI_BP];
|
||||
|
||||
for_each_insn(file, insn) {
|
||||
struct orc_entry *orc = &insn->orc;
|
||||
struct cfi_reg *cfa = &insn->cfi.cfa;
|
||||
struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
|
||||
memset(orc, 0, sizeof(*orc));
|
||||
|
||||
if (!insn->sec->text)
|
||||
continue;
|
||||
orc->end = cfi->end;
|
||||
|
||||
orc->end = insn->cfi.end;
|
||||
|
||||
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->cfi.type;
|
||||
if (cfi->cfa.base == CFI_UNDEFINED) {
|
||||
orc->sp_reg = ORC_REG_UNDEFINED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cfi->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, cfi->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 = cfi->cfa.offset;
|
||||
orc->bp_offset = bp->offset;
|
||||
orc->type = cfi->type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec,
|
||||
unsigned int idx, struct section *insn_sec,
|
||||
unsigned long insn_off, struct orc_entry *o)
|
||||
static int write_orc_entry(struct elf *elf, struct section *orc_sec,
|
||||
struct section *ip_rsec, unsigned int idx,
|
||||
struct section *insn_sec, unsigned long insn_off,
|
||||
struct orc_entry *o)
|
||||
{
|
||||
struct orc_entry *orc;
|
||||
struct reloc *reloc;
|
||||
|
||||
/* populate ORC data */
|
||||
orc = (struct orc_entry *)u_sec->data->d_buf + idx;
|
||||
orc = (struct orc_entry *)orc_sec->data->d_buf + idx;
|
||||
memcpy(orc, o, sizeof(*orc));
|
||||
orc->sp_offset = bswap_if_needed(orc->sp_offset);
|
||||
orc->bp_offset = bswap_if_needed(orc->bp_offset);
|
||||
|
||||
/* populate reloc for ip */
|
||||
reloc = malloc(sizeof(*reloc));
|
||||
|
|
@ -114,102 +112,149 @@ static int create_orc_entry(struct elf *elf, struct section *u_sec, struct secti
|
|||
|
||||
reloc->type = R_X86_64_PC32;
|
||||
reloc->offset = idx * sizeof(int);
|
||||
reloc->sec = ip_relocsec;
|
||||
reloc->sec = ip_rsec;
|
||||
|
||||
elf_add_reloc(elf, reloc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int create_orc_sections(struct objtool_file *file)
|
||||
{
|
||||
struct instruction *insn, *prev_insn;
|
||||
struct section *sec, *u_sec, *ip_relocsec;
|
||||
unsigned int idx;
|
||||
struct orc_list_entry {
|
||||
struct list_head list;
|
||||
struct orc_entry orc;
|
||||
struct section *insn_sec;
|
||||
unsigned long insn_off;
|
||||
};
|
||||
|
||||
struct orc_entry empty = {
|
||||
.sp_reg = ORC_REG_UNDEFINED,
|
||||
static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc,
|
||||
struct section *sec, unsigned long offset)
|
||||
{
|
||||
struct orc_list_entry *entry = malloc(sizeof(*entry));
|
||||
|
||||
if (!entry) {
|
||||
WARN("malloc failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry->orc = *orc;
|
||||
entry->insn_sec = sec;
|
||||
entry->insn_off = offset;
|
||||
|
||||
list_add_tail(&entry->list, orc_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long alt_group_len(struct alt_group *alt_group)
|
||||
{
|
||||
return alt_group->last_insn->offset +
|
||||
alt_group->last_insn->len -
|
||||
alt_group->first_insn->offset;
|
||||
}
|
||||
|
||||
int orc_create(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec, *ip_rsec, *orc_sec;
|
||||
unsigned int nr = 0, idx = 0;
|
||||
struct orc_list_entry *entry;
|
||||
struct list_head orc_list;
|
||||
|
||||
struct orc_entry null = {
|
||||
.sp_reg = ORC_REG_UNDEFINED,
|
||||
.bp_reg = ORC_REG_UNDEFINED,
|
||||
.type = UNWIND_HINT_TYPE_CALL,
|
||||
};
|
||||
|
||||
/* Build a deduplicated list of ORC entries: */
|
||||
INIT_LIST_HEAD(&orc_list);
|
||||
for_each_sec(file, sec) {
|
||||
struct orc_entry orc, prev_orc = {0};
|
||||
struct instruction *insn;
|
||||
bool empty = true;
|
||||
|
||||
if (!sec->text)
|
||||
continue;
|
||||
|
||||
sec_for_each_insn(file, sec, insn) {
|
||||
struct alt_group *alt_group = insn->alt_group;
|
||||
int i;
|
||||
|
||||
if (!alt_group) {
|
||||
if (init_orc_entry(&orc, &insn->cfi))
|
||||
return -1;
|
||||
if (!memcmp(&prev_orc, &orc, sizeof(orc)))
|
||||
continue;
|
||||
if (orc_list_add(&orc_list, &orc, sec,
|
||||
insn->offset))
|
||||
return -1;
|
||||
nr++;
|
||||
prev_orc = orc;
|
||||
empty = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Alternatives can have different stack layout
|
||||
* possibilities (but they shouldn't conflict).
|
||||
* Instead of traversing the instructions, use the
|
||||
* alt_group's flattened byte-offset-addressed CFI
|
||||
* array.
|
||||
*/
|
||||
for (i = 0; i < alt_group_len(alt_group); i++) {
|
||||
struct cfi_state *cfi = alt_group->cfi[i];
|
||||
if (!cfi)
|
||||
continue;
|
||||
if (init_orc_entry(&orc, cfi))
|
||||
return -1;
|
||||
if (!memcmp(&prev_orc, &orc, sizeof(orc)))
|
||||
continue;
|
||||
if (orc_list_add(&orc_list, &orc, insn->sec,
|
||||
insn->offset + i))
|
||||
return -1;
|
||||
nr++;
|
||||
prev_orc = orc;
|
||||
empty = false;
|
||||
}
|
||||
|
||||
/* Skip to the end of the alt_group */
|
||||
insn = alt_group->last_insn;
|
||||
}
|
||||
|
||||
/* Add a section terminator */
|
||||
if (!empty) {
|
||||
orc_list_add(&orc_list, &null, sec, sec->len);
|
||||
nr++;
|
||||
}
|
||||
}
|
||||
if (!nr)
|
||||
return 0;
|
||||
|
||||
/* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */
|
||||
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)
|
||||
orc_sec = elf_create_section(file->elf, ".orc_unwind", 0,
|
||||
sizeof(struct orc_entry), nr);
|
||||
if (!orc_sec)
|
||||
return -1;
|
||||
|
||||
|
||||
/* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
|
||||
sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), idx);
|
||||
sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr);
|
||||
if (!sec)
|
||||
return -1;
|
||||
|
||||
ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
|
||||
if (!ip_relocsec)
|
||||
ip_rsec = elf_create_reloc_section(file->elf, sec, SHT_RELA);
|
||||
if (!ip_rsec)
|
||||
return -1;
|
||||
|
||||
/* create .orc_unwind section */
|
||||
u_sec = elf_create_section(file->elf, ".orc_unwind", 0,
|
||||
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(file->elf, u_sec, ip_relocsec, idx,
|
||||
insn->sec, insn->offset,
|
||||
&insn->orc))
|
||||
return -1;
|
||||
|
||||
idx++;
|
||||
}
|
||||
prev_insn = insn;
|
||||
}
|
||||
|
||||
/* section terminator */
|
||||
if (prev_insn) {
|
||||
if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx,
|
||||
prev_insn->sec,
|
||||
prev_insn->offset + prev_insn->len,
|
||||
&empty))
|
||||
return -1;
|
||||
|
||||
idx++;
|
||||
}
|
||||
/* Write ORC entries to sections: */
|
||||
list_for_each_entry(entry, &orc_list, list) {
|
||||
if (write_orc_entry(file->elf, orc_sec, ip_rsec, idx++,
|
||||
entry->insn_sec, entry->insn_off,
|
||||
&entry->orc))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elf_rebuild_reloc_section(file->elf, ip_relocsec))
|
||||
if (elf_rebuild_reloc_section(file->elf, ip_rsec))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "builtin.h"
|
||||
#include "special.h"
|
||||
#include "warn.h"
|
||||
#include "arch_special.h"
|
||||
#include <arch/special.h>
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/special.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
struct special_entry {
|
||||
const char *sec;
|
||||
|
|
@ -77,8 +78,9 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry,
|
|||
if (entry->feature) {
|
||||
unsigned short feature;
|
||||
|
||||
feature = *(unsigned short *)(sec->data->d_buf + offset +
|
||||
entry->feature);
|
||||
feature = bswap_if_needed(*(unsigned short *)(sec->data->d_buf +
|
||||
offset +
|
||||
entry->feature));
|
||||
arch_handle_alternative(feature, alt);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include "objtool.h"
|
||||
#include <objtool/objtool.h>
|
||||
|
||||
#define UNSUPPORTED(name) \
|
||||
({ \
|
||||
|
|
@ -25,12 +25,7 @@ int __weak orc_dump(const char *_objname)
|
|||
UNSUPPORTED("orc");
|
||||
}
|
||||
|
||||
int __weak create_orc(struct objtool_file *file)
|
||||
{
|
||||
UNSUPPORTED("orc");
|
||||
}
|
||||
|
||||
int __weak create_orc_sections(struct objtool_file *file)
|
||||
int __weak orc_create(struct objtool_file *file)
|
||||
{
|
||||
UNSUPPORTED("orc");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue