Merge branch 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull core fixes from Thomas Gleixner: - A collection of objtool fixes which address recent fallout partially exposed by newer toolchains, clang, BPF and general code changes. - Force USER_DS for user stack traces [ Note: the "objtool fixes" are not all to objtool itself, but for kernel code that triggers objtool warnings. Things like missing function size annotations, or code that confuses the unwinder etc. - Linus] * 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (27 commits) objtool: Support conditional retpolines objtool: Convert insn type to enum objtool: Fix seg fault on bad switch table entry objtool: Support repeated uses of the same C jump table objtool: Refactor jump table code objtool: Refactor sibling call detection logic objtool: Do frame pointer check before dead end check objtool: Change dead_end_function() to return boolean objtool: Warn on zero-length functions objtool: Refactor function alias logic objtool: Track original function across branches objtool: Add mcsafe_handle_tail() to the uaccess safe list bpf: Disable GCC -fgcse optimization for ___bpf_prog_run() x86/uaccess: Remove redundant CLACs in getuser/putuser error paths x86/uaccess: Don't leak AC flag into fentry from mcsafe_handle_tail() x86/uaccess: Remove ELF function annotation from copy_user_handle_tail() x86/head/64: Annotate start_cpu0() as non-callable x86/entry: Fix thunk function ELF sizes x86/kvm: Don't call kvm_spurious_fault() from .fixup x86/kvm: Replace vmx_vmenter()'s call to kvm_spurious_fault() with UD2 ...
This commit is contained in:
commit
e6023adc5c
|
@ -12,9 +12,7 @@
|
||||||
|
|
||||||
/* rdi: arg1 ... normal C conventions. rax is saved/restored. */
|
/* rdi: arg1 ... normal C conventions. rax is saved/restored. */
|
||||||
.macro THUNK name, func, put_ret_addr_in_rdi=0
|
.macro THUNK name, func, put_ret_addr_in_rdi=0
|
||||||
.globl \name
|
ENTRY(\name)
|
||||||
.type \name, @function
|
|
||||||
\name:
|
|
||||||
pushq %rbp
|
pushq %rbp
|
||||||
movq %rsp, %rbp
|
movq %rsp, %rbp
|
||||||
|
|
||||||
|
@ -35,6 +33,7 @@
|
||||||
|
|
||||||
call \func
|
call \func
|
||||||
jmp .L_restore
|
jmp .L_restore
|
||||||
|
ENDPROC(\name)
|
||||||
_ASM_NOKPROBE(\name)
|
_ASM_NOKPROBE(\name)
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
|
|
@ -1496,25 +1496,29 @@ enum {
|
||||||
#define kvm_arch_vcpu_memslots_id(vcpu) ((vcpu)->arch.hflags & HF_SMM_MASK ? 1 : 0)
|
#define kvm_arch_vcpu_memslots_id(vcpu) ((vcpu)->arch.hflags & HF_SMM_MASK ? 1 : 0)
|
||||||
#define kvm_memslots_for_spte_role(kvm, role) __kvm_memslots(kvm, (role).smm)
|
#define kvm_memslots_for_spte_role(kvm, role) __kvm_memslots(kvm, (role).smm)
|
||||||
|
|
||||||
|
asmlinkage void __noreturn kvm_spurious_fault(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hardware virtualization extension instructions may fault if a
|
* Hardware virtualization extension instructions may fault if a
|
||||||
* reboot turns off virtualization while processes are running.
|
* reboot turns off virtualization while processes are running.
|
||||||
* Trap the fault and ignore the instruction if that happens.
|
* Usually after catching the fault we just panic; during reboot
|
||||||
|
* instead the instruction is ignored.
|
||||||
*/
|
*/
|
||||||
asmlinkage void kvm_spurious_fault(void);
|
#define ____kvm_handle_fault_on_reboot(insn, cleanup_insn) \
|
||||||
|
"666: \n\t" \
|
||||||
#define ____kvm_handle_fault_on_reboot(insn, cleanup_insn) \
|
insn "\n\t" \
|
||||||
"666: " insn "\n\t" \
|
"jmp 668f \n\t" \
|
||||||
"668: \n\t" \
|
"667: \n\t" \
|
||||||
".pushsection .fixup, \"ax\" \n" \
|
"call kvm_spurious_fault \n\t" \
|
||||||
"667: \n\t" \
|
"668: \n\t" \
|
||||||
cleanup_insn "\n\t" \
|
".pushsection .fixup, \"ax\" \n\t" \
|
||||||
"cmpb $0, kvm_rebooting \n\t" \
|
"700: \n\t" \
|
||||||
"jne 668b \n\t" \
|
cleanup_insn "\n\t" \
|
||||||
__ASM_SIZE(push) " $666b \n\t" \
|
"cmpb $0, kvm_rebooting\n\t" \
|
||||||
"jmp kvm_spurious_fault \n\t" \
|
"je 667b \n\t" \
|
||||||
".popsection \n\t" \
|
"jmp 668b \n\t" \
|
||||||
_ASM_EXTABLE(666b, 667b)
|
".popsection \n\t" \
|
||||||
|
_ASM_EXTABLE(666b, 700b)
|
||||||
|
|
||||||
#define __kvm_handle_fault_on_reboot(insn) \
|
#define __kvm_handle_fault_on_reboot(insn) \
|
||||||
____kvm_handle_fault_on_reboot(insn, "")
|
____kvm_handle_fault_on_reboot(insn, "")
|
||||||
|
|
|
@ -746,6 +746,7 @@ bool __raw_callee_save___native_vcpu_is_preempted(long cpu);
|
||||||
PV_RESTORE_ALL_CALLER_REGS \
|
PV_RESTORE_ALL_CALLER_REGS \
|
||||||
FRAME_END \
|
FRAME_END \
|
||||||
"ret;" \
|
"ret;" \
|
||||||
|
".size " PV_THUNK_NAME(func) ", .-" PV_THUNK_NAME(func) ";" \
|
||||||
".popsection")
|
".popsection")
|
||||||
|
|
||||||
/* Get a reference to a callee-save function */
|
/* Get a reference to a callee-save function */
|
||||||
|
|
|
@ -253,10 +253,10 @@ END(secondary_startup_64)
|
||||||
* start_secondary() via .Ljump_to_C_code.
|
* start_secondary() via .Ljump_to_C_code.
|
||||||
*/
|
*/
|
||||||
ENTRY(start_cpu0)
|
ENTRY(start_cpu0)
|
||||||
movq initial_stack(%rip), %rsp
|
|
||||||
UNWIND_HINT_EMPTY
|
UNWIND_HINT_EMPTY
|
||||||
|
movq initial_stack(%rip), %rsp
|
||||||
jmp .Ljump_to_C_code
|
jmp .Ljump_to_C_code
|
||||||
ENDPROC(start_cpu0)
|
END(start_cpu0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Both SMP bootup and ACPI suspend change these variables */
|
/* Both SMP bootup and ACPI suspend change these variables */
|
||||||
|
|
|
@ -838,6 +838,7 @@ asm(
|
||||||
"cmpb $0, " __stringify(KVM_STEAL_TIME_preempted) "+steal_time(%rax);"
|
"cmpb $0, " __stringify(KVM_STEAL_TIME_preempted) "+steal_time(%rax);"
|
||||||
"setne %al;"
|
"setne %al;"
|
||||||
"ret;"
|
"ret;"
|
||||||
|
".size __raw_callee_save___kvm_vcpu_is_preempted, .-__raw_callee_save___kvm_vcpu_is_preempted;"
|
||||||
".popsection");
|
".popsection");
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -312,29 +312,42 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt)
|
||||||
|
|
||||||
static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *));
|
static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *));
|
||||||
|
|
||||||
#define FOP_FUNC(name) \
|
#define __FOP_FUNC(name) \
|
||||||
".align " __stringify(FASTOP_SIZE) " \n\t" \
|
".align " __stringify(FASTOP_SIZE) " \n\t" \
|
||||||
".type " name ", @function \n\t" \
|
".type " name ", @function \n\t" \
|
||||||
name ":\n\t"
|
name ":\n\t"
|
||||||
|
|
||||||
#define FOP_RET "ret \n\t"
|
#define FOP_FUNC(name) \
|
||||||
|
__FOP_FUNC(#name)
|
||||||
|
|
||||||
|
#define __FOP_RET(name) \
|
||||||
|
"ret \n\t" \
|
||||||
|
".size " name ", .-" name "\n\t"
|
||||||
|
|
||||||
|
#define FOP_RET(name) \
|
||||||
|
__FOP_RET(#name)
|
||||||
|
|
||||||
#define FOP_START(op) \
|
#define FOP_START(op) \
|
||||||
extern void em_##op(struct fastop *fake); \
|
extern void em_##op(struct fastop *fake); \
|
||||||
asm(".pushsection .text, \"ax\" \n\t" \
|
asm(".pushsection .text, \"ax\" \n\t" \
|
||||||
".global em_" #op " \n\t" \
|
".global em_" #op " \n\t" \
|
||||||
FOP_FUNC("em_" #op)
|
".align " __stringify(FASTOP_SIZE) " \n\t" \
|
||||||
|
"em_" #op ":\n\t"
|
||||||
|
|
||||||
#define FOP_END \
|
#define FOP_END \
|
||||||
".popsection")
|
".popsection")
|
||||||
|
|
||||||
|
#define __FOPNOP(name) \
|
||||||
|
__FOP_FUNC(name) \
|
||||||
|
__FOP_RET(name)
|
||||||
|
|
||||||
#define FOPNOP() \
|
#define FOPNOP() \
|
||||||
FOP_FUNC(__stringify(__UNIQUE_ID(nop))) \
|
__FOPNOP(__stringify(__UNIQUE_ID(nop)))
|
||||||
FOP_RET
|
|
||||||
|
|
||||||
#define FOP1E(op, dst) \
|
#define FOP1E(op, dst) \
|
||||||
FOP_FUNC(#op "_" #dst) \
|
__FOP_FUNC(#op "_" #dst) \
|
||||||
"10: " #op " %" #dst " \n\t" FOP_RET
|
"10: " #op " %" #dst " \n\t" \
|
||||||
|
__FOP_RET(#op "_" #dst)
|
||||||
|
|
||||||
#define FOP1EEX(op, dst) \
|
#define FOP1EEX(op, dst) \
|
||||||
FOP1E(op, dst) _ASM_EXTABLE(10b, kvm_fastop_exception)
|
FOP1E(op, dst) _ASM_EXTABLE(10b, kvm_fastop_exception)
|
||||||
|
@ -366,8 +379,9 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *));
|
||||||
FOP_END
|
FOP_END
|
||||||
|
|
||||||
#define FOP2E(op, dst, src) \
|
#define FOP2E(op, dst, src) \
|
||||||
FOP_FUNC(#op "_" #dst "_" #src) \
|
__FOP_FUNC(#op "_" #dst "_" #src) \
|
||||||
#op " %" #src ", %" #dst " \n\t" FOP_RET
|
#op " %" #src ", %" #dst " \n\t" \
|
||||||
|
__FOP_RET(#op "_" #dst "_" #src)
|
||||||
|
|
||||||
#define FASTOP2(op) \
|
#define FASTOP2(op) \
|
||||||
FOP_START(op) \
|
FOP_START(op) \
|
||||||
|
@ -405,8 +419,9 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *));
|
||||||
FOP_END
|
FOP_END
|
||||||
|
|
||||||
#define FOP3E(op, dst, src, src2) \
|
#define FOP3E(op, dst, src, src2) \
|
||||||
FOP_FUNC(#op "_" #dst "_" #src "_" #src2) \
|
__FOP_FUNC(#op "_" #dst "_" #src "_" #src2) \
|
||||||
#op " %" #src2 ", %" #src ", %" #dst " \n\t" FOP_RET
|
#op " %" #src2 ", %" #src ", %" #dst " \n\t"\
|
||||||
|
__FOP_RET(#op "_" #dst "_" #src "_" #src2)
|
||||||
|
|
||||||
/* 3-operand, word-only, src2=cl */
|
/* 3-operand, word-only, src2=cl */
|
||||||
#define FASTOP3WCL(op) \
|
#define FASTOP3WCL(op) \
|
||||||
|
@ -423,7 +438,7 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *));
|
||||||
".type " #op ", @function \n\t" \
|
".type " #op ", @function \n\t" \
|
||||||
#op ": \n\t" \
|
#op ": \n\t" \
|
||||||
#op " %al \n\t" \
|
#op " %al \n\t" \
|
||||||
FOP_RET
|
__FOP_RET(#op)
|
||||||
|
|
||||||
asm(".pushsection .fixup, \"ax\"\n"
|
asm(".pushsection .fixup, \"ax\"\n"
|
||||||
".global kvm_fastop_exception \n"
|
".global kvm_fastop_exception \n"
|
||||||
|
@ -449,7 +464,10 @@ FOP_SETCC(setle)
|
||||||
FOP_SETCC(setnle)
|
FOP_SETCC(setnle)
|
||||||
FOP_END;
|
FOP_END;
|
||||||
|
|
||||||
FOP_START(salc) "pushf; sbb %al, %al; popf \n\t" FOP_RET
|
FOP_START(salc)
|
||||||
|
FOP_FUNC(salc)
|
||||||
|
"pushf; sbb %al, %al; popf \n\t"
|
||||||
|
FOP_RET(salc)
|
||||||
FOP_END;
|
FOP_END;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -54,9 +54,9 @@ ENTRY(vmx_vmenter)
|
||||||
ret
|
ret
|
||||||
|
|
||||||
3: cmpb $0, kvm_rebooting
|
3: cmpb $0, kvm_rebooting
|
||||||
jne 4f
|
je 4f
|
||||||
call kvm_spurious_fault
|
ret
|
||||||
4: ret
|
4: ud2
|
||||||
|
|
||||||
.pushsection .fixup, "ax"
|
.pushsection .fixup, "ax"
|
||||||
5: jmp 3b
|
5: jmp 3b
|
||||||
|
|
|
@ -239,7 +239,7 @@ copy_user_handle_tail:
|
||||||
ret
|
ret
|
||||||
|
|
||||||
_ASM_EXTABLE_UA(1b, 2b)
|
_ASM_EXTABLE_UA(1b, 2b)
|
||||||
ENDPROC(copy_user_handle_tail)
|
END(copy_user_handle_tail)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* copy_user_nocache - Uncached memory copy with exception handling
|
* copy_user_nocache - Uncached memory copy with exception handling
|
||||||
|
|
|
@ -115,29 +115,29 @@ ENDPROC(__get_user_8)
|
||||||
EXPORT_SYMBOL(__get_user_8)
|
EXPORT_SYMBOL(__get_user_8)
|
||||||
|
|
||||||
|
|
||||||
|
bad_get_user_clac:
|
||||||
|
ASM_CLAC
|
||||||
bad_get_user:
|
bad_get_user:
|
||||||
xor %edx,%edx
|
xor %edx,%edx
|
||||||
mov $(-EFAULT),%_ASM_AX
|
mov $(-EFAULT),%_ASM_AX
|
||||||
ASM_CLAC
|
|
||||||
ret
|
ret
|
||||||
END(bad_get_user)
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_32
|
#ifdef CONFIG_X86_32
|
||||||
|
bad_get_user_8_clac:
|
||||||
|
ASM_CLAC
|
||||||
bad_get_user_8:
|
bad_get_user_8:
|
||||||
xor %edx,%edx
|
xor %edx,%edx
|
||||||
xor %ecx,%ecx
|
xor %ecx,%ecx
|
||||||
mov $(-EFAULT),%_ASM_AX
|
mov $(-EFAULT),%_ASM_AX
|
||||||
ASM_CLAC
|
|
||||||
ret
|
ret
|
||||||
END(bad_get_user_8)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_ASM_EXTABLE_UA(1b, bad_get_user)
|
_ASM_EXTABLE_UA(1b, bad_get_user_clac)
|
||||||
_ASM_EXTABLE_UA(2b, bad_get_user)
|
_ASM_EXTABLE_UA(2b, bad_get_user_clac)
|
||||||
_ASM_EXTABLE_UA(3b, bad_get_user)
|
_ASM_EXTABLE_UA(3b, bad_get_user_clac)
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
_ASM_EXTABLE_UA(4b, bad_get_user)
|
_ASM_EXTABLE_UA(4b, bad_get_user_clac)
|
||||||
#else
|
#else
|
||||||
_ASM_EXTABLE_UA(4b, bad_get_user_8)
|
_ASM_EXTABLE_UA(4b, bad_get_user_8_clac)
|
||||||
_ASM_EXTABLE_UA(5b, bad_get_user_8)
|
_ASM_EXTABLE_UA(5b, bad_get_user_8_clac)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,8 +32,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define ENTER mov PER_CPU_VAR(current_task), %_ASM_BX
|
#define ENTER mov PER_CPU_VAR(current_task), %_ASM_BX
|
||||||
#define EXIT ASM_CLAC ; \
|
|
||||||
ret
|
|
||||||
|
|
||||||
.text
|
.text
|
||||||
ENTRY(__put_user_1)
|
ENTRY(__put_user_1)
|
||||||
|
@ -43,7 +41,8 @@ ENTRY(__put_user_1)
|
||||||
ASM_STAC
|
ASM_STAC
|
||||||
1: movb %al,(%_ASM_CX)
|
1: movb %al,(%_ASM_CX)
|
||||||
xor %eax,%eax
|
xor %eax,%eax
|
||||||
EXIT
|
ASM_CLAC
|
||||||
|
ret
|
||||||
ENDPROC(__put_user_1)
|
ENDPROC(__put_user_1)
|
||||||
EXPORT_SYMBOL(__put_user_1)
|
EXPORT_SYMBOL(__put_user_1)
|
||||||
|
|
||||||
|
@ -56,7 +55,8 @@ ENTRY(__put_user_2)
|
||||||
ASM_STAC
|
ASM_STAC
|
||||||
2: movw %ax,(%_ASM_CX)
|
2: movw %ax,(%_ASM_CX)
|
||||||
xor %eax,%eax
|
xor %eax,%eax
|
||||||
EXIT
|
ASM_CLAC
|
||||||
|
ret
|
||||||
ENDPROC(__put_user_2)
|
ENDPROC(__put_user_2)
|
||||||
EXPORT_SYMBOL(__put_user_2)
|
EXPORT_SYMBOL(__put_user_2)
|
||||||
|
|
||||||
|
@ -69,7 +69,8 @@ ENTRY(__put_user_4)
|
||||||
ASM_STAC
|
ASM_STAC
|
||||||
3: movl %eax,(%_ASM_CX)
|
3: movl %eax,(%_ASM_CX)
|
||||||
xor %eax,%eax
|
xor %eax,%eax
|
||||||
EXIT
|
ASM_CLAC
|
||||||
|
ret
|
||||||
ENDPROC(__put_user_4)
|
ENDPROC(__put_user_4)
|
||||||
EXPORT_SYMBOL(__put_user_4)
|
EXPORT_SYMBOL(__put_user_4)
|
||||||
|
|
||||||
|
@ -85,19 +86,21 @@ ENTRY(__put_user_8)
|
||||||
5: movl %edx,4(%_ASM_CX)
|
5: movl %edx,4(%_ASM_CX)
|
||||||
#endif
|
#endif
|
||||||
xor %eax,%eax
|
xor %eax,%eax
|
||||||
EXIT
|
ASM_CLAC
|
||||||
|
RET
|
||||||
ENDPROC(__put_user_8)
|
ENDPROC(__put_user_8)
|
||||||
EXPORT_SYMBOL(__put_user_8)
|
EXPORT_SYMBOL(__put_user_8)
|
||||||
|
|
||||||
|
bad_put_user_clac:
|
||||||
|
ASM_CLAC
|
||||||
bad_put_user:
|
bad_put_user:
|
||||||
movl $-EFAULT,%eax
|
movl $-EFAULT,%eax
|
||||||
EXIT
|
RET
|
||||||
END(bad_put_user)
|
|
||||||
|
|
||||||
_ASM_EXTABLE_UA(1b, bad_put_user)
|
_ASM_EXTABLE_UA(1b, bad_put_user_clac)
|
||||||
_ASM_EXTABLE_UA(2b, bad_put_user)
|
_ASM_EXTABLE_UA(2b, bad_put_user_clac)
|
||||||
_ASM_EXTABLE_UA(3b, bad_put_user)
|
_ASM_EXTABLE_UA(3b, bad_put_user_clac)
|
||||||
_ASM_EXTABLE_UA(4b, bad_put_user)
|
_ASM_EXTABLE_UA(4b, bad_put_user_clac)
|
||||||
#ifdef CONFIG_X86_32
|
#ifdef CONFIG_X86_32
|
||||||
_ASM_EXTABLE_UA(5b, bad_put_user)
|
_ASM_EXTABLE_UA(5b, bad_put_user_clac)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -60,7 +60,7 @@ EXPORT_SYMBOL(clear_user);
|
||||||
* but reuse __memcpy_mcsafe in case a new read error is encountered.
|
* but reuse __memcpy_mcsafe in case a new read error is encountered.
|
||||||
* clac() is handled in _copy_to_iter_mcsafe().
|
* clac() is handled in _copy_to_iter_mcsafe().
|
||||||
*/
|
*/
|
||||||
__visible unsigned long
|
__visible notrace unsigned long
|
||||||
mcsafe_handle_tail(char *to, char *from, unsigned len)
|
mcsafe_handle_tail(char *to, char *from, unsigned len)
|
||||||
{
|
{
|
||||||
for (; len; --len, to++, from++) {
|
for (; len; --len, to++, from++) {
|
||||||
|
|
|
@ -170,3 +170,5 @@
|
||||||
#else
|
#else
|
||||||
#define __diag_GCC_8(s)
|
#define __diag_GCC_8(s)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define __no_fgcse __attribute__((optimize("-fno-gcse")))
|
||||||
|
|
|
@ -116,9 +116,14 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
|
||||||
".pushsection .discard.unreachable\n\t" \
|
".pushsection .discard.unreachable\n\t" \
|
||||||
".long 999b - .\n\t" \
|
".long 999b - .\n\t" \
|
||||||
".popsection\n\t"
|
".popsection\n\t"
|
||||||
|
|
||||||
|
/* Annotate a C jump table to allow objtool to follow the code flow */
|
||||||
|
#define __annotate_jump_table __section(".rodata..c_jump_table")
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define annotate_reachable()
|
#define annotate_reachable()
|
||||||
#define annotate_unreachable()
|
#define annotate_unreachable()
|
||||||
|
#define __annotate_jump_table
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ASM_UNREACHABLE
|
#ifndef ASM_UNREACHABLE
|
||||||
|
|
|
@ -189,6 +189,10 @@ struct ftrace_likely_data {
|
||||||
#define asm_volatile_goto(x...) asm goto(x)
|
#define asm_volatile_goto(x...) asm goto(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef __no_fgcse
|
||||||
|
# define __no_fgcse
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Are two types/vars the same type (ignoring qualifiers)? */
|
/* Are two types/vars the same type (ignoring qualifiers)? */
|
||||||
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
|
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
|
||||||
|
|
||||||
|
|
|
@ -1295,11 +1295,11 @@ bool bpf_opcode_in_insntable(u8 code)
|
||||||
*
|
*
|
||||||
* Decode and execute eBPF instructions.
|
* Decode and execute eBPF instructions.
|
||||||
*/
|
*/
|
||||||
static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn, u64 *stack)
|
static u64 __no_fgcse ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn, u64 *stack)
|
||||||
{
|
{
|
||||||
#define BPF_INSN_2_LBL(x, y) [BPF_##x | BPF_##y] = &&x##_##y
|
#define BPF_INSN_2_LBL(x, y) [BPF_##x | BPF_##y] = &&x##_##y
|
||||||
#define BPF_INSN_3_LBL(x, y, z) [BPF_##x | BPF_##y | BPF_##z] = &&x##_##y##_##z
|
#define BPF_INSN_3_LBL(x, y, z) [BPF_##x | BPF_##y | BPF_##z] = &&x##_##y##_##z
|
||||||
static const void *jumptable[256] = {
|
static const void * const jumptable[256] __annotate_jump_table = {
|
||||||
[0 ... 255] = &&default_label,
|
[0 ... 255] = &&default_label,
|
||||||
/* Now overwrite non-defaults ... */
|
/* Now overwrite non-defaults ... */
|
||||||
BPF_INSN_MAP(BPF_INSN_2_LBL, BPF_INSN_3_LBL),
|
BPF_INSN_MAP(BPF_INSN_2_LBL, BPF_INSN_3_LBL),
|
||||||
|
@ -1558,7 +1558,6 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn, u64 *stack)
|
||||||
BUG_ON(1);
|
BUG_ON(1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
STACK_FRAME_NON_STANDARD(___bpf_prog_run); /* jump table */
|
|
||||||
|
|
||||||
#define PROG_NAME(stack_size) __bpf_prog_run##stack_size
|
#define PROG_NAME(stack_size) __bpf_prog_run##stack_size
|
||||||
#define DEFINE_BPF_PROG_RUN(stack_size) \
|
#define DEFINE_BPF_PROG_RUN(stack_size) \
|
||||||
|
|
|
@ -226,12 +226,17 @@ unsigned int stack_trace_save_user(unsigned long *store, unsigned int size)
|
||||||
.store = store,
|
.store = store,
|
||||||
.size = size,
|
.size = size,
|
||||||
};
|
};
|
||||||
|
mm_segment_t fs;
|
||||||
|
|
||||||
/* Trace user stack if not a kernel thread */
|
/* Trace user stack if not a kernel thread */
|
||||||
if (current->flags & PF_KTHREAD)
|
if (current->flags & PF_KTHREAD)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
fs = get_fs();
|
||||||
|
set_fs(USER_DS);
|
||||||
arch_stack_walk_user(consume_entry, &c, task_pt_regs(current));
|
arch_stack_walk_user(consume_entry, &c, task_pt_regs(current));
|
||||||
|
set_fs(fs);
|
||||||
|
|
||||||
return c.len;
|
return c.len;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -11,22 +11,24 @@
|
||||||
#include "elf.h"
|
#include "elf.h"
|
||||||
#include "cfi.h"
|
#include "cfi.h"
|
||||||
|
|
||||||
#define INSN_JUMP_CONDITIONAL 1
|
enum insn_type {
|
||||||
#define INSN_JUMP_UNCONDITIONAL 2
|
INSN_JUMP_CONDITIONAL,
|
||||||
#define INSN_JUMP_DYNAMIC 3
|
INSN_JUMP_UNCONDITIONAL,
|
||||||
#define INSN_CALL 4
|
INSN_JUMP_DYNAMIC,
|
||||||
#define INSN_CALL_DYNAMIC 5
|
INSN_JUMP_DYNAMIC_CONDITIONAL,
|
||||||
#define INSN_RETURN 6
|
INSN_CALL,
|
||||||
#define INSN_CONTEXT_SWITCH 7
|
INSN_CALL_DYNAMIC,
|
||||||
#define INSN_STACK 8
|
INSN_RETURN,
|
||||||
#define INSN_BUG 9
|
INSN_CONTEXT_SWITCH,
|
||||||
#define INSN_NOP 10
|
INSN_STACK,
|
||||||
#define INSN_STAC 11
|
INSN_BUG,
|
||||||
#define INSN_CLAC 12
|
INSN_NOP,
|
||||||
#define INSN_STD 13
|
INSN_STAC,
|
||||||
#define INSN_CLD 14
|
INSN_CLAC,
|
||||||
#define INSN_OTHER 15
|
INSN_STD,
|
||||||
#define INSN_LAST INSN_OTHER
|
INSN_CLD,
|
||||||
|
INSN_OTHER,
|
||||||
|
};
|
||||||
|
|
||||||
enum op_dest_type {
|
enum op_dest_type {
|
||||||
OP_DEST_REG,
|
OP_DEST_REG,
|
||||||
|
@ -68,7 +70,7 @@ void arch_initial_func_cfi_state(struct cfi_state *state);
|
||||||
|
|
||||||
int arch_decode_instruction(struct elf *elf, struct section *sec,
|
int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
unsigned long offset, unsigned int maxlen,
|
unsigned long offset, unsigned int maxlen,
|
||||||
unsigned int *len, unsigned char *type,
|
unsigned int *len, enum insn_type *type,
|
||||||
unsigned long *immediate, struct stack_op *op);
|
unsigned long *immediate, struct stack_op *op);
|
||||||
|
|
||||||
bool arch_callee_saved_reg(unsigned char reg);
|
bool arch_callee_saved_reg(unsigned char reg);
|
||||||
|
|
|
@ -68,7 +68,7 @@ bool arch_callee_saved_reg(unsigned char reg)
|
||||||
|
|
||||||
int arch_decode_instruction(struct elf *elf, struct section *sec,
|
int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
unsigned long offset, unsigned int maxlen,
|
unsigned long offset, unsigned int maxlen,
|
||||||
unsigned int *len, unsigned char *type,
|
unsigned int *len, enum insn_type *type,
|
||||||
unsigned long *immediate, struct stack_op *op)
|
unsigned long *immediate, struct stack_op *op)
|
||||||
{
|
{
|
||||||
struct insn insn;
|
struct insn insn;
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#define FAKE_JUMP_OFFSET -1
|
#define FAKE_JUMP_OFFSET -1
|
||||||
|
|
||||||
|
#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
|
||||||
|
|
||||||
struct alternative {
|
struct alternative {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct instruction *insn;
|
struct instruction *insn;
|
||||||
|
@ -95,6 +97,20 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
|
||||||
for (insn = next_insn_same_sec(file, insn); insn; \
|
for (insn = next_insn_same_sec(file, insn); insn; \
|
||||||
insn = next_insn_same_sec(file, insn))
|
insn = next_insn_same_sec(file, insn))
|
||||||
|
|
||||||
|
static bool is_sibling_call(struct instruction *insn)
|
||||||
|
{
|
||||||
|
/* 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 (insn->type != INSN_JUMP_CONDITIONAL &&
|
||||||
|
insn->type != INSN_JUMP_UNCONDITIONAL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* add_jump_destinations() sets insn->call_dest for sibling calls. */
|
||||||
|
return !!insn->call_dest;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This checks to see if the given function is a "noreturn" function.
|
* This checks to see if the given function is a "noreturn" function.
|
||||||
*
|
*
|
||||||
|
@ -103,14 +119,9 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
|
||||||
*
|
*
|
||||||
* For local functions, we have to detect them manually by simply looking for
|
* For local functions, we have to detect them manually by simply looking for
|
||||||
* the lack of a return instruction.
|
* the lack of a return instruction.
|
||||||
*
|
|
||||||
* Returns:
|
|
||||||
* -1: error
|
|
||||||
* 0: no dead end
|
|
||||||
* 1: dead end
|
|
||||||
*/
|
*/
|
||||||
static int __dead_end_function(struct objtool_file *file, struct symbol *func,
|
static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
|
||||||
int recursion)
|
int recursion)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct instruction *insn;
|
struct instruction *insn;
|
||||||
|
@ -136,30 +147,33 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
|
||||||
"rewind_stack_do_exit",
|
"rewind_stack_do_exit",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!func)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (func->bind == STB_WEAK)
|
if (func->bind == STB_WEAK)
|
||||||
return 0;
|
return false;
|
||||||
|
|
||||||
if (func->bind == STB_GLOBAL)
|
if (func->bind == STB_GLOBAL)
|
||||||
for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
|
for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
|
||||||
if (!strcmp(func->name, global_noreturns[i]))
|
if (!strcmp(func->name, global_noreturns[i]))
|
||||||
return 1;
|
return true;
|
||||||
|
|
||||||
if (!func->len)
|
if (!func->len)
|
||||||
return 0;
|
return false;
|
||||||
|
|
||||||
insn = find_insn(file, func->sec, func->offset);
|
insn = find_insn(file, func->sec, func->offset);
|
||||||
if (!insn->func)
|
if (!insn->func)
|
||||||
return 0;
|
return false;
|
||||||
|
|
||||||
func_for_each_insn_all(file, func, insn) {
|
func_for_each_insn_all(file, func, insn) {
|
||||||
empty = false;
|
empty = false;
|
||||||
|
|
||||||
if (insn->type == INSN_RETURN)
|
if (insn->type == INSN_RETURN)
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty)
|
if (empty)
|
||||||
return 0;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A function can have a sibling call instead of a return. In that
|
* A function can have a sibling call instead of a return. In that
|
||||||
|
@ -167,40 +181,31 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
|
||||||
* of the sibling call returns.
|
* of the sibling call returns.
|
||||||
*/
|
*/
|
||||||
func_for_each_insn_all(file, func, insn) {
|
func_for_each_insn_all(file, func, insn) {
|
||||||
if (insn->type == INSN_JUMP_UNCONDITIONAL) {
|
if (is_sibling_call(insn)) {
|
||||||
struct instruction *dest = insn->jump_dest;
|
struct instruction *dest = insn->jump_dest;
|
||||||
|
|
||||||
if (!dest)
|
if (!dest)
|
||||||
/* sibling call to another file */
|
/* sibling call to another file */
|
||||||
return 0;
|
return false;
|
||||||
|
|
||||||
if (dest->func && dest->func->pfunc != insn->func->pfunc) {
|
/* local sibling call */
|
||||||
|
if (recursion == 5) {
|
||||||
/* local sibling call */
|
/*
|
||||||
if (recursion == 5) {
|
* Infinite recursion: two functions have
|
||||||
/*
|
* sibling calls to each other. This is a very
|
||||||
* Infinite recursion: two functions
|
* rare case. It means they aren't dead ends.
|
||||||
* have sibling calls to each other.
|
*/
|
||||||
* This is a very rare case. It means
|
return false;
|
||||||
* they aren't dead ends.
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return __dead_end_function(file, dest->func,
|
|
||||||
recursion + 1);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
|
return __dead_end_function(file, dest->func, recursion+1);
|
||||||
/* sibling call */
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dead_end_function(struct objtool_file *file, struct symbol *func)
|
static bool dead_end_function(struct objtool_file *file, struct symbol *func)
|
||||||
{
|
{
|
||||||
return __dead_end_function(file, func, 0);
|
return __dead_end_function(file, func, 0);
|
||||||
}
|
}
|
||||||
|
@ -262,19 +267,12 @@ static int decode_instructions(struct objtool_file *file)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (!insn->type || insn->type > INSN_LAST) {
|
|
||||||
WARN_FUNC("invalid instruction type %d",
|
|
||||||
insn->sec, insn->offset, insn->type);
|
|
||||||
ret = -1;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_add(file->insn_hash, &insn->hash, insn->offset);
|
hash_add(file->insn_hash, &insn->hash, insn->offset);
|
||||||
list_add_tail(&insn->list, &file->insn_list);
|
list_add_tail(&insn->list, &file->insn_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(func, &sec->symbol_list, list) {
|
list_for_each_entry(func, &sec->symbol_list, list) {
|
||||||
if (func->type != STT_FUNC)
|
if (func->type != STT_FUNC || func->alias != func)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!find_insn(file, sec, func->offset)) {
|
if (!find_insn(file, sec, func->offset)) {
|
||||||
|
@ -284,8 +282,7 @@ static int decode_instructions(struct objtool_file *file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func_for_each_insn(file, func, insn)
|
func_for_each_insn(file, func, insn)
|
||||||
if (!insn->func)
|
insn->func = func;
|
||||||
insn->func = func;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,6 +485,7 @@ static const char *uaccess_safe_builtin[] = {
|
||||||
/* misc */
|
/* misc */
|
||||||
"csum_partial_copy_generic",
|
"csum_partial_copy_generic",
|
||||||
"__memcpy_mcsafe",
|
"__memcpy_mcsafe",
|
||||||
|
"mcsafe_handle_tail",
|
||||||
"ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
|
"ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -505,7 +503,7 @@ static void add_uaccess_safe(struct objtool_file *file)
|
||||||
if (!func)
|
if (!func)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
func->alias->uaccess_safe = true;
|
func->uaccess_safe = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,13 +575,16 @@ static int add_jump_destinations(struct objtool_file *file)
|
||||||
* Retpoline jumps are really dynamic jumps in
|
* Retpoline jumps are really dynamic jumps in
|
||||||
* disguise, so convert them accordingly.
|
* disguise, so convert them accordingly.
|
||||||
*/
|
*/
|
||||||
insn->type = INSN_JUMP_DYNAMIC;
|
if (insn->type == INSN_JUMP_UNCONDITIONAL)
|
||||||
|
insn->type = INSN_JUMP_DYNAMIC;
|
||||||
|
else
|
||||||
|
insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
|
||||||
|
|
||||||
insn->retpoline_safe = true;
|
insn->retpoline_safe = true;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
/* sibling call */
|
/* external sibling call */
|
||||||
insn->call_dest = rela->sym;
|
insn->call_dest = rela->sym;
|
||||||
insn->jump_dest = NULL;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,7 +624,7 @@ static int add_jump_destinations(struct objtool_file *file)
|
||||||
* However this code can't completely replace the
|
* However this code can't completely replace the
|
||||||
* read_symbols() code because this doesn't detect the
|
* read_symbols() code because this doesn't detect the
|
||||||
* case where the parent function's only reference to a
|
* case where the parent function's only reference to a
|
||||||
* subfunction is through a switch table.
|
* subfunction is through a jump table.
|
||||||
*/
|
*/
|
||||||
if (!strstr(insn->func->name, ".cold.") &&
|
if (!strstr(insn->func->name, ".cold.") &&
|
||||||
strstr(insn->jump_dest->func->name, ".cold.")) {
|
strstr(insn->jump_dest->func->name, ".cold.")) {
|
||||||
|
@ -633,9 +634,8 @@ static int add_jump_destinations(struct objtool_file *file)
|
||||||
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
|
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
|
||||||
insn->jump_dest->offset == insn->jump_dest->func->offset) {
|
insn->jump_dest->offset == insn->jump_dest->func->offset) {
|
||||||
|
|
||||||
/* sibling class */
|
/* internal sibling call */
|
||||||
insn->call_dest = insn->jump_dest->func;
|
insn->call_dest = insn->jump_dest->func;
|
||||||
insn->jump_dest = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -896,20 +896,26 @@ static int add_special_section_alts(struct objtool_file *file)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
static int add_jump_table(struct objtool_file *file, struct instruction *insn,
|
||||||
struct rela *table, struct rela *next_table)
|
struct rela *table)
|
||||||
{
|
{
|
||||||
struct rela *rela = table;
|
struct rela *rela = table;
|
||||||
struct instruction *alt_insn;
|
struct instruction *dest_insn;
|
||||||
struct alternative *alt;
|
struct alternative *alt;
|
||||||
struct symbol *pfunc = insn->func->pfunc;
|
struct symbol *pfunc = insn->func->pfunc;
|
||||||
unsigned int prev_offset = 0;
|
unsigned int prev_offset = 0;
|
||||||
|
|
||||||
list_for_each_entry_from(rela, &table->rela_sec->rela_list, list) {
|
/*
|
||||||
if (rela == next_table)
|
* Each @rela is a switch table relocation which points to the target
|
||||||
|
* instruction.
|
||||||
|
*/
|
||||||
|
list_for_each_entry_from(rela, &table->sec->rela_list, list) {
|
||||||
|
|
||||||
|
/* Check for the end of the table: */
|
||||||
|
if (rela != table && rela->jump_table_start)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Make sure the switch table entries are consecutive: */
|
/* Make sure the table entries are consecutive: */
|
||||||
if (prev_offset && rela->offset != prev_offset + 8)
|
if (prev_offset && rela->offset != prev_offset + 8)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -918,12 +924,12 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
||||||
rela->addend == pfunc->offset)
|
rela->addend == pfunc->offset)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
alt_insn = find_insn(file, rela->sym->sec, rela->addend);
|
dest_insn = find_insn(file, rela->sym->sec, rela->addend);
|
||||||
if (!alt_insn)
|
if (!dest_insn)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Make sure the jmp dest is in the function or subfunction: */
|
/* Make sure the destination is in the same function: */
|
||||||
if (alt_insn->func->pfunc != pfunc)
|
if (!dest_insn->func || dest_insn->func->pfunc != pfunc)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
alt = malloc(sizeof(*alt));
|
alt = malloc(sizeof(*alt));
|
||||||
|
@ -932,7 +938,7 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
alt->insn = alt_insn;
|
alt->insn = dest_insn;
|
||||||
list_add_tail(&alt->list, &insn->alts);
|
list_add_tail(&alt->list, &insn->alts);
|
||||||
prev_offset = rela->offset;
|
prev_offset = rela->offset;
|
||||||
}
|
}
|
||||||
|
@ -947,7 +953,7 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* find_switch_table() - Given a dynamic jump, find the switch jump table in
|
* find_jump_table() - Given a dynamic jump, find the switch jump table in
|
||||||
* .rodata associated with it.
|
* .rodata associated with it.
|
||||||
*
|
*
|
||||||
* There are 3 basic patterns:
|
* There are 3 basic patterns:
|
||||||
|
@ -989,13 +995,13 @@ static int add_switch_table(struct objtool_file *file, struct instruction *insn,
|
||||||
*
|
*
|
||||||
* NOTE: RETPOLINE made it harder still to decode dynamic jumps.
|
* NOTE: RETPOLINE made it harder still to decode dynamic jumps.
|
||||||
*/
|
*/
|
||||||
static struct rela *find_switch_table(struct objtool_file *file,
|
static struct rela *find_jump_table(struct objtool_file *file,
|
||||||
struct symbol *func,
|
struct symbol *func,
|
||||||
struct instruction *insn)
|
struct instruction *insn)
|
||||||
{
|
{
|
||||||
struct rela *text_rela, *rodata_rela;
|
struct rela *text_rela, *table_rela;
|
||||||
struct instruction *orig_insn = insn;
|
struct instruction *orig_insn = insn;
|
||||||
struct section *rodata_sec;
|
struct section *table_sec;
|
||||||
unsigned long table_offset;
|
unsigned long table_offset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1028,42 +1034,52 @@ static struct rela *find_switch_table(struct objtool_file *file,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
table_offset = text_rela->addend;
|
table_offset = text_rela->addend;
|
||||||
rodata_sec = text_rela->sym->sec;
|
table_sec = text_rela->sym->sec;
|
||||||
|
|
||||||
if (text_rela->type == R_X86_64_PC32)
|
if (text_rela->type == R_X86_64_PC32)
|
||||||
table_offset += 4;
|
table_offset += 4;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the .rodata address isn't associated with a
|
* Make sure the .rodata address isn't associated with a
|
||||||
* symbol. gcc jump tables are anonymous data.
|
* symbol. GCC jump tables are anonymous data.
|
||||||
|
*
|
||||||
|
* Also support C jump tables which are in the same format as
|
||||||
|
* switch jump tables. For objtool to recognize them, they
|
||||||
|
* need to be placed in the C_JUMP_TABLE_SECTION section. They
|
||||||
|
* have symbols associated with them.
|
||||||
*/
|
*/
|
||||||
if (find_symbol_containing(rodata_sec, table_offset))
|
if (find_symbol_containing(table_sec, table_offset) &&
|
||||||
|
strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
rodata_rela = find_rela_by_dest(rodata_sec, table_offset);
|
/* Each table entry has a rela associated with it. */
|
||||||
if (rodata_rela) {
|
table_rela = find_rela_by_dest(table_sec, table_offset);
|
||||||
/*
|
if (!table_rela)
|
||||||
* Use of RIP-relative switch jumps is quite rare, and
|
continue;
|
||||||
* indicates a rare GCC quirk/bug which can leave dead
|
|
||||||
* code behind.
|
|
||||||
*/
|
|
||||||
if (text_rela->type == R_X86_64_PC32)
|
|
||||||
file->ignore_unreachables = true;
|
|
||||||
|
|
||||||
return rodata_rela;
|
/*
|
||||||
}
|
* Use of RIP-relative switch jumps is quite rare, and
|
||||||
|
* indicates a rare GCC quirk/bug which can leave dead code
|
||||||
|
* behind.
|
||||||
|
*/
|
||||||
|
if (text_rela->type == R_X86_64_PC32)
|
||||||
|
file->ignore_unreachables = true;
|
||||||
|
|
||||||
|
return table_rela;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
static int add_func_switch_tables(struct objtool_file *file,
|
* First pass: Mark the head of each jump table so that in the next pass,
|
||||||
struct symbol *func)
|
* we know when a given jump table ends and the next one starts.
|
||||||
|
*/
|
||||||
|
static void mark_func_jump_tables(struct objtool_file *file,
|
||||||
|
struct symbol *func)
|
||||||
{
|
{
|
||||||
struct instruction *insn, *last = NULL, *prev_jump = NULL;
|
struct instruction *insn, *last = NULL;
|
||||||
struct rela *rela, *prev_rela = NULL;
|
struct rela *rela;
|
||||||
int ret;
|
|
||||||
|
|
||||||
func_for_each_insn_all(file, func, insn) {
|
func_for_each_insn_all(file, func, insn) {
|
||||||
if (!last)
|
if (!last)
|
||||||
|
@ -1071,7 +1087,7 @@ static int add_func_switch_tables(struct objtool_file *file,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store back-pointers for unconditional forward jumps such
|
* Store back-pointers for unconditional forward jumps such
|
||||||
* that find_switch_table() can back-track using those and
|
* that find_jump_table() can back-track using those and
|
||||||
* avoid some potentially confusing code.
|
* avoid some potentially confusing code.
|
||||||
*/
|
*/
|
||||||
if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
|
if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
|
||||||
|
@ -1086,27 +1102,25 @@ static int add_func_switch_tables(struct objtool_file *file,
|
||||||
if (insn->type != INSN_JUMP_DYNAMIC)
|
if (insn->type != INSN_JUMP_DYNAMIC)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
rela = find_switch_table(file, func, insn);
|
rela = find_jump_table(file, func, insn);
|
||||||
if (!rela)
|
if (rela) {
|
||||||
|
rela->jump_table_start = true;
|
||||||
|
insn->jump_table = rela;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_func_jump_tables(struct objtool_file *file,
|
||||||
|
struct symbol *func)
|
||||||
|
{
|
||||||
|
struct instruction *insn;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
func_for_each_insn_all(file, func, insn) {
|
||||||
|
if (!insn->jump_table)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
ret = add_jump_table(file, insn, insn->jump_table);
|
||||||
* We found a switch table, but we don't know yet how big it
|
|
||||||
* is. Don't add it until we reach the end of the function or
|
|
||||||
* the beginning of another switch table in the same function.
|
|
||||||
*/
|
|
||||||
if (prev_jump) {
|
|
||||||
ret = add_switch_table(file, prev_jump, prev_rela, rela);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev_jump = insn;
|
|
||||||
prev_rela = rela;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prev_jump) {
|
|
||||||
ret = add_switch_table(file, prev_jump, prev_rela, NULL);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1119,7 +1133,7 @@ static int add_func_switch_tables(struct objtool_file *file,
|
||||||
* section which contains a list of addresses within the function to jump to.
|
* section which contains a list of addresses within the function to jump to.
|
||||||
* This finds these jump tables and adds them to the insn->alts lists.
|
* This finds these jump tables and adds them to the insn->alts lists.
|
||||||
*/
|
*/
|
||||||
static int add_switch_table_alts(struct objtool_file *file)
|
static int add_jump_table_alts(struct objtool_file *file)
|
||||||
{
|
{
|
||||||
struct section *sec;
|
struct section *sec;
|
||||||
struct symbol *func;
|
struct symbol *func;
|
||||||
|
@ -1133,7 +1147,8 @@ static int add_switch_table_alts(struct objtool_file *file)
|
||||||
if (func->type != STT_FUNC)
|
if (func->type != STT_FUNC)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ret = add_func_switch_tables(file, func);
|
mark_func_jump_tables(file, func);
|
||||||
|
ret = add_func_jump_tables(file, func);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1277,13 +1292,18 @@ static void mark_rodata(struct objtool_file *file)
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This searches for the .rodata section or multiple .rodata.func_name
|
* Search for the following rodata sections, each of which can
|
||||||
* sections if -fdata-sections is being used. The .str.1.1 and .str.1.8
|
* potentially contain jump tables:
|
||||||
* rodata sections are ignored as they don't contain jump tables.
|
*
|
||||||
|
* - .rodata: can contain GCC switch tables
|
||||||
|
* - .rodata.<func>: same, if -fdata-sections is being used
|
||||||
|
* - .rodata..c_jump_table: contains C annotated jump tables
|
||||||
|
*
|
||||||
|
* .rodata.str1.* sections are ignored; they don't contain jump tables.
|
||||||
*/
|
*/
|
||||||
for_each_sec(file, sec) {
|
for_each_sec(file, sec) {
|
||||||
if (!strncmp(sec->name, ".rodata", 7) &&
|
if ((!strncmp(sec->name, ".rodata", 7) && !strstr(sec->name, ".str1.")) ||
|
||||||
!strstr(sec->name, ".str1.")) {
|
!strcmp(sec->name, C_JUMP_TABLE_SECTION)) {
|
||||||
sec->rodata = true;
|
sec->rodata = true;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
|
@ -1325,7 +1345,7 @@ static int decode_sections(struct objtool_file *file)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = add_switch_table_alts(file);
|
ret = add_jump_table_alts(file);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -1873,12 +1893,12 @@ static bool insn_state_match(struct instruction *insn, struct insn_state *state)
|
||||||
static inline bool func_uaccess_safe(struct symbol *func)
|
static inline bool func_uaccess_safe(struct symbol *func)
|
||||||
{
|
{
|
||||||
if (func)
|
if (func)
|
||||||
return func->alias->uaccess_safe;
|
return func->uaccess_safe;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const char *insn_dest_name(struct instruction *insn)
|
static inline const char *call_dest_name(struct instruction *insn)
|
||||||
{
|
{
|
||||||
if (insn->call_dest)
|
if (insn->call_dest)
|
||||||
return insn->call_dest->name;
|
return insn->call_dest->name;
|
||||||
|
@ -1890,13 +1910,13 @@ static int validate_call(struct instruction *insn, struct insn_state *state)
|
||||||
{
|
{
|
||||||
if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
|
if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
|
||||||
WARN_FUNC("call to %s() with UACCESS enabled",
|
WARN_FUNC("call to %s() with UACCESS enabled",
|
||||||
insn->sec, insn->offset, insn_dest_name(insn));
|
insn->sec, insn->offset, call_dest_name(insn));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->df) {
|
if (state->df) {
|
||||||
WARN_FUNC("call to %s() with DF set",
|
WARN_FUNC("call to %s() with DF set",
|
||||||
insn->sec, insn->offset, insn_dest_name(insn));
|
insn->sec, insn->offset, call_dest_name(insn));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1920,13 +1940,12 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st
|
||||||
* each instruction and validate all the rules described in
|
* each instruction and validate all the rules described in
|
||||||
* tools/objtool/Documentation/stack-validation.txt.
|
* tools/objtool/Documentation/stack-validation.txt.
|
||||||
*/
|
*/
|
||||||
static int validate_branch(struct objtool_file *file, struct instruction *first,
|
static int validate_branch(struct objtool_file *file, struct symbol *func,
|
||||||
struct insn_state state)
|
struct instruction *first, struct insn_state state)
|
||||||
{
|
{
|
||||||
struct alternative *alt;
|
struct alternative *alt;
|
||||||
struct instruction *insn, *next_insn;
|
struct instruction *insn, *next_insn;
|
||||||
struct section *sec;
|
struct section *sec;
|
||||||
struct symbol *func = NULL;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
insn = first;
|
insn = first;
|
||||||
|
@ -1947,9 +1966,6 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (insn->func)
|
|
||||||
func = insn->func->pfunc;
|
|
||||||
|
|
||||||
if (func && insn->ignore) {
|
if (func && insn->ignore) {
|
||||||
WARN_FUNC("BUG: why am I validating an ignored function?",
|
WARN_FUNC("BUG: why am I validating an ignored function?",
|
||||||
sec, insn->offset);
|
sec, insn->offset);
|
||||||
|
@ -1971,7 +1987,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
||||||
|
|
||||||
i = insn;
|
i = insn;
|
||||||
save_insn = NULL;
|
save_insn = NULL;
|
||||||
func_for_each_insn_continue_reverse(file, insn->func, i) {
|
func_for_each_insn_continue_reverse(file, func, i) {
|
||||||
if (i->save) {
|
if (i->save) {
|
||||||
save_insn = i;
|
save_insn = i;
|
||||||
break;
|
break;
|
||||||
|
@ -2017,7 +2033,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
||||||
if (alt->skip_orig)
|
if (alt->skip_orig)
|
||||||
skip_orig = true;
|
skip_orig = true;
|
||||||
|
|
||||||
ret = validate_branch(file, alt->insn, state);
|
ret = validate_branch(file, func, alt->insn, state);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (backtrace)
|
if (backtrace)
|
||||||
BT_FUNC("(alt)", insn);
|
BT_FUNC("(alt)", insn);
|
||||||
|
@ -2055,7 +2071,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
||||||
|
|
||||||
if (state.bp_scratch) {
|
if (state.bp_scratch) {
|
||||||
WARN("%s uses BP as a scratch register",
|
WARN("%s uses BP as a scratch register",
|
||||||
insn->func->name);
|
func->name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2067,36 +2083,28 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (insn->type == INSN_CALL) {
|
if (!no_fp && func && !is_fentry_call(insn) &&
|
||||||
if (is_fentry_call(insn))
|
!has_valid_stack_frame(&state)) {
|
||||||
break;
|
|
||||||
|
|
||||||
ret = dead_end_function(file, insn->call_dest);
|
|
||||||
if (ret == 1)
|
|
||||||
return 0;
|
|
||||||
if (ret == -1)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!no_fp && func && !has_valid_stack_frame(&state)) {
|
|
||||||
WARN_FUNC("call without frame pointer save/setup",
|
WARN_FUNC("call without frame pointer save/setup",
|
||||||
sec, insn->offset);
|
sec, insn->offset);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dead_end_function(file, insn->call_dest))
|
||||||
|
return 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INSN_JUMP_CONDITIONAL:
|
case INSN_JUMP_CONDITIONAL:
|
||||||
case INSN_JUMP_UNCONDITIONAL:
|
case INSN_JUMP_UNCONDITIONAL:
|
||||||
if (func && !insn->jump_dest) {
|
if (func && is_sibling_call(insn)) {
|
||||||
ret = validate_sibling_call(insn, &state);
|
ret = validate_sibling_call(insn, &state);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
} else if (insn->jump_dest &&
|
} else if (insn->jump_dest) {
|
||||||
(!func || !insn->jump_dest->func ||
|
ret = validate_branch(file, func,
|
||||||
insn->jump_dest->func->pfunc == func)) {
|
insn->jump_dest, state);
|
||||||
ret = validate_branch(file, insn->jump_dest,
|
|
||||||
state);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (backtrace)
|
if (backtrace)
|
||||||
BT_FUNC("(branch)", insn);
|
BT_FUNC("(branch)", insn);
|
||||||
|
@ -2110,13 +2118,17 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INSN_JUMP_DYNAMIC:
|
case INSN_JUMP_DYNAMIC:
|
||||||
if (func && list_empty(&insn->alts)) {
|
case INSN_JUMP_DYNAMIC_CONDITIONAL:
|
||||||
|
if (func && is_sibling_call(insn)) {
|
||||||
ret = validate_sibling_call(insn, &state);
|
ret = validate_sibling_call(insn, &state);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
if (insn->type == INSN_JUMP_DYNAMIC)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case INSN_CONTEXT_SWITCH:
|
case INSN_CONTEXT_SWITCH:
|
||||||
if (func && (!next_insn || !next_insn->hint)) {
|
if (func && (!next_insn || !next_insn->hint)) {
|
||||||
|
@ -2162,7 +2174,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INSN_CLAC:
|
case INSN_CLAC:
|
||||||
if (!state.uaccess && insn->func) {
|
if (!state.uaccess && func) {
|
||||||
WARN_FUNC("redundant UACCESS disable", sec, insn->offset);
|
WARN_FUNC("redundant UACCESS disable", sec, insn->offset);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -2183,7 +2195,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INSN_CLD:
|
case INSN_CLD:
|
||||||
if (!state.df && insn->func)
|
if (!state.df && func)
|
||||||
WARN_FUNC("redundant CLD", sec, insn->offset);
|
WARN_FUNC("redundant CLD", sec, insn->offset);
|
||||||
|
|
||||||
state.df = false;
|
state.df = false;
|
||||||
|
@ -2222,7 +2234,7 @@ static int validate_unwind_hints(struct objtool_file *file)
|
||||||
|
|
||||||
for_each_insn(file, insn) {
|
for_each_insn(file, insn) {
|
||||||
if (insn->hint && !insn->visited) {
|
if (insn->hint && !insn->visited) {
|
||||||
ret = validate_branch(file, insn, state);
|
ret = validate_branch(file, insn->func, insn, state);
|
||||||
if (ret && backtrace)
|
if (ret && backtrace)
|
||||||
BT_FUNC("<=== (hint)", insn);
|
BT_FUNC("<=== (hint)", insn);
|
||||||
warnings += ret;
|
warnings += ret;
|
||||||
|
@ -2345,16 +2357,25 @@ static int validate_functions(struct objtool_file *file)
|
||||||
|
|
||||||
for_each_sec(file, sec) {
|
for_each_sec(file, sec) {
|
||||||
list_for_each_entry(func, &sec->symbol_list, list) {
|
list_for_each_entry(func, &sec->symbol_list, list) {
|
||||||
if (func->type != STT_FUNC || func->pfunc != func)
|
if (func->type != STT_FUNC)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!func->len) {
|
||||||
|
WARN("%s() is missing an ELF size annotation",
|
||||||
|
func->name);
|
||||||
|
warnings++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (func->pfunc != func || func->alias != func)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
insn = find_insn(file, sec, func->offset);
|
insn = find_insn(file, sec, func->offset);
|
||||||
if (!insn || insn->ignore)
|
if (!insn || insn->ignore || insn->visited)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
state.uaccess = func->alias->uaccess_safe;
|
state.uaccess = func->uaccess_safe;
|
||||||
|
|
||||||
ret = validate_branch(file, insn, state);
|
ret = validate_branch(file, func, insn, state);
|
||||||
if (ret && backtrace)
|
if (ret && backtrace)
|
||||||
BT_FUNC("<=== (func)", insn);
|
BT_FUNC("<=== (func)", insn);
|
||||||
warnings += ret;
|
warnings += ret;
|
||||||
|
@ -2407,7 +2428,7 @@ int check(const char *_objname, bool orc)
|
||||||
|
|
||||||
objname = _objname;
|
objname = _objname;
|
||||||
|
|
||||||
file.elf = elf_open(objname, orc ? O_RDWR : O_RDONLY);
|
file.elf = elf_read(objname, orc ? O_RDWR : O_RDONLY);
|
||||||
if (!file.elf)
|
if (!file.elf)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
|
|
@ -31,13 +31,14 @@ struct instruction {
|
||||||
struct section *sec;
|
struct section *sec;
|
||||||
unsigned long offset;
|
unsigned long offset;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
unsigned char type;
|
enum insn_type type;
|
||||||
unsigned long immediate;
|
unsigned long immediate;
|
||||||
bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts;
|
bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts;
|
||||||
bool retpoline_safe;
|
bool retpoline_safe;
|
||||||
struct symbol *call_dest;
|
struct symbol *call_dest;
|
||||||
struct instruction *jump_dest;
|
struct instruction *jump_dest;
|
||||||
struct instruction *first_jump_src;
|
struct instruction *first_jump_src;
|
||||||
|
struct rela *jump_table;
|
||||||
struct list_head alts;
|
struct list_head alts;
|
||||||
struct symbol *func;
|
struct symbol *func;
|
||||||
struct stack_op stack_op;
|
struct stack_op stack_op;
|
||||||
|
|
|
@ -278,7 +278,7 @@ static int read_symbols(struct elf *elf)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sym->offset == s->offset) {
|
if (sym->offset == s->offset) {
|
||||||
if (sym->len == s->len && alias == sym)
|
if (sym->len && sym->len == s->len && alias == sym)
|
||||||
alias = s;
|
alias = s;
|
||||||
|
|
||||||
if (sym->len >= s->len) {
|
if (sym->len >= s->len) {
|
||||||
|
@ -385,7 +385,7 @@ static int read_relas(struct elf *elf)
|
||||||
rela->offset = rela->rela.r_offset;
|
rela->offset = rela->rela.r_offset;
|
||||||
symndx = GELF_R_SYM(rela->rela.r_info);
|
symndx = GELF_R_SYM(rela->rela.r_info);
|
||||||
rela->sym = find_symbol_by_index(elf, symndx);
|
rela->sym = find_symbol_by_index(elf, symndx);
|
||||||
rela->rela_sec = sec;
|
rela->sec = sec;
|
||||||
if (!rela->sym) {
|
if (!rela->sym) {
|
||||||
WARN("can't find rela entry symbol %d for %s",
|
WARN("can't find rela entry symbol %d for %s",
|
||||||
symndx, sec->name);
|
symndx, sec->name);
|
||||||
|
@ -401,7 +401,7 @@ static int read_relas(struct elf *elf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct elf *elf_open(const char *name, int flags)
|
struct elf *elf_read(const char *name, int flags)
|
||||||
{
|
{
|
||||||
struct elf *elf;
|
struct elf *elf;
|
||||||
Elf_Cmd cmd;
|
Elf_Cmd cmd;
|
||||||
|
@ -463,7 +463,7 @@ struct section *elf_create_section(struct elf *elf, const char *name,
|
||||||
{
|
{
|
||||||
struct section *sec, *shstrtab;
|
struct section *sec, *shstrtab;
|
||||||
size_t size = entsize * nr;
|
size_t size = entsize * nr;
|
||||||
struct Elf_Scn *s;
|
Elf_Scn *s;
|
||||||
Elf_Data *data;
|
Elf_Data *data;
|
||||||
|
|
||||||
sec = malloc(sizeof(*sec));
|
sec = malloc(sizeof(*sec));
|
||||||
|
|
|
@ -57,11 +57,12 @@ struct rela {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct hlist_node hash;
|
struct hlist_node hash;
|
||||||
GElf_Rela rela;
|
GElf_Rela rela;
|
||||||
struct section *rela_sec;
|
struct section *sec;
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
unsigned int type;
|
unsigned int type;
|
||||||
unsigned long offset;
|
unsigned long offset;
|
||||||
int addend;
|
int addend;
|
||||||
|
bool jump_table_start;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct elf {
|
struct elf {
|
||||||
|
@ -74,7 +75,7 @@ struct elf {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct elf *elf_open(const char *name, int flags);
|
struct elf *elf_read(const char *name, int flags);
|
||||||
struct section *find_section_by_name(struct elf *elf, const char *name);
|
struct section *find_section_by_name(struct elf *elf, const char *name);
|
||||||
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
|
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
|
||||||
struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
|
struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
|
||||||
|
|
Loading…
Reference in New Issue