Merge tag 'loongarch-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
Pull LoongArch updates from Huacai Chen:
- Add objtool support for LoongArch
- Add ORC stack unwinder support for LoongArch
- Add kernel livepatching support for LoongArch
- Select ARCH_HAS_CURRENT_STACK_POINTER in Kconfig
- Select HAVE_ARCH_USERFAULTFD_MINOR in Kconfig
- Some bug fixes and other small changes
* tag 'loongarch-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson:
LoongArch/crypto: Clean up useless assignment operations
LoongArch: Define the __io_aw() hook as mmiowb()
LoongArch: Remove superfluous flush_dcache_page() definition
LoongArch: Move {dmw,tlb}_virt_to_page() definition to page.h
LoongArch: Change __my_cpu_offset definition to avoid mis-optimization
LoongArch: Select HAVE_ARCH_USERFAULTFD_MINOR in Kconfig
LoongArch: Select ARCH_HAS_CURRENT_STACK_POINTER in Kconfig
LoongArch: Add kernel livepatching support
LoongArch: Add ORC stack unwinder support
objtool: Check local label in read_unwind_hints()
objtool: Check local label in add_dead_ends()
objtool/LoongArch: Enable orc to be built
objtool/x86: Separate arch-specific and generic parts
objtool/LoongArch: Implement instruction decoder
objtool/LoongArch: Enable objtool to be built
This commit is contained in:
@@ -57,6 +57,10 @@ ifeq ($(SRCARCH),x86)
|
||||
BUILD_ORC := y
|
||||
endif
|
||||
|
||||
ifeq ($(SRCARCH),loongarch)
|
||||
BUILD_ORC := y
|
||||
endif
|
||||
|
||||
export BUILD_ORC
|
||||
export srctree OUTPUT CFLAGS SRCARCH AWK
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
objtool-y += decode.o
|
||||
objtool-y += special.o
|
||||
objtool-y += orc.o
|
||||
@@ -0,0 +1,356 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <string.h>
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <asm/inst.h>
|
||||
#include <asm/orc_types.h>
|
||||
#include <linux/objtool_types.h>
|
||||
|
||||
#ifndef EM_LOONGARCH
|
||||
#define EM_LOONGARCH 258
|
||||
#endif
|
||||
|
||||
int arch_ftrace_match(char *name)
|
||||
{
|
||||
return !strcmp(name, "_mcount");
|
||||
}
|
||||
|
||||
unsigned long arch_jump_destination(struct instruction *insn)
|
||||
{
|
||||
return insn->offset + (insn->immediate << 2);
|
||||
}
|
||||
|
||||
unsigned long arch_dest_reloc_offset(int addend)
|
||||
{
|
||||
return addend;
|
||||
}
|
||||
|
||||
bool arch_pc_relative_reloc(struct reloc *reloc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool arch_callee_saved_reg(unsigned char reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CFI_RA:
|
||||
case CFI_FP:
|
||||
case CFI_S0 ... CFI_S8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int arch_decode_hint_reg(u8 sp_reg, int *base)
|
||||
{
|
||||
switch (sp_reg) {
|
||||
case ORC_REG_UNDEFINED:
|
||||
*base = CFI_UNDEFINED;
|
||||
break;
|
||||
case ORC_REG_SP:
|
||||
*base = CFI_SP;
|
||||
break;
|
||||
case ORC_REG_FP:
|
||||
*base = CFI_FP;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_loongarch(const struct elf *elf)
|
||||
{
|
||||
if (elf->ehdr.e_machine == EM_LOONGARCH)
|
||||
return true;
|
||||
|
||||
WARN("unexpected ELF machine type %d", elf->ehdr.e_machine);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define ADD_OP(op) \
|
||||
if (!(op = calloc(1, sizeof(*op)))) \
|
||||
return -1; \
|
||||
else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
|
||||
|
||||
static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst,
|
||||
struct instruction *insn)
|
||||
{
|
||||
switch (inst.reg0i26_format.opcode) {
|
||||
case b_op:
|
||||
insn->type = INSN_JUMP_UNCONDITIONAL;
|
||||
insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
|
||||
inst.reg0i26_format.immediate_l, 25);
|
||||
break;
|
||||
case bl_op:
|
||||
insn->type = INSN_CALL;
|
||||
insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
|
||||
inst.reg0i26_format.immediate_l, 25);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst,
|
||||
struct instruction *insn)
|
||||
{
|
||||
switch (inst.reg1i21_format.opcode) {
|
||||
case beqz_op:
|
||||
case bnez_op:
|
||||
case bceqz_op:
|
||||
insn->type = INSN_JUMP_CONDITIONAL;
|
||||
insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 |
|
||||
inst.reg1i21_format.immediate_l, 20);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst,
|
||||
struct instruction *insn,
|
||||
struct stack_op **ops_list,
|
||||
struct stack_op *op)
|
||||
{
|
||||
switch (inst.reg2i12_format.opcode) {
|
||||
case addid_op:
|
||||
if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) {
|
||||
/* addi.d sp,sp,si12 or addi.d fp,sp,si12 */
|
||||
insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
|
||||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_ADD;
|
||||
op->src.reg = inst.reg2i12_format.rj;
|
||||
op->src.offset = insn->immediate;
|
||||
op->dest.type = OP_DEST_REG;
|
||||
op->dest.reg = inst.reg2i12_format.rd;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ldd_op:
|
||||
if (inst.reg2i12_format.rj == CFI_SP) {
|
||||
/* ld.d rd,sp,si12 */
|
||||
insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
|
||||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_REG_INDIRECT;
|
||||
op->src.reg = CFI_SP;
|
||||
op->src.offset = insn->immediate;
|
||||
op->dest.type = OP_DEST_REG;
|
||||
op->dest.reg = inst.reg2i12_format.rd;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case std_op:
|
||||
if (inst.reg2i12_format.rj == CFI_SP) {
|
||||
/* st.d rd,sp,si12 */
|
||||
insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
|
||||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_REG;
|
||||
op->src.reg = inst.reg2i12_format.rd;
|
||||
op->dest.type = OP_DEST_REG_INDIRECT;
|
||||
op->dest.reg = CFI_SP;
|
||||
op->dest.offset = insn->immediate;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case andi_op:
|
||||
if (inst.reg2i12_format.rd == 0 &&
|
||||
inst.reg2i12_format.rj == 0 &&
|
||||
inst.reg2i12_format.immediate == 0)
|
||||
/* andi r0,r0,0 */
|
||||
insn->type = INSN_NOP;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst,
|
||||
struct instruction *insn,
|
||||
struct stack_op **ops_list,
|
||||
struct stack_op *op)
|
||||
{
|
||||
switch (inst.reg2i14_format.opcode) {
|
||||
case ldptrd_op:
|
||||
if (inst.reg2i14_format.rj == CFI_SP) {
|
||||
/* ldptr.d rd,sp,si14 */
|
||||
insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
|
||||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_REG_INDIRECT;
|
||||
op->src.reg = CFI_SP;
|
||||
op->src.offset = insn->immediate;
|
||||
op->dest.type = OP_DEST_REG;
|
||||
op->dest.reg = inst.reg2i14_format.rd;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case stptrd_op:
|
||||
if (inst.reg2i14_format.rj == CFI_SP) {
|
||||
/* stptr.d ra,sp,0 */
|
||||
if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA &&
|
||||
inst.reg2i14_format.immediate == 0)
|
||||
break;
|
||||
|
||||
/* stptr.d rd,sp,si14 */
|
||||
insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
|
||||
ADD_OP(op) {
|
||||
op->src.type = OP_SRC_REG;
|
||||
op->src.reg = inst.reg2i14_format.rd;
|
||||
op->dest.type = OP_DEST_REG_INDIRECT;
|
||||
op->dest.reg = CFI_SP;
|
||||
op->dest.offset = insn->immediate;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst,
|
||||
struct instruction *insn)
|
||||
{
|
||||
switch (inst.reg2i16_format.opcode) {
|
||||
case jirl_op:
|
||||
if (inst.reg2i16_format.rd == 0 &&
|
||||
inst.reg2i16_format.rj == CFI_RA &&
|
||||
inst.reg2i16_format.immediate == 0) {
|
||||
/* jirl r0,ra,0 */
|
||||
insn->type = INSN_RETURN;
|
||||
} else if (inst.reg2i16_format.rd == CFI_RA) {
|
||||
/* jirl ra,rj,offs16 */
|
||||
insn->type = INSN_CALL_DYNAMIC;
|
||||
} else if (inst.reg2i16_format.rd == CFI_A0 &&
|
||||
inst.reg2i16_format.immediate == 0) {
|
||||
/*
|
||||
* jirl a0,t0,0
|
||||
* this is a special case in loongarch_suspend_enter,
|
||||
* just treat it as a call instruction.
|
||||
*/
|
||||
insn->type = INSN_CALL_DYNAMIC;
|
||||
} else if (inst.reg2i16_format.rd == 0 &&
|
||||
inst.reg2i16_format.immediate == 0) {
|
||||
/* jirl r0,rj,0 */
|
||||
insn->type = INSN_JUMP_DYNAMIC;
|
||||
} else if (inst.reg2i16_format.rd == 0 &&
|
||||
inst.reg2i16_format.immediate != 0) {
|
||||
/*
|
||||
* jirl r0,t0,12
|
||||
* this is a rare case in JUMP_VIRT_ADDR,
|
||||
* just ignore it due to it is harmless for tracing.
|
||||
*/
|
||||
break;
|
||||
} else {
|
||||
/* jirl rd,rj,offs16 */
|
||||
insn->type = INSN_JUMP_UNCONDITIONAL;
|
||||
insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
|
||||
}
|
||||
break;
|
||||
case beq_op:
|
||||
case bne_op:
|
||||
case blt_op:
|
||||
case bge_op:
|
||||
case bltu_op:
|
||||
case bgeu_op:
|
||||
insn->type = INSN_JUMP_CONDITIONAL;
|
||||
insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
|
||||
unsigned long offset, unsigned int maxlen,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct stack_op **ops_list = &insn->stack_ops;
|
||||
const struct elf *elf = file->elf;
|
||||
struct stack_op *op = NULL;
|
||||
union loongarch_instruction inst;
|
||||
|
||||
if (!is_loongarch(elf))
|
||||
return -1;
|
||||
|
||||
if (maxlen < LOONGARCH_INSN_SIZE)
|
||||
return 0;
|
||||
|
||||
insn->len = LOONGARCH_INSN_SIZE;
|
||||
insn->type = INSN_OTHER;
|
||||
insn->immediate = 0;
|
||||
|
||||
inst = *(union loongarch_instruction *)(sec->data->d_buf + offset);
|
||||
|
||||
if (decode_insn_reg0i26_fomat(inst, insn))
|
||||
return 0;
|
||||
if (decode_insn_reg1i21_fomat(inst, insn))
|
||||
return 0;
|
||||
if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op))
|
||||
return 0;
|
||||
if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op))
|
||||
return 0;
|
||||
if (decode_insn_reg2i16_fomat(inst, insn))
|
||||
return 0;
|
||||
|
||||
if (inst.word == 0)
|
||||
insn->type = INSN_NOP;
|
||||
else if (inst.reg0i15_format.opcode == break_op) {
|
||||
/* break */
|
||||
insn->type = INSN_BUG;
|
||||
} else if (inst.reg2_format.opcode == ertn_op) {
|
||||
/* ertn */
|
||||
insn->type = INSN_RETURN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *arch_nop_insn(int len)
|
||||
{
|
||||
static u32 nop;
|
||||
|
||||
if (len != LOONGARCH_INSN_SIZE)
|
||||
WARN("invalid NOP size: %d\n", len);
|
||||
|
||||
nop = LOONGARCH_INSN_NOP;
|
||||
|
||||
return (const char *)&nop;
|
||||
}
|
||||
|
||||
const char *arch_ret_insn(int len)
|
||||
{
|
||||
static u32 ret;
|
||||
|
||||
if (len != LOONGARCH_INSN_SIZE)
|
||||
WARN("invalid RET size: %d\n", len);
|
||||
|
||||
emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0);
|
||||
|
||||
return (const char *)&ret;
|
||||
}
|
||||
|
||||
void arch_initial_func_cfi_state(struct cfi_init_state *state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CFI_NUM_REGS; i++) {
|
||||
state->regs[i].base = CFI_UNDEFINED;
|
||||
state->regs[i].offset = 0;
|
||||
}
|
||||
|
||||
/* initial CFA (call frame address) */
|
||||
state->cfa.base = CFI_SP;
|
||||
state->cfa.offset = 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _OBJTOOL_ARCH_CFI_REGS_H
|
||||
#define _OBJTOOL_ARCH_CFI_REGS_H
|
||||
|
||||
#define CFI_RA 1
|
||||
#define CFI_SP 3
|
||||
#define CFI_A0 4
|
||||
#define CFI_FP 22
|
||||
#define CFI_S0 23
|
||||
#define CFI_S1 24
|
||||
#define CFI_S2 25
|
||||
#define CFI_S3 26
|
||||
#define CFI_S4 27
|
||||
#define CFI_S5 28
|
||||
#define CFI_S6 29
|
||||
#define CFI_S7 30
|
||||
#define CFI_S8 31
|
||||
#define CFI_NUM_REGS 32
|
||||
|
||||
#define CFI_BP CFI_FP
|
||||
|
||||
#endif /* _OBJTOOL_ARCH_CFI_REGS_H */
|
||||
@@ -0,0 +1,30 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _OBJTOOL_ARCH_ELF_H
|
||||
#define _OBJTOOL_ARCH_ELF_H
|
||||
|
||||
/*
|
||||
* See the following link for more info about ELF Relocation types:
|
||||
* https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_relocations
|
||||
*/
|
||||
#ifndef R_LARCH_NONE
|
||||
#define R_LARCH_NONE 0
|
||||
#endif
|
||||
#ifndef R_LARCH_32
|
||||
#define R_LARCH_32 1
|
||||
#endif
|
||||
#ifndef R_LARCH_64
|
||||
#define R_LARCH_64 2
|
||||
#endif
|
||||
#ifndef R_LARCH_32_PCREL
|
||||
#define R_LARCH_32_PCREL 99
|
||||
#endif
|
||||
|
||||
#define R_NONE R_LARCH_NONE
|
||||
#define R_ABS32 R_LARCH_32
|
||||
#define R_ABS64 R_LARCH_64
|
||||
#define R_DATA32 R_LARCH_32_PCREL
|
||||
#define R_DATA64 R_LARCH_32_PCREL
|
||||
#define R_TEXT32 R_LARCH_32_PCREL
|
||||
#define R_TEXT64 R_LARCH_32_PCREL
|
||||
|
||||
#endif /* _OBJTOOL_ARCH_ELF_H */
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _OBJTOOL_ARCH_SPECIAL_H
|
||||
#define _OBJTOOL_ARCH_SPECIAL_H
|
||||
|
||||
/*
|
||||
* See more info about struct exception_table_entry
|
||||
* in arch/loongarch/include/asm/extable.h
|
||||
*/
|
||||
#define EX_ENTRY_SIZE 12
|
||||
#define EX_ORIG_OFFSET 0
|
||||
#define EX_NEW_OFFSET 4
|
||||
|
||||
/*
|
||||
* See more info about struct jump_entry
|
||||
* in include/linux/jump_label.h
|
||||
*/
|
||||
#define JUMP_ENTRY_SIZE 16
|
||||
#define JUMP_ORIG_OFFSET 0
|
||||
#define JUMP_NEW_OFFSET 4
|
||||
#define JUMP_KEY_OFFSET 8
|
||||
|
||||
/*
|
||||
* See more info about struct alt_instr
|
||||
* in arch/loongarch/include/asm/alternative.h
|
||||
*/
|
||||
#define ALT_ENTRY_SIZE 12
|
||||
#define ALT_ORIG_OFFSET 0
|
||||
#define ALT_NEW_OFFSET 4
|
||||
#define ALT_FEATURE_OFFSET 8
|
||||
#define ALT_ORIG_LEN_OFFSET 10
|
||||
#define ALT_NEW_LEN_OFFSET 11
|
||||
|
||||
#endif /* _OBJTOOL_ARCH_SPECIAL_H */
|
||||
@@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <linux/objtool_types.h>
|
||||
#include <asm/orc_types.h>
|
||||
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/orc.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn)
|
||||
{
|
||||
struct cfi_reg *fp = &cfi->regs[CFI_FP];
|
||||
struct cfi_reg *ra = &cfi->regs[CFI_RA];
|
||||
|
||||
memset(orc, 0, sizeof(*orc));
|
||||
|
||||
if (!cfi) {
|
||||
/*
|
||||
* This is usually either unreachable nops/traps (which don't
|
||||
* trigger unreachable instruction warnings), or
|
||||
* STACK_FRAME_NON_STANDARD functions.
|
||||
*/
|
||||
orc->type = ORC_TYPE_UNDEFINED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cfi->type) {
|
||||
case UNWIND_HINT_TYPE_UNDEFINED:
|
||||
orc->type = ORC_TYPE_UNDEFINED;
|
||||
return 0;
|
||||
case UNWIND_HINT_TYPE_END_OF_STACK:
|
||||
orc->type = ORC_TYPE_END_OF_STACK;
|
||||
return 0;
|
||||
case UNWIND_HINT_TYPE_CALL:
|
||||
orc->type = ORC_TYPE_CALL;
|
||||
break;
|
||||
case UNWIND_HINT_TYPE_REGS:
|
||||
orc->type = ORC_TYPE_REGS;
|
||||
break;
|
||||
case UNWIND_HINT_TYPE_REGS_PARTIAL:
|
||||
orc->type = ORC_TYPE_REGS_PARTIAL;
|
||||
break;
|
||||
default:
|
||||
WARN_INSN(insn, "unknown unwind hint type %d", cfi->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
orc->signal = cfi->signal;
|
||||
|
||||
switch (cfi->cfa.base) {
|
||||
case CFI_SP:
|
||||
orc->sp_reg = ORC_REG_SP;
|
||||
break;
|
||||
case CFI_FP:
|
||||
orc->sp_reg = ORC_REG_FP;
|
||||
break;
|
||||
default:
|
||||
WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (fp->base) {
|
||||
case CFI_UNDEFINED:
|
||||
orc->fp_reg = ORC_REG_UNDEFINED;
|
||||
orc->fp_offset = 0;
|
||||
break;
|
||||
case CFI_CFA:
|
||||
orc->fp_reg = ORC_REG_PREV_SP;
|
||||
orc->fp_offset = fp->offset;
|
||||
break;
|
||||
case CFI_FP:
|
||||
orc->fp_reg = ORC_REG_FP;
|
||||
break;
|
||||
default:
|
||||
WARN_INSN(insn, "unknown FP base reg %d", fp->base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (ra->base) {
|
||||
case CFI_UNDEFINED:
|
||||
orc->ra_reg = ORC_REG_UNDEFINED;
|
||||
orc->ra_offset = 0;
|
||||
break;
|
||||
case CFI_CFA:
|
||||
orc->ra_reg = ORC_REG_PREV_SP;
|
||||
orc->ra_offset = ra->offset;
|
||||
break;
|
||||
case CFI_FP:
|
||||
orc->ra_reg = ORC_REG_FP;
|
||||
break;
|
||||
default:
|
||||
WARN_INSN(insn, "unknown RA base reg %d", ra->base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
orc->sp_offset = cfi->cfa.offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_orc_entry(struct elf *elf, struct section *orc_sec,
|
||||
struct section *ip_sec, unsigned int idx,
|
||||
struct section *insn_sec, unsigned long insn_off,
|
||||
struct orc_entry *o)
|
||||
{
|
||||
struct orc_entry *orc;
|
||||
|
||||
/* populate ORC data */
|
||||
orc = (struct orc_entry *)orc_sec->data->d_buf + idx;
|
||||
memcpy(orc, o, sizeof(*orc));
|
||||
|
||||
/* populate reloc for ip */
|
||||
if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx,
|
||||
insn_sec, insn_off))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *reg_name(unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case ORC_REG_SP:
|
||||
return "sp";
|
||||
case ORC_REG_FP:
|
||||
return "fp";
|
||||
case ORC_REG_PREV_SP:
|
||||
return "prevsp";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *orc_type_name(unsigned int type)
|
||||
{
|
||||
switch (type) {
|
||||
case UNWIND_HINT_TYPE_CALL:
|
||||
return "call";
|
||||
case UNWIND_HINT_TYPE_REGS:
|
||||
return "regs";
|
||||
case UNWIND_HINT_TYPE_REGS_PARTIAL:
|
||||
return "regs (partial)";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static void print_reg(unsigned int reg, int offset)
|
||||
{
|
||||
if (reg == ORC_REG_UNDEFINED)
|
||||
printf(" (und) ");
|
||||
else
|
||||
printf("%s + %3d", reg_name(reg), offset);
|
||||
|
||||
}
|
||||
|
||||
void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i)
|
||||
{
|
||||
printf("type:%s", orc_type_name(orc[i].type));
|
||||
|
||||
printf(" sp:");
|
||||
print_reg(orc[i].sp_reg, orc[i].sp_offset);
|
||||
|
||||
printf(" fp:");
|
||||
print_reg(orc[i].fp_reg, orc[i].fp_offset);
|
||||
|
||||
printf(" ra:");
|
||||
print_reg(orc[i].ra_reg, orc[i].ra_offset);
|
||||
|
||||
printf(" signal:%d\n", orc[i].signal);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <objtool/special.h>
|
||||
|
||||
bool arch_support_alt_relocation(struct special_alt *special_alt,
|
||||
struct instruction *insn,
|
||||
struct reloc *reloc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
struct reloc *arch_find_switch_table(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
objtool-y += special.o
|
||||
objtool-y += decode.o
|
||||
objtool-y += orc.o
|
||||
|
||||
inat_tables_script = ../arch/x86/tools/gen-insn-attr-x86.awk
|
||||
inat_tables_maps = ../arch/x86/lib/x86-opcode-map.txt
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <linux/objtool_types.h>
|
||||
#include <asm/orc_types.h>
|
||||
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/orc.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn)
|
||||
{
|
||||
struct cfi_reg *bp = &cfi->regs[CFI_BP];
|
||||
|
||||
memset(orc, 0, sizeof(*orc));
|
||||
|
||||
if (!cfi) {
|
||||
/*
|
||||
* This is usually either unreachable nops/traps (which don't
|
||||
* trigger unreachable instruction warnings), or
|
||||
* STACK_FRAME_NON_STANDARD functions.
|
||||
*/
|
||||
orc->type = ORC_TYPE_UNDEFINED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cfi->type) {
|
||||
case UNWIND_HINT_TYPE_UNDEFINED:
|
||||
orc->type = ORC_TYPE_UNDEFINED;
|
||||
return 0;
|
||||
case UNWIND_HINT_TYPE_END_OF_STACK:
|
||||
orc->type = ORC_TYPE_END_OF_STACK;
|
||||
return 0;
|
||||
case UNWIND_HINT_TYPE_CALL:
|
||||
orc->type = ORC_TYPE_CALL;
|
||||
break;
|
||||
case UNWIND_HINT_TYPE_REGS:
|
||||
orc->type = ORC_TYPE_REGS;
|
||||
break;
|
||||
case UNWIND_HINT_TYPE_REGS_PARTIAL:
|
||||
orc->type = ORC_TYPE_REGS_PARTIAL;
|
||||
break;
|
||||
default:
|
||||
WARN_INSN(insn, "unknown unwind hint type %d", cfi->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
orc->signal = cfi->signal;
|
||||
|
||||
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_INSN(insn, "unknown CFA base reg %d", 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_INSN(insn, "unknown BP base reg %d", bp->base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
orc->sp_offset = cfi->cfa.offset;
|
||||
orc->bp_offset = bp->offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_orc_entry(struct elf *elf, struct section *orc_sec,
|
||||
struct section *ip_sec, unsigned int idx,
|
||||
struct section *insn_sec, unsigned long insn_off,
|
||||
struct orc_entry *o)
|
||||
{
|
||||
struct orc_entry *orc;
|
||||
|
||||
/* populate ORC data */
|
||||
orc = (struct orc_entry *)orc_sec->data->d_buf + idx;
|
||||
memcpy(orc, o, sizeof(*orc));
|
||||
orc->sp_offset = bswap_if_needed(elf, orc->sp_offset);
|
||||
orc->bp_offset = bswap_if_needed(elf, orc->bp_offset);
|
||||
|
||||
/* populate reloc for ip */
|
||||
if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx,
|
||||
insn_sec, insn_off))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_UNDEFINED:
|
||||
return "(und)";
|
||||
case ORC_TYPE_END_OF_STACK:
|
||||
return "end";
|
||||
case ORC_TYPE_CALL:
|
||||
return "call";
|
||||
case ORC_TYPE_REGS:
|
||||
return "regs";
|
||||
case ORC_TYPE_REGS_PARTIAL:
|
||||
return "regs (partial)";
|
||||
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);
|
||||
}
|
||||
|
||||
void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i)
|
||||
{
|
||||
printf("type:%s", orc_type_name(orc[i].type));
|
||||
|
||||
printf(" sp:");
|
||||
print_reg(orc[i].sp_reg, bswap_if_needed(dummy_elf, orc[i].sp_offset));
|
||||
|
||||
printf(" bp:");
|
||||
print_reg(orc[i].bp_reg, bswap_if_needed(dummy_elf, orc[i].bp_offset));
|
||||
|
||||
printf(" signal:%d\n", orc[i].signal);
|
||||
}
|
||||
+34
-18
@@ -20,6 +20,7 @@
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/static_call_types.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
struct alternative {
|
||||
struct alternative *next;
|
||||
@@ -584,7 +585,7 @@ static int add_dead_ends(struct objtool_file *file)
|
||||
struct section *rsec;
|
||||
struct reloc *reloc;
|
||||
struct instruction *insn;
|
||||
s64 addend;
|
||||
unsigned long offset;
|
||||
|
||||
/*
|
||||
* Check for manually annotated dead ends.
|
||||
@@ -594,27 +595,28 @@ static int add_dead_ends(struct objtool_file *file)
|
||||
goto reachable;
|
||||
|
||||
for_each_reloc(rsec, reloc) {
|
||||
|
||||
if (reloc->sym->type != STT_SECTION) {
|
||||
if (reloc->sym->type == STT_SECTION) {
|
||||
offset = reloc_addend(reloc);
|
||||
} else if (reloc->sym->local_label) {
|
||||
offset = reloc->sym->offset;
|
||||
} else {
|
||||
WARN("unexpected relocation symbol type in %s", rsec->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addend = reloc_addend(reloc);
|
||||
|
||||
insn = find_insn(file, reloc->sym->sec, addend);
|
||||
insn = find_insn(file, reloc->sym->sec, offset);
|
||||
if (insn)
|
||||
insn = prev_insn_same_sec(file, insn);
|
||||
else if (addend == reloc->sym->sec->sh.sh_size) {
|
||||
else if (offset == reloc->sym->sec->sh.sh_size) {
|
||||
insn = find_last_insn(file, reloc->sym->sec);
|
||||
if (!insn) {
|
||||
WARN("can't find unreachable insn at %s+0x%" PRIx64,
|
||||
reloc->sym->sec->name, addend);
|
||||
reloc->sym->sec->name, offset);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
WARN("can't find unreachable insn at %s+0x%" PRIx64,
|
||||
reloc->sym->sec->name, addend);
|
||||
reloc->sym->sec->name, offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -633,27 +635,28 @@ reachable:
|
||||
return 0;
|
||||
|
||||
for_each_reloc(rsec, reloc) {
|
||||
|
||||
if (reloc->sym->type != STT_SECTION) {
|
||||
if (reloc->sym->type == STT_SECTION) {
|
||||
offset = reloc_addend(reloc);
|
||||
} else if (reloc->sym->local_label) {
|
||||
offset = reloc->sym->offset;
|
||||
} else {
|
||||
WARN("unexpected relocation symbol type in %s", rsec->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addend = reloc_addend(reloc);
|
||||
|
||||
insn = find_insn(file, reloc->sym->sec, addend);
|
||||
insn = find_insn(file, reloc->sym->sec, offset);
|
||||
if (insn)
|
||||
insn = prev_insn_same_sec(file, insn);
|
||||
else if (addend == reloc->sym->sec->sh.sh_size) {
|
||||
else if (offset == reloc->sym->sec->sh.sh_size) {
|
||||
insn = find_last_insn(file, reloc->sym->sec);
|
||||
if (!insn) {
|
||||
WARN("can't find reachable insn at %s+0x%" PRIx64,
|
||||
reloc->sym->sec->name, addend);
|
||||
reloc->sym->sec->name, offset);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
WARN("can't find reachable insn at %s+0x%" PRIx64,
|
||||
reloc->sym->sec->name, addend);
|
||||
reloc->sym->sec->name, offset);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -2224,6 +2227,7 @@ static int read_unwind_hints(struct objtool_file *file)
|
||||
struct unwind_hint *hint;
|
||||
struct instruction *insn;
|
||||
struct reloc *reloc;
|
||||
unsigned long offset;
|
||||
int i;
|
||||
|
||||
sec = find_section_by_name(file->elf, ".discard.unwind_hints");
|
||||
@@ -2251,7 +2255,16 @@ static int read_unwind_hints(struct objtool_file *file)
|
||||
return -1;
|
||||
}
|
||||
|
||||
insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
|
||||
if (reloc->sym->type == STT_SECTION) {
|
||||
offset = reloc_addend(reloc);
|
||||
} else if (reloc->sym->local_label) {
|
||||
offset = reloc->sym->offset;
|
||||
} else {
|
||||
WARN("unexpected relocation symbol type in %s", sec->rsec->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
insn = find_insn(file, reloc->sym->sec, offset);
|
||||
if (!insn) {
|
||||
WARN("can't find insn for unwind_hints[%d]", i);
|
||||
return -1;
|
||||
@@ -2522,6 +2535,9 @@ static int classify_symbols(struct objtool_file *file)
|
||||
struct symbol *func;
|
||||
|
||||
for_each_sym(file, func) {
|
||||
if (func->type == STT_NOTYPE && strstarts(func->name, ".L"))
|
||||
func->local_label = true;
|
||||
|
||||
if (func->bind != STB_GLOBAL)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ struct symbol {
|
||||
u8 profiling_func : 1;
|
||||
u8 warned : 1;
|
||||
u8 embedded_insn : 1;
|
||||
u8 local_label : 1;
|
||||
struct list_head pv_target;
|
||||
struct reloc *relocs;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _OBJTOOL_ORC_H
|
||||
#define _OBJTOOL_ORC_H
|
||||
|
||||
#include <objtool/check.h>
|
||||
|
||||
int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruction *insn);
|
||||
void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i);
|
||||
int write_orc_entry(struct elf *elf, struct section *orc_sec,
|
||||
struct section *ip_sec, unsigned int idx,
|
||||
struct section *insn_sec, unsigned long insn_off,
|
||||
struct orc_entry *o);
|
||||
|
||||
#endif /* _OBJTOOL_ORC_H */
|
||||
@@ -6,65 +6,10 @@
|
||||
#include <unistd.h>
|
||||
#include <asm/orc_types.h>
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/orc.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.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_UNDEFINED:
|
||||
return "(und)";
|
||||
case ORC_TYPE_END_OF_STACK:
|
||||
return "end";
|
||||
case ORC_TYPE_CALL:
|
||||
return "call";
|
||||
case ORC_TYPE_REGS:
|
||||
return "regs";
|
||||
case ORC_TYPE_REGS_PARTIAL:
|
||||
return "regs (partial)";
|
||||
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;
|
||||
@@ -205,17 +150,7 @@ int orc_dump(const char *_objname)
|
||||
printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i]));
|
||||
}
|
||||
|
||||
printf("type:%s", orc_type_name(orc[i].type));
|
||||
|
||||
printf(" sp:");
|
||||
|
||||
print_reg(orc[i].sp_reg, bswap_if_needed(&dummy_elf, orc[i].sp_offset));
|
||||
|
||||
printf(" bp:");
|
||||
|
||||
print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset));
|
||||
|
||||
printf(" signal:%d\n", orc[i].signal);
|
||||
orc_print_dump(&dummy_elf, orc, i);
|
||||
}
|
||||
|
||||
elf_end(elf);
|
||||
|
||||
+1
-112
@@ -10,121 +10,10 @@
|
||||
#include <asm/orc_types.h>
|
||||
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/orc.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/endianness.h>
|
||||
|
||||
static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct cfi_reg *bp = &cfi->regs[CFI_BP];
|
||||
|
||||
memset(orc, 0, sizeof(*orc));
|
||||
|
||||
if (!cfi) {
|
||||
/*
|
||||
* This is usually either unreachable nops/traps (which don't
|
||||
* trigger unreachable instruction warnings), or
|
||||
* STACK_FRAME_NON_STANDARD functions.
|
||||
*/
|
||||
orc->type = ORC_TYPE_UNDEFINED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cfi->type) {
|
||||
case UNWIND_HINT_TYPE_UNDEFINED:
|
||||
orc->type = ORC_TYPE_UNDEFINED;
|
||||
return 0;
|
||||
case UNWIND_HINT_TYPE_END_OF_STACK:
|
||||
orc->type = ORC_TYPE_END_OF_STACK;
|
||||
return 0;
|
||||
case UNWIND_HINT_TYPE_CALL:
|
||||
orc->type = ORC_TYPE_CALL;
|
||||
break;
|
||||
case UNWIND_HINT_TYPE_REGS:
|
||||
orc->type = ORC_TYPE_REGS;
|
||||
break;
|
||||
case UNWIND_HINT_TYPE_REGS_PARTIAL:
|
||||
orc->type = ORC_TYPE_REGS_PARTIAL;
|
||||
break;
|
||||
default:
|
||||
WARN_INSN(insn, "unknown unwind hint type %d", cfi->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
orc->signal = cfi->signal;
|
||||
|
||||
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_INSN(insn, "unknown CFA base reg %d", 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_INSN(insn, "unknown BP base reg %d", bp->base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
orc->sp_offset = cfi->cfa.offset;
|
||||
orc->bp_offset = bp->offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_orc_entry(struct elf *elf, struct section *orc_sec,
|
||||
struct section *ip_sec, unsigned int idx,
|
||||
struct section *insn_sec, unsigned long insn_off,
|
||||
struct orc_entry *o)
|
||||
{
|
||||
struct orc_entry *orc;
|
||||
|
||||
/* populate ORC data */
|
||||
orc = (struct orc_entry *)orc_sec->data->d_buf + idx;
|
||||
memcpy(orc, o, sizeof(*orc));
|
||||
orc->sp_offset = bswap_if_needed(elf, orc->sp_offset);
|
||||
orc->bp_offset = bswap_if_needed(elf, orc->bp_offset);
|
||||
|
||||
/* populate reloc for ip */
|
||||
if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx,
|
||||
insn_sec, insn_off))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct orc_list_entry {
|
||||
struct list_head list;
|
||||
struct orc_entry orc;
|
||||
|
||||
Reference in New Issue
Block a user