mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-26 14:17:26 -04:00
There are a lot of objtool changes in this cycle, all across the map:
- Speed up objtool significantly, especially when there are large number of sections - Improve objtool's understanding of special instructions such as IRET, to reduce the number of annotations required - Implement 'noinstr' validation - Do baby steps for non-x86 objtool use - Simplify/fix retpoline decoding - Add vmlinux validation - Improve documentation - Fix various bugs and apply smaller cleanups Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAl7VHvcRHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1gEfBAAhvPWljUmfQsetYq4q9BdbuC4xPSQN9ra e+2zu1MQaohkjAdFM1boNVhCCGKFUvlTEEw3GJR141Us6Y/ZRS8VIo70tmVSku6I OwuR5i8SgEKwurr1SwLxrI05rovYWRLSaDIRTHn2CViPEjgriyFGRV8QKam3AYmI dx47la3ELwuQR68nIdIMzDRt49oZVy+ZKW8Pjgjklzrd5KMYsPy7HPtraHUMeDg+ GdoC7RresIt5AFiDiIJzKTT/jROI7KuHFnM6blluKHoKenWhYBFCz3sd6IvCdQWX JGy+KKY6H+YDMSpgc4FRP56M3GI0hX14oCd7L72epSLfOuzPr9Tmf6wfyQ8f50Je LGLD47tyltIcQR9H85YdR8UQspkjSW6xcql4ByCPTEqp0UzSGTsVntvsHzwsgz6A Csh3s+DVdv0rk5ZjMCu8STA2oErpehJm7fmugt2oLx+nsCNCBUI25lilw5JGeq5c +cO0IRxRxHPeRvMWvItTjbixVAHOHYlB00ilDbvsm+GnTJgu/5cMqpXdLvfXI2Rr nl360bSS3t3J4w5rX0mXw4x24vjQmVrA69jU+oo8RSHje2X8Y4Q7sFHNjmN0YAI3 Re8aP6HSLQjioJxGz9aISlrxmPOXe0CMp8JE586SREVgmS/olXtidMgi7l12uZ2B cRdtNYcn31U= =dbCU -----END PGP SIGNATURE----- Merge tag 'objtool-core-2020-06-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull objtool updates from Ingo Molnar: "There are a lot of objtool changes in this cycle, all across the map: - Speed up objtool significantly, especially when there are large number of sections - Improve objtool's understanding of special instructions such as IRET, to reduce the number of annotations required - Implement 'noinstr' validation - Do baby steps for non-x86 objtool use - Simplify/fix retpoline decoding - Add vmlinux validation - Improve documentation - Fix various bugs and apply smaller cleanups" * tag 'objtool-core-2020-06-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (54 commits) objtool: Enable compilation of objtool for all architectures objtool: Move struct objtool_file into arch-independent header objtool: Exit successfully when requesting help objtool: Add check_kcov_mode() to the uaccess safelist samples/ftrace: Fix asm function ELF annotations objtool: optimize add_dead_ends for split sections objtool: use gelf_getsymshndx to handle >64k sections objtool: Allow no-op CFI ops in alternatives x86/retpoline: Fix retpoline unwind x86: Change {JMP,CALL}_NOSPEC argument x86: Simplify retpoline declaration x86/speculation: Change FILL_RETURN_BUFFER to work with objtool objtool: Add support for intra-function calls objtool: Move the IRET hack into the arch decoder objtool: Remove INSN_STACK objtool: Make handle_insn_ops() unconditional objtool: Rework allocating stack_ops on decode objtool: UNWIND_HINT_RET_OFFSET should not check registers objtool: is_fentry_call() crashes if call has no destination x86,smap: Fix smap_{save,restore}() alternatives ...
This commit is contained in:
commit
69fc06f70f
46 changed files with 1231 additions and 727 deletions
|
@ -2758,7 +2758,7 @@ SYM_FUNC_START(aesni_xts_crypt8)
|
||||||
pxor INC, STATE4
|
pxor INC, STATE4
|
||||||
movdqu IV, 0x30(OUTP)
|
movdqu IV, 0x30(OUTP)
|
||||||
|
|
||||||
CALL_NOSPEC %r11
|
CALL_NOSPEC r11
|
||||||
|
|
||||||
movdqu 0x00(OUTP), INC
|
movdqu 0x00(OUTP), INC
|
||||||
pxor INC, STATE1
|
pxor INC, STATE1
|
||||||
|
@ -2803,7 +2803,7 @@ SYM_FUNC_START(aesni_xts_crypt8)
|
||||||
_aesni_gf128mul_x_ble()
|
_aesni_gf128mul_x_ble()
|
||||||
movups IV, (IVP)
|
movups IV, (IVP)
|
||||||
|
|
||||||
CALL_NOSPEC %r11
|
CALL_NOSPEC r11
|
||||||
|
|
||||||
movdqu 0x40(OUTP), INC
|
movdqu 0x40(OUTP), INC
|
||||||
pxor INC, STATE1
|
pxor INC, STATE1
|
||||||
|
|
|
@ -1228,7 +1228,7 @@ SYM_FUNC_START_LOCAL(camellia_xts_crypt_16way)
|
||||||
vpxor 14 * 16(%rax), %xmm15, %xmm14;
|
vpxor 14 * 16(%rax), %xmm15, %xmm14;
|
||||||
vpxor 15 * 16(%rax), %xmm15, %xmm15;
|
vpxor 15 * 16(%rax), %xmm15, %xmm15;
|
||||||
|
|
||||||
CALL_NOSPEC %r9;
|
CALL_NOSPEC r9;
|
||||||
|
|
||||||
addq $(16 * 16), %rsp;
|
addq $(16 * 16), %rsp;
|
||||||
|
|
||||||
|
|
|
@ -1339,7 +1339,7 @@ SYM_FUNC_START_LOCAL(camellia_xts_crypt_32way)
|
||||||
vpxor 14 * 32(%rax), %ymm15, %ymm14;
|
vpxor 14 * 32(%rax), %ymm15, %ymm14;
|
||||||
vpxor 15 * 32(%rax), %ymm15, %ymm15;
|
vpxor 15 * 32(%rax), %ymm15, %ymm15;
|
||||||
|
|
||||||
CALL_NOSPEC %r9;
|
CALL_NOSPEC r9;
|
||||||
|
|
||||||
addq $(16 * 32), %rsp;
|
addq $(16 * 32), %rsp;
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
|
|
||||||
.text
|
.text
|
||||||
SYM_FUNC_START(crc_pcl)
|
SYM_FUNC_START(crc_pcl)
|
||||||
#define bufp %rdi
|
#define bufp rdi
|
||||||
#define bufp_dw %edi
|
#define bufp_dw %edi
|
||||||
#define bufp_w %di
|
#define bufp_w %di
|
||||||
#define bufp_b %dil
|
#define bufp_b %dil
|
||||||
|
@ -105,9 +105,9 @@ SYM_FUNC_START(crc_pcl)
|
||||||
## 1) ALIGN:
|
## 1) ALIGN:
|
||||||
################################################################
|
################################################################
|
||||||
|
|
||||||
mov bufp, bufptmp # rdi = *buf
|
mov %bufp, bufptmp # rdi = *buf
|
||||||
neg bufp
|
neg %bufp
|
||||||
and $7, bufp # calculate the unalignment amount of
|
and $7, %bufp # calculate the unalignment amount of
|
||||||
# the address
|
# the address
|
||||||
je proc_block # Skip if aligned
|
je proc_block # Skip if aligned
|
||||||
|
|
||||||
|
@ -123,13 +123,13 @@ SYM_FUNC_START(crc_pcl)
|
||||||
do_align:
|
do_align:
|
||||||
#### Calculate CRC of unaligned bytes of the buffer (if any)
|
#### Calculate CRC of unaligned bytes of the buffer (if any)
|
||||||
movq (bufptmp), tmp # load a quadward from the buffer
|
movq (bufptmp), tmp # load a quadward from the buffer
|
||||||
add bufp, bufptmp # align buffer pointer for quadword
|
add %bufp, bufptmp # align buffer pointer for quadword
|
||||||
# processing
|
# processing
|
||||||
sub bufp, len # update buffer length
|
sub %bufp, len # update buffer length
|
||||||
align_loop:
|
align_loop:
|
||||||
crc32b %bl, crc_init_dw # compute crc32 of 1-byte
|
crc32b %bl, crc_init_dw # compute crc32 of 1-byte
|
||||||
shr $8, tmp # get next byte
|
shr $8, tmp # get next byte
|
||||||
dec bufp
|
dec %bufp
|
||||||
jne align_loop
|
jne align_loop
|
||||||
|
|
||||||
proc_block:
|
proc_block:
|
||||||
|
@ -169,10 +169,10 @@ continue_block:
|
||||||
xor crc2, crc2
|
xor crc2, crc2
|
||||||
|
|
||||||
## branch into array
|
## branch into array
|
||||||
lea jump_table(%rip), bufp
|
lea jump_table(%rip), %bufp
|
||||||
movzxw (bufp, %rax, 2), len
|
movzxw (%bufp, %rax, 2), len
|
||||||
lea crc_array(%rip), bufp
|
lea crc_array(%rip), %bufp
|
||||||
lea (bufp, len, 1), bufp
|
lea (%bufp, len, 1), %bufp
|
||||||
JMP_NOSPEC bufp
|
JMP_NOSPEC bufp
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
|
@ -218,9 +218,9 @@ LABEL crc_ %i
|
||||||
## 4) Combine three results:
|
## 4) Combine three results:
|
||||||
################################################################
|
################################################################
|
||||||
|
|
||||||
lea (K_table-8)(%rip), bufp # first entry is for idx 1
|
lea (K_table-8)(%rip), %bufp # first entry is for idx 1
|
||||||
shlq $3, %rax # rax *= 8
|
shlq $3, %rax # rax *= 8
|
||||||
pmovzxdq (bufp,%rax), %xmm0 # 2 consts: K1:K2
|
pmovzxdq (%bufp,%rax), %xmm0 # 2 consts: K1:K2
|
||||||
leal (%eax,%eax,2), %eax # rax *= 3 (total *24)
|
leal (%eax,%eax,2), %eax # rax *= 3 (total *24)
|
||||||
subq %rax, tmp # tmp -= rax*24
|
subq %rax, tmp # tmp -= rax*24
|
||||||
|
|
||||||
|
|
|
@ -816,7 +816,7 @@ SYM_CODE_START(ret_from_fork)
|
||||||
|
|
||||||
/* kernel thread */
|
/* kernel thread */
|
||||||
1: movl %edi, %eax
|
1: movl %edi, %eax
|
||||||
CALL_NOSPEC %ebx
|
CALL_NOSPEC ebx
|
||||||
/*
|
/*
|
||||||
* A kernel thread is allowed to return here after successfully
|
* A kernel thread is allowed to return here after successfully
|
||||||
* calling do_execve(). Exit to userspace to complete the execve()
|
* calling do_execve(). Exit to userspace to complete the execve()
|
||||||
|
@ -1501,7 +1501,7 @@ SYM_CODE_START_LOCAL_NOALIGN(common_exception_read_cr2)
|
||||||
|
|
||||||
TRACE_IRQS_OFF
|
TRACE_IRQS_OFF
|
||||||
movl %esp, %eax # pt_regs pointer
|
movl %esp, %eax # pt_regs pointer
|
||||||
CALL_NOSPEC %edi
|
CALL_NOSPEC edi
|
||||||
jmp ret_from_exception
|
jmp ret_from_exception
|
||||||
SYM_CODE_END(common_exception_read_cr2)
|
SYM_CODE_END(common_exception_read_cr2)
|
||||||
|
|
||||||
|
@ -1522,7 +1522,7 @@ SYM_CODE_START_LOCAL_NOALIGN(common_exception)
|
||||||
|
|
||||||
TRACE_IRQS_OFF
|
TRACE_IRQS_OFF
|
||||||
movl %esp, %eax # pt_regs pointer
|
movl %esp, %eax # pt_regs pointer
|
||||||
CALL_NOSPEC %edi
|
CALL_NOSPEC edi
|
||||||
jmp ret_from_exception
|
jmp ret_from_exception
|
||||||
SYM_CODE_END(common_exception)
|
SYM_CODE_END(common_exception)
|
||||||
|
|
||||||
|
|
|
@ -348,7 +348,7 @@ SYM_CODE_START(ret_from_fork)
|
||||||
/* kernel thread */
|
/* kernel thread */
|
||||||
UNWIND_HINT_EMPTY
|
UNWIND_HINT_EMPTY
|
||||||
movq %r12, %rdi
|
movq %r12, %rdi
|
||||||
CALL_NOSPEC %rbx
|
CALL_NOSPEC rbx
|
||||||
/*
|
/*
|
||||||
* A kernel thread is allowed to return here after successfully
|
* A kernel thread is allowed to return here after successfully
|
||||||
* calling do_execve(). Exit to userspace to complete the execve()
|
* calling do_execve(). Exit to userspace to complete the execve()
|
||||||
|
|
25
arch/x86/include/asm/GEN-for-each-reg.h
Normal file
25
arch/x86/include/asm/GEN-for-each-reg.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
GEN(rax)
|
||||||
|
GEN(rbx)
|
||||||
|
GEN(rcx)
|
||||||
|
GEN(rdx)
|
||||||
|
GEN(rsi)
|
||||||
|
GEN(rdi)
|
||||||
|
GEN(rbp)
|
||||||
|
GEN(r8)
|
||||||
|
GEN(r9)
|
||||||
|
GEN(r10)
|
||||||
|
GEN(r11)
|
||||||
|
GEN(r12)
|
||||||
|
GEN(r13)
|
||||||
|
GEN(r14)
|
||||||
|
GEN(r15)
|
||||||
|
#else
|
||||||
|
GEN(eax)
|
||||||
|
GEN(ebx)
|
||||||
|
GEN(ecx)
|
||||||
|
GEN(edx)
|
||||||
|
GEN(esi)
|
||||||
|
GEN(edi)
|
||||||
|
GEN(ebp)
|
||||||
|
#endif
|
|
@ -17,24 +17,19 @@ extern void cmpxchg8b_emu(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_RETPOLINE
|
#ifdef CONFIG_RETPOLINE
|
||||||
#ifdef CONFIG_X86_32
|
|
||||||
#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_e ## reg(void);
|
#define DECL_INDIRECT_THUNK(reg) \
|
||||||
#else
|
extern asmlinkage void __x86_indirect_thunk_ ## reg (void);
|
||||||
#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_r ## reg(void);
|
|
||||||
INDIRECT_THUNK(8)
|
#define DECL_RETPOLINE(reg) \
|
||||||
INDIRECT_THUNK(9)
|
extern asmlinkage void __x86_retpoline_ ## reg (void);
|
||||||
INDIRECT_THUNK(10)
|
|
||||||
INDIRECT_THUNK(11)
|
#undef GEN
|
||||||
INDIRECT_THUNK(12)
|
#define GEN(reg) DECL_INDIRECT_THUNK(reg)
|
||||||
INDIRECT_THUNK(13)
|
#include <asm/GEN-for-each-reg.h>
|
||||||
INDIRECT_THUNK(14)
|
|
||||||
INDIRECT_THUNK(15)
|
#undef GEN
|
||||||
#endif
|
#define GEN(reg) DECL_RETPOLINE(reg)
|
||||||
INDIRECT_THUNK(ax)
|
#include <asm/GEN-for-each-reg.h>
|
||||||
INDIRECT_THUNK(bx)
|
|
||||||
INDIRECT_THUNK(cx)
|
|
||||||
INDIRECT_THUNK(dx)
|
|
||||||
INDIRECT_THUNK(si)
|
|
||||||
INDIRECT_THUNK(di)
|
|
||||||
INDIRECT_THUNK(bp)
|
|
||||||
#endif /* CONFIG_RETPOLINE */
|
#endif /* CONFIG_RETPOLINE */
|
||||||
|
|
|
@ -4,20 +4,13 @@
|
||||||
#define _ASM_X86_NOSPEC_BRANCH_H_
|
#define _ASM_X86_NOSPEC_BRANCH_H_
|
||||||
|
|
||||||
#include <linux/static_key.h>
|
#include <linux/static_key.h>
|
||||||
|
#include <linux/frame.h>
|
||||||
|
|
||||||
#include <asm/alternative.h>
|
#include <asm/alternative.h>
|
||||||
#include <asm/alternative-asm.h>
|
#include <asm/alternative-asm.h>
|
||||||
#include <asm/cpufeatures.h>
|
#include <asm/cpufeatures.h>
|
||||||
#include <asm/msr-index.h>
|
#include <asm/msr-index.h>
|
||||||
|
#include <asm/unwind_hints.h>
|
||||||
/*
|
|
||||||
* This should be used immediately before a retpoline alternative. It tells
|
|
||||||
* objtool where the retpolines are so that it can make sense of the control
|
|
||||||
* flow by just reading the original instruction(s) and ignoring the
|
|
||||||
* alternatives.
|
|
||||||
*/
|
|
||||||
#define ANNOTATE_NOSPEC_ALTERNATIVE \
|
|
||||||
ANNOTATE_IGNORE_ALTERNATIVE
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fill the CPU return stack buffer.
|
* Fill the CPU return stack buffer.
|
||||||
|
@ -46,21 +39,25 @@
|
||||||
#define __FILL_RETURN_BUFFER(reg, nr, sp) \
|
#define __FILL_RETURN_BUFFER(reg, nr, sp) \
|
||||||
mov $(nr/2), reg; \
|
mov $(nr/2), reg; \
|
||||||
771: \
|
771: \
|
||||||
|
ANNOTATE_INTRA_FUNCTION_CALL; \
|
||||||
call 772f; \
|
call 772f; \
|
||||||
773: /* speculation trap */ \
|
773: /* speculation trap */ \
|
||||||
|
UNWIND_HINT_EMPTY; \
|
||||||
pause; \
|
pause; \
|
||||||
lfence; \
|
lfence; \
|
||||||
jmp 773b; \
|
jmp 773b; \
|
||||||
772: \
|
772: \
|
||||||
|
ANNOTATE_INTRA_FUNCTION_CALL; \
|
||||||
call 774f; \
|
call 774f; \
|
||||||
775: /* speculation trap */ \
|
775: /* speculation trap */ \
|
||||||
|
UNWIND_HINT_EMPTY; \
|
||||||
pause; \
|
pause; \
|
||||||
lfence; \
|
lfence; \
|
||||||
jmp 775b; \
|
jmp 775b; \
|
||||||
774: \
|
774: \
|
||||||
|
add $(BITS_PER_LONG/8) * 2, sp; \
|
||||||
dec reg; \
|
dec reg; \
|
||||||
jnz 771b; \
|
jnz 771b;
|
||||||
add $(BITS_PER_LONG/8) * nr, sp;
|
|
||||||
|
|
||||||
#ifdef __ASSEMBLY__
|
#ifdef __ASSEMBLY__
|
||||||
|
|
||||||
|
@ -76,34 +73,6 @@
|
||||||
.popsection
|
.popsection
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
/*
|
|
||||||
* These are the bare retpoline primitives for indirect jmp and call.
|
|
||||||
* Do not use these directly; they only exist to make the ALTERNATIVE
|
|
||||||
* invocation below less ugly.
|
|
||||||
*/
|
|
||||||
.macro RETPOLINE_JMP reg:req
|
|
||||||
call .Ldo_rop_\@
|
|
||||||
.Lspec_trap_\@:
|
|
||||||
pause
|
|
||||||
lfence
|
|
||||||
jmp .Lspec_trap_\@
|
|
||||||
.Ldo_rop_\@:
|
|
||||||
mov \reg, (%_ASM_SP)
|
|
||||||
ret
|
|
||||||
.endm
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a wrapper around RETPOLINE_JMP so the called function in reg
|
|
||||||
* returns to the instruction after the macro.
|
|
||||||
*/
|
|
||||||
.macro RETPOLINE_CALL reg:req
|
|
||||||
jmp .Ldo_call_\@
|
|
||||||
.Ldo_retpoline_jmp_\@:
|
|
||||||
RETPOLINE_JMP \reg
|
|
||||||
.Ldo_call_\@:
|
|
||||||
call .Ldo_retpoline_jmp_\@
|
|
||||||
.endm
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* JMP_NOSPEC and CALL_NOSPEC macros can be used instead of a simple
|
* JMP_NOSPEC and CALL_NOSPEC macros can be used instead of a simple
|
||||||
* indirect jmp/call which may be susceptible to the Spectre variant 2
|
* indirect jmp/call which may be susceptible to the Spectre variant 2
|
||||||
|
@ -111,23 +80,21 @@
|
||||||
*/
|
*/
|
||||||
.macro JMP_NOSPEC reg:req
|
.macro JMP_NOSPEC reg:req
|
||||||
#ifdef CONFIG_RETPOLINE
|
#ifdef CONFIG_RETPOLINE
|
||||||
ANNOTATE_NOSPEC_ALTERNATIVE
|
ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; jmp *%\reg), \
|
||||||
ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; jmp *\reg), \
|
__stringify(jmp __x86_retpoline_\reg), X86_FEATURE_RETPOLINE, \
|
||||||
__stringify(RETPOLINE_JMP \reg), X86_FEATURE_RETPOLINE, \
|
__stringify(lfence; ANNOTATE_RETPOLINE_SAFE; jmp *%\reg), X86_FEATURE_RETPOLINE_AMD
|
||||||
__stringify(lfence; ANNOTATE_RETPOLINE_SAFE; jmp *\reg), X86_FEATURE_RETPOLINE_AMD
|
|
||||||
#else
|
#else
|
||||||
jmp *\reg
|
jmp *%\reg
|
||||||
#endif
|
#endif
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro CALL_NOSPEC reg:req
|
.macro CALL_NOSPEC reg:req
|
||||||
#ifdef CONFIG_RETPOLINE
|
#ifdef CONFIG_RETPOLINE
|
||||||
ANNOTATE_NOSPEC_ALTERNATIVE
|
ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; call *%\reg), \
|
||||||
ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; call *\reg), \
|
__stringify(call __x86_retpoline_\reg), X86_FEATURE_RETPOLINE, \
|
||||||
__stringify(RETPOLINE_CALL \reg), X86_FEATURE_RETPOLINE,\
|
__stringify(lfence; ANNOTATE_RETPOLINE_SAFE; call *%\reg), X86_FEATURE_RETPOLINE_AMD
|
||||||
__stringify(lfence; ANNOTATE_RETPOLINE_SAFE; call *\reg), X86_FEATURE_RETPOLINE_AMD
|
|
||||||
#else
|
#else
|
||||||
call *\reg
|
call *%\reg
|
||||||
#endif
|
#endif
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
|
@ -137,10 +104,8 @@
|
||||||
*/
|
*/
|
||||||
.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req
|
.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req
|
||||||
#ifdef CONFIG_RETPOLINE
|
#ifdef CONFIG_RETPOLINE
|
||||||
ANNOTATE_NOSPEC_ALTERNATIVE
|
ALTERNATIVE "jmp .Lskip_rsb_\@", "", \ftr
|
||||||
ALTERNATIVE "jmp .Lskip_rsb_\@", \
|
__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP)
|
||||||
__stringify(__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP)) \
|
|
||||||
\ftr
|
|
||||||
.Lskip_rsb_\@:
|
.Lskip_rsb_\@:
|
||||||
#endif
|
#endif
|
||||||
.endm
|
.endm
|
||||||
|
@ -161,16 +126,16 @@
|
||||||
* which is ensured when CONFIG_RETPOLINE is defined.
|
* which is ensured when CONFIG_RETPOLINE is defined.
|
||||||
*/
|
*/
|
||||||
# define CALL_NOSPEC \
|
# define CALL_NOSPEC \
|
||||||
ANNOTATE_NOSPEC_ALTERNATIVE \
|
|
||||||
ALTERNATIVE_2( \
|
ALTERNATIVE_2( \
|
||||||
ANNOTATE_RETPOLINE_SAFE \
|
ANNOTATE_RETPOLINE_SAFE \
|
||||||
"call *%[thunk_target]\n", \
|
"call *%[thunk_target]\n", \
|
||||||
"call __x86_indirect_thunk_%V[thunk_target]\n", \
|
"call __x86_retpoline_%V[thunk_target]\n", \
|
||||||
X86_FEATURE_RETPOLINE, \
|
X86_FEATURE_RETPOLINE, \
|
||||||
"lfence;\n" \
|
"lfence;\n" \
|
||||||
ANNOTATE_RETPOLINE_SAFE \
|
ANNOTATE_RETPOLINE_SAFE \
|
||||||
"call *%[thunk_target]\n", \
|
"call *%[thunk_target]\n", \
|
||||||
X86_FEATURE_RETPOLINE_AMD)
|
X86_FEATURE_RETPOLINE_AMD)
|
||||||
|
|
||||||
# define THUNK_TARGET(addr) [thunk_target] "r" (addr)
|
# define THUNK_TARGET(addr) [thunk_target] "r" (addr)
|
||||||
|
|
||||||
#else /* CONFIG_X86_32 */
|
#else /* CONFIG_X86_32 */
|
||||||
|
@ -180,7 +145,6 @@
|
||||||
* here, anyway.
|
* here, anyway.
|
||||||
*/
|
*/
|
||||||
# define CALL_NOSPEC \
|
# define CALL_NOSPEC \
|
||||||
ANNOTATE_NOSPEC_ALTERNATIVE \
|
|
||||||
ALTERNATIVE_2( \
|
ALTERNATIVE_2( \
|
||||||
ANNOTATE_RETPOLINE_SAFE \
|
ANNOTATE_RETPOLINE_SAFE \
|
||||||
"call *%[thunk_target]\n", \
|
"call *%[thunk_target]\n", \
|
||||||
|
|
|
@ -58,8 +58,7 @@
|
||||||
#define ORC_TYPE_CALL 0
|
#define ORC_TYPE_CALL 0
|
||||||
#define ORC_TYPE_REGS 1
|
#define ORC_TYPE_REGS 1
|
||||||
#define ORC_TYPE_REGS_IRET 2
|
#define ORC_TYPE_REGS_IRET 2
|
||||||
#define UNWIND_HINT_TYPE_SAVE 3
|
#define UNWIND_HINT_TYPE_RET_OFFSET 3
|
||||||
#define UNWIND_HINT_TYPE_RESTORE 4
|
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -728,7 +728,6 @@ static inline void sync_core(void)
|
||||||
unsigned int tmp;
|
unsigned int tmp;
|
||||||
|
|
||||||
asm volatile (
|
asm volatile (
|
||||||
UNWIND_HINT_SAVE
|
|
||||||
"mov %%ss, %0\n\t"
|
"mov %%ss, %0\n\t"
|
||||||
"pushq %q0\n\t"
|
"pushq %q0\n\t"
|
||||||
"pushq %%rsp\n\t"
|
"pushq %%rsp\n\t"
|
||||||
|
@ -738,7 +737,6 @@ static inline void sync_core(void)
|
||||||
"pushq %q0\n\t"
|
"pushq %q0\n\t"
|
||||||
"pushq $1f\n\t"
|
"pushq $1f\n\t"
|
||||||
"iretq\n\t"
|
"iretq\n\t"
|
||||||
UNWIND_HINT_RESTORE
|
|
||||||
"1:"
|
"1:"
|
||||||
: "=&r" (tmp), ASM_CALL_CONSTRAINT : : "cc", "memory");
|
: "=&r" (tmp), ASM_CALL_CONSTRAINT : : "cc", "memory");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -57,8 +57,10 @@ static __always_inline unsigned long smap_save(void)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
asm volatile (ALTERNATIVE("", "pushf; pop %0; " __ASM_CLAC,
|
asm volatile ("# smap_save\n\t"
|
||||||
X86_FEATURE_SMAP)
|
ALTERNATIVE("jmp 1f", "", X86_FEATURE_SMAP)
|
||||||
|
"pushf; pop %0; " __ASM_CLAC "\n\t"
|
||||||
|
"1:"
|
||||||
: "=rm" (flags) : : "memory", "cc");
|
: "=rm" (flags) : : "memory", "cc");
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
|
@ -66,7 +68,10 @@ static __always_inline unsigned long smap_save(void)
|
||||||
|
|
||||||
static __always_inline void smap_restore(unsigned long flags)
|
static __always_inline void smap_restore(unsigned long flags)
|
||||||
{
|
{
|
||||||
asm volatile (ALTERNATIVE("", "push %0; popf", X86_FEATURE_SMAP)
|
asm volatile ("# smap_restore\n\t"
|
||||||
|
ALTERNATIVE("jmp 1f", "", X86_FEATURE_SMAP)
|
||||||
|
"push %0; popf\n\t"
|
||||||
|
"1:"
|
||||||
: : "g" (flags) : "memory", "cc");
|
: : "g" (flags) : "memory", "cc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,32 +86,15 @@
|
||||||
UNWIND_HINT sp_offset=\sp_offset
|
UNWIND_HINT sp_offset=\sp_offset
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro UNWIND_HINT_SAVE
|
/*
|
||||||
UNWIND_HINT type=UNWIND_HINT_TYPE_SAVE
|
* RET_OFFSET: Used on instructions that terminate a function; mostly RETURN
|
||||||
|
* and sibling calls. On these, sp_offset denotes the expected offset from
|
||||||
|
* initial_func_cfi.
|
||||||
|
*/
|
||||||
|
.macro UNWIND_HINT_RET_OFFSET sp_offset=8
|
||||||
|
UNWIND_HINT type=UNWIND_HINT_TYPE_RET_OFFSET sp_offset=\sp_offset
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
.macro UNWIND_HINT_RESTORE
|
|
||||||
UNWIND_HINT type=UNWIND_HINT_TYPE_RESTORE
|
|
||||||
.endm
|
|
||||||
|
|
||||||
#else /* !__ASSEMBLY__ */
|
|
||||||
|
|
||||||
#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
|
|
||||||
"987: \n\t" \
|
|
||||||
".pushsection .discard.unwind_hints\n\t" \
|
|
||||||
/* struct unwind_hint */ \
|
|
||||||
".long 987b - .\n\t" \
|
|
||||||
".short " __stringify(sp_offset) "\n\t" \
|
|
||||||
".byte " __stringify(sp_reg) "\n\t" \
|
|
||||||
".byte " __stringify(type) "\n\t" \
|
|
||||||
".byte " __stringify(end) "\n\t" \
|
|
||||||
".balign 4 \n\t" \
|
|
||||||
".popsection\n\t"
|
|
||||||
|
|
||||||
#define UNWIND_HINT_SAVE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_SAVE, 0)
|
|
||||||
|
|
||||||
#define UNWIND_HINT_RESTORE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_RESTORE, 0)
|
|
||||||
|
|
||||||
#endif /* __ASSEMBLY__ */
|
#endif /* __ASSEMBLY__ */
|
||||||
|
|
||||||
#endif /* _ASM_X86_UNWIND_HINTS_H */
|
#endif /* _ASM_X86_UNWIND_HINTS_H */
|
||||||
|
|
|
@ -282,7 +282,8 @@ static inline void tramp_free(void *tramp) { }
|
||||||
|
|
||||||
/* Defined as markers to the end of the ftrace default trampolines */
|
/* Defined as markers to the end of the ftrace default trampolines */
|
||||||
extern void ftrace_regs_caller_end(void);
|
extern void ftrace_regs_caller_end(void);
|
||||||
extern void ftrace_epilogue(void);
|
extern void ftrace_regs_caller_ret(void);
|
||||||
|
extern void ftrace_caller_end(void);
|
||||||
extern void ftrace_caller_op_ptr(void);
|
extern void ftrace_caller_op_ptr(void);
|
||||||
extern void ftrace_regs_caller_op_ptr(void);
|
extern void ftrace_regs_caller_op_ptr(void);
|
||||||
|
|
||||||
|
@ -334,7 +335,7 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
|
||||||
call_offset = (unsigned long)ftrace_regs_call;
|
call_offset = (unsigned long)ftrace_regs_call;
|
||||||
} else {
|
} else {
|
||||||
start_offset = (unsigned long)ftrace_caller;
|
start_offset = (unsigned long)ftrace_caller;
|
||||||
end_offset = (unsigned long)ftrace_epilogue;
|
end_offset = (unsigned long)ftrace_caller_end;
|
||||||
op_offset = (unsigned long)ftrace_caller_op_ptr;
|
op_offset = (unsigned long)ftrace_caller_op_ptr;
|
||||||
call_offset = (unsigned long)ftrace_call;
|
call_offset = (unsigned long)ftrace_call;
|
||||||
}
|
}
|
||||||
|
@ -366,6 +367,13 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size)
|
||||||
if (WARN_ON(ret < 0))
|
if (WARN_ON(ret < 0))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
if (ops->flags & FTRACE_OPS_FL_SAVE_REGS) {
|
||||||
|
ip = trampoline + (ftrace_regs_caller_ret - ftrace_regs_caller);
|
||||||
|
ret = probe_kernel_read(ip, (void *)retq, RET_SIZE);
|
||||||
|
if (WARN_ON(ret < 0))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The address of the ftrace_ops that is used for this trampoline
|
* The address of the ftrace_ops that is used for this trampoline
|
||||||
* is stored at the end of the trampoline. This will be used to
|
* is stored at the end of the trampoline. This will be used to
|
||||||
|
@ -433,7 +441,7 @@ void set_ftrace_ops_ro(void)
|
||||||
end_offset = (unsigned long)ftrace_regs_caller_end;
|
end_offset = (unsigned long)ftrace_regs_caller_end;
|
||||||
} else {
|
} else {
|
||||||
start_offset = (unsigned long)ftrace_caller;
|
start_offset = (unsigned long)ftrace_caller;
|
||||||
end_offset = (unsigned long)ftrace_epilogue;
|
end_offset = (unsigned long)ftrace_caller_end;
|
||||||
}
|
}
|
||||||
size = end_offset - start_offset;
|
size = end_offset - start_offset;
|
||||||
size = size + RET_SIZE + sizeof(void *);
|
size = size + RET_SIZE + sizeof(void *);
|
||||||
|
|
|
@ -189,5 +189,5 @@ return_to_handler:
|
||||||
movl %eax, %ecx
|
movl %eax, %ecx
|
||||||
popl %edx
|
popl %edx
|
||||||
popl %eax
|
popl %eax
|
||||||
JMP_NOSPEC %ecx
|
JMP_NOSPEC ecx
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#endif /* CONFIG_FRAME_POINTER */
|
#endif /* CONFIG_FRAME_POINTER */
|
||||||
|
|
||||||
/* Size of stack used to save mcount regs in save_mcount_regs */
|
/* Size of stack used to save mcount regs in save_mcount_regs */
|
||||||
#define MCOUNT_REG_SIZE (SS+8 + MCOUNT_FRAME_SIZE)
|
#define MCOUNT_REG_SIZE (FRAME_SIZE + MCOUNT_FRAME_SIZE)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* gcc -pg option adds a call to 'mcount' in most functions.
|
* gcc -pg option adds a call to 'mcount' in most functions.
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
/*
|
/*
|
||||||
* We add enough stack to save all regs.
|
* We add enough stack to save all regs.
|
||||||
*/
|
*/
|
||||||
subq $(MCOUNT_REG_SIZE - MCOUNT_FRAME_SIZE), %rsp
|
subq $(FRAME_SIZE), %rsp
|
||||||
movq %rax, RAX(%rsp)
|
movq %rax, RAX(%rsp)
|
||||||
movq %rcx, RCX(%rsp)
|
movq %rcx, RCX(%rsp)
|
||||||
movq %rdx, RDX(%rsp)
|
movq %rdx, RDX(%rsp)
|
||||||
|
@ -157,8 +157,12 @@ SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
|
||||||
* think twice before adding any new code or changing the
|
* think twice before adding any new code or changing the
|
||||||
* layout here.
|
* layout here.
|
||||||
*/
|
*/
|
||||||
SYM_INNER_LABEL(ftrace_epilogue, SYM_L_GLOBAL)
|
SYM_INNER_LABEL(ftrace_caller_end, SYM_L_GLOBAL)
|
||||||
|
|
||||||
|
jmp ftrace_epilogue
|
||||||
|
SYM_FUNC_END(ftrace_caller);
|
||||||
|
|
||||||
|
SYM_FUNC_START(ftrace_epilogue)
|
||||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
|
SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
|
||||||
jmp ftrace_stub
|
jmp ftrace_stub
|
||||||
|
@ -170,14 +174,12 @@ SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
|
||||||
*/
|
*/
|
||||||
SYM_INNER_LABEL_ALIGN(ftrace_stub, SYM_L_WEAK)
|
SYM_INNER_LABEL_ALIGN(ftrace_stub, SYM_L_WEAK)
|
||||||
retq
|
retq
|
||||||
SYM_FUNC_END(ftrace_caller)
|
SYM_FUNC_END(ftrace_epilogue)
|
||||||
|
|
||||||
SYM_FUNC_START(ftrace_regs_caller)
|
SYM_FUNC_START(ftrace_regs_caller)
|
||||||
/* Save the current flags before any operations that can change them */
|
/* Save the current flags before any operations that can change them */
|
||||||
pushfq
|
pushfq
|
||||||
|
|
||||||
UNWIND_HINT_SAVE
|
|
||||||
|
|
||||||
/* added 8 bytes to save flags */
|
/* added 8 bytes to save flags */
|
||||||
save_mcount_regs 8
|
save_mcount_regs 8
|
||||||
/* save_mcount_regs fills in first two parameters */
|
/* save_mcount_regs fills in first two parameters */
|
||||||
|
@ -233,10 +235,13 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
|
||||||
movq ORIG_RAX(%rsp), %rax
|
movq ORIG_RAX(%rsp), %rax
|
||||||
movq %rax, MCOUNT_REG_SIZE-8(%rsp)
|
movq %rax, MCOUNT_REG_SIZE-8(%rsp)
|
||||||
|
|
||||||
/* If ORIG_RAX is anything but zero, make this a call to that */
|
/*
|
||||||
|
* If ORIG_RAX is anything but zero, make this a call to that.
|
||||||
|
* See arch_ftrace_set_direct_caller().
|
||||||
|
*/
|
||||||
movq ORIG_RAX(%rsp), %rax
|
movq ORIG_RAX(%rsp), %rax
|
||||||
cmpq $0, %rax
|
testq %rax, %rax
|
||||||
je 1f
|
jz 1f
|
||||||
|
|
||||||
/* Swap the flags with orig_rax */
|
/* Swap the flags with orig_rax */
|
||||||
movq MCOUNT_REG_SIZE(%rsp), %rdi
|
movq MCOUNT_REG_SIZE(%rsp), %rdi
|
||||||
|
@ -244,20 +249,14 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
|
||||||
movq %rax, MCOUNT_REG_SIZE(%rsp)
|
movq %rax, MCOUNT_REG_SIZE(%rsp)
|
||||||
|
|
||||||
restore_mcount_regs 8
|
restore_mcount_regs 8
|
||||||
|
/* Restore flags */
|
||||||
|
popfq
|
||||||
|
|
||||||
jmp 2f
|
SYM_INNER_LABEL(ftrace_regs_caller_ret, SYM_L_GLOBAL);
|
||||||
|
UNWIND_HINT_RET_OFFSET
|
||||||
|
jmp ftrace_epilogue
|
||||||
|
|
||||||
1: restore_mcount_regs
|
1: restore_mcount_regs
|
||||||
|
|
||||||
|
|
||||||
2:
|
|
||||||
/*
|
|
||||||
* The stack layout is nondetermistic here, depending on which path was
|
|
||||||
* taken. This confuses objtool and ORC, rightfully so. For now,
|
|
||||||
* pretend the stack always looks like the non-direct case.
|
|
||||||
*/
|
|
||||||
UNWIND_HINT_RESTORE
|
|
||||||
|
|
||||||
/* Restore flags */
|
/* Restore flags */
|
||||||
popfq
|
popfq
|
||||||
|
|
||||||
|
@ -268,7 +267,6 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
|
||||||
* to the return.
|
* to the return.
|
||||||
*/
|
*/
|
||||||
SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL)
|
SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL)
|
||||||
|
|
||||||
jmp ftrace_epilogue
|
jmp ftrace_epilogue
|
||||||
|
|
||||||
SYM_FUNC_END(ftrace_regs_caller)
|
SYM_FUNC_END(ftrace_regs_caller)
|
||||||
|
@ -303,7 +301,7 @@ trace:
|
||||||
* function tracing is enabled.
|
* function tracing is enabled.
|
||||||
*/
|
*/
|
||||||
movq ftrace_trace_function, %r8
|
movq ftrace_trace_function, %r8
|
||||||
CALL_NOSPEC %r8
|
CALL_NOSPEC r8
|
||||||
restore_mcount_regs
|
restore_mcount_regs
|
||||||
|
|
||||||
jmp fgraph_trace
|
jmp fgraph_trace
|
||||||
|
@ -340,6 +338,6 @@ SYM_CODE_START(return_to_handler)
|
||||||
movq 8(%rsp), %rdx
|
movq 8(%rsp), %rdx
|
||||||
movq (%rsp), %rax
|
movq (%rsp), %rax
|
||||||
addq $24, %rsp
|
addq $24, %rsp
|
||||||
JMP_NOSPEC %rdi
|
JMP_NOSPEC rdi
|
||||||
SYM_CODE_END(return_to_handler)
|
SYM_CODE_END(return_to_handler)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -153,7 +153,7 @@ SYM_FUNC_START(csum_partial)
|
||||||
negl %ebx
|
negl %ebx
|
||||||
lea 45f(%ebx,%ebx,2), %ebx
|
lea 45f(%ebx,%ebx,2), %ebx
|
||||||
testl %esi, %esi
|
testl %esi, %esi
|
||||||
JMP_NOSPEC %ebx
|
JMP_NOSPEC ebx
|
||||||
|
|
||||||
# Handle 2-byte-aligned regions
|
# Handle 2-byte-aligned regions
|
||||||
20: addw (%esi), %ax
|
20: addw (%esi), %ax
|
||||||
|
@ -436,7 +436,7 @@ SYM_FUNC_START(csum_partial_copy_generic)
|
||||||
andl $-32,%edx
|
andl $-32,%edx
|
||||||
lea 3f(%ebx,%ebx), %ebx
|
lea 3f(%ebx,%ebx), %ebx
|
||||||
testl %esi, %esi
|
testl %esi, %esi
|
||||||
JMP_NOSPEC %ebx
|
JMP_NOSPEC ebx
|
||||||
1: addl $64,%esi
|
1: addl $64,%esi
|
||||||
addl $64,%edi
|
addl $64,%edi
|
||||||
SRC(movb -32(%edx),%bl) ; SRC(movb (%edx),%bl)
|
SRC(movb -32(%edx),%bl) ; SRC(movb (%edx),%bl)
|
||||||
|
|
|
@ -7,15 +7,31 @@
|
||||||
#include <asm/alternative-asm.h>
|
#include <asm/alternative-asm.h>
|
||||||
#include <asm/export.h>
|
#include <asm/export.h>
|
||||||
#include <asm/nospec-branch.h>
|
#include <asm/nospec-branch.h>
|
||||||
|
#include <asm/unwind_hints.h>
|
||||||
|
#include <asm/frame.h>
|
||||||
|
|
||||||
.macro THUNK reg
|
.macro THUNK reg
|
||||||
.section .text.__x86.indirect_thunk
|
.section .text.__x86.indirect_thunk
|
||||||
|
|
||||||
|
.align 32
|
||||||
SYM_FUNC_START(__x86_indirect_thunk_\reg)
|
SYM_FUNC_START(__x86_indirect_thunk_\reg)
|
||||||
CFI_STARTPROC
|
JMP_NOSPEC \reg
|
||||||
JMP_NOSPEC %\reg
|
|
||||||
CFI_ENDPROC
|
|
||||||
SYM_FUNC_END(__x86_indirect_thunk_\reg)
|
SYM_FUNC_END(__x86_indirect_thunk_\reg)
|
||||||
|
|
||||||
|
SYM_FUNC_START_NOALIGN(__x86_retpoline_\reg)
|
||||||
|
ANNOTATE_INTRA_FUNCTION_CALL
|
||||||
|
call .Ldo_rop_\@
|
||||||
|
.Lspec_trap_\@:
|
||||||
|
UNWIND_HINT_EMPTY
|
||||||
|
pause
|
||||||
|
lfence
|
||||||
|
jmp .Lspec_trap_\@
|
||||||
|
.Ldo_rop_\@:
|
||||||
|
mov %\reg, (%_ASM_SP)
|
||||||
|
UNWIND_HINT_RET_OFFSET
|
||||||
|
ret
|
||||||
|
SYM_FUNC_END(__x86_retpoline_\reg)
|
||||||
|
|
||||||
.endm
|
.endm
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -24,25 +40,24 @@ SYM_FUNC_END(__x86_indirect_thunk_\reg)
|
||||||
* only see one instance of "__x86_indirect_thunk_\reg" rather
|
* only see one instance of "__x86_indirect_thunk_\reg" rather
|
||||||
* than one per register with the correct names. So we do it
|
* than one per register with the correct names. So we do it
|
||||||
* the simple and nasty way...
|
* the simple and nasty way...
|
||||||
|
*
|
||||||
|
* Worse, you can only have a single EXPORT_SYMBOL per line,
|
||||||
|
* and CPP can't insert newlines, so we have to repeat everything
|
||||||
|
* at least twice.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define __EXPORT_THUNK(sym) _ASM_NOKPROBE(sym); EXPORT_SYMBOL(sym)
|
#define __EXPORT_THUNK(sym) _ASM_NOKPROBE(sym); EXPORT_SYMBOL(sym)
|
||||||
#define EXPORT_THUNK(reg) __EXPORT_THUNK(__x86_indirect_thunk_ ## reg)
|
#define EXPORT_THUNK(reg) __EXPORT_THUNK(__x86_indirect_thunk_ ## reg)
|
||||||
#define GENERATE_THUNK(reg) THUNK reg ; EXPORT_THUNK(reg)
|
#define EXPORT_RETPOLINE(reg) __EXPORT_THUNK(__x86_retpoline_ ## reg)
|
||||||
|
|
||||||
GENERATE_THUNK(_ASM_AX)
|
#undef GEN
|
||||||
GENERATE_THUNK(_ASM_BX)
|
#define GEN(reg) THUNK reg
|
||||||
GENERATE_THUNK(_ASM_CX)
|
#include <asm/GEN-for-each-reg.h>
|
||||||
GENERATE_THUNK(_ASM_DX)
|
|
||||||
GENERATE_THUNK(_ASM_SI)
|
#undef GEN
|
||||||
GENERATE_THUNK(_ASM_DI)
|
#define GEN(reg) EXPORT_THUNK(reg)
|
||||||
GENERATE_THUNK(_ASM_BP)
|
#include <asm/GEN-for-each-reg.h>
|
||||||
#ifdef CONFIG_64BIT
|
|
||||||
GENERATE_THUNK(r8)
|
#undef GEN
|
||||||
GENERATE_THUNK(r9)
|
#define GEN(reg) EXPORT_RETPOLINE(reg)
|
||||||
GENERATE_THUNK(r10)
|
#include <asm/GEN-for-each-reg.h>
|
||||||
GENERATE_THUNK(r11)
|
|
||||||
GENERATE_THUNK(r12)
|
|
||||||
GENERATE_THUNK(r13)
|
|
||||||
GENERATE_THUNK(r14)
|
|
||||||
GENERATE_THUNK(r15)
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ SYM_FUNC_START(__efi_call)
|
||||||
mov %r8, %r9
|
mov %r8, %r9
|
||||||
mov %rcx, %r8
|
mov %rcx, %r8
|
||||||
mov %rsi, %rcx
|
mov %rsi, %rcx
|
||||||
CALL_NOSPEC %rdi
|
CALL_NOSPEC rdi
|
||||||
leave
|
leave
|
||||||
ret
|
ret
|
||||||
SYM_FUNC_END(__efi_call)
|
SYM_FUNC_END(__efi_call)
|
||||||
|
|
|
@ -15,9 +15,20 @@
|
||||||
static void __used __section(.discard.func_stack_frame_non_standard) \
|
static void __used __section(.discard.func_stack_frame_non_standard) \
|
||||||
*__func_stack_frame_non_standard_##func = func
|
*__func_stack_frame_non_standard_##func = func
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This macro indicates that the following intra-function call is valid.
|
||||||
|
* Any non-annotated intra-function call will cause objtool to issue a warning.
|
||||||
|
*/
|
||||||
|
#define ANNOTATE_INTRA_FUNCTION_CALL \
|
||||||
|
999: \
|
||||||
|
.pushsection .discard.intra_function_calls; \
|
||||||
|
.long 999b; \
|
||||||
|
.popsection;
|
||||||
|
|
||||||
#else /* !CONFIG_STACK_VALIDATION */
|
#else /* !CONFIG_STACK_VALIDATION */
|
||||||
|
|
||||||
#define STACK_FRAME_NON_STANDARD(func)
|
#define STACK_FRAME_NON_STANDARD(func)
|
||||||
|
#define ANNOTATE_INTRA_FUNCTION_CALL
|
||||||
|
|
||||||
#endif /* CONFIG_STACK_VALIDATION */
|
#endif /* CONFIG_STACK_VALIDATION */
|
||||||
|
|
||||||
|
|
|
@ -369,6 +369,11 @@ config STACK_VALIDATION
|
||||||
For more information, see
|
For more information, see
|
||||||
tools/objtool/Documentation/stack-validation.txt.
|
tools/objtool/Documentation/stack-validation.txt.
|
||||||
|
|
||||||
|
config VMLINUX_VALIDATION
|
||||||
|
bool
|
||||||
|
depends on STACK_VALIDATION && DEBUG_ENTRY && !PARAVIRT
|
||||||
|
default y
|
||||||
|
|
||||||
config DEBUG_FORCE_WEAK_PER_CPU
|
config DEBUG_FORCE_WEAK_PER_CPU
|
||||||
bool "Force weak per-cpu definitions"
|
bool "Force weak per-cpu definitions"
|
||||||
depends on DEBUG_KERNEL
|
depends on DEBUG_KERNEL
|
||||||
|
|
|
@ -20,18 +20,22 @@ static unsigned long my_ip = (unsigned long)schedule;
|
||||||
|
|
||||||
asm (
|
asm (
|
||||||
" .pushsection .text, \"ax\", @progbits\n"
|
" .pushsection .text, \"ax\", @progbits\n"
|
||||||
|
" .type my_tramp1, @function\n"
|
||||||
" my_tramp1:"
|
" my_tramp1:"
|
||||||
" pushq %rbp\n"
|
" pushq %rbp\n"
|
||||||
" movq %rsp, %rbp\n"
|
" movq %rsp, %rbp\n"
|
||||||
" call my_direct_func1\n"
|
" call my_direct_func1\n"
|
||||||
" leave\n"
|
" leave\n"
|
||||||
|
" .size my_tramp1, .-my_tramp1\n"
|
||||||
" ret\n"
|
" ret\n"
|
||||||
|
" .type my_tramp2, @function\n"
|
||||||
" my_tramp2:"
|
" my_tramp2:"
|
||||||
" pushq %rbp\n"
|
" pushq %rbp\n"
|
||||||
" movq %rsp, %rbp\n"
|
" movq %rsp, %rbp\n"
|
||||||
" call my_direct_func2\n"
|
" call my_direct_func2\n"
|
||||||
" leave\n"
|
" leave\n"
|
||||||
" ret\n"
|
" ret\n"
|
||||||
|
" .size my_tramp2, .-my_tramp2\n"
|
||||||
" .popsection\n"
|
" .popsection\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ extern void my_tramp(void *);
|
||||||
|
|
||||||
asm (
|
asm (
|
||||||
" .pushsection .text, \"ax\", @progbits\n"
|
" .pushsection .text, \"ax\", @progbits\n"
|
||||||
|
" .type my_tramp, @function\n"
|
||||||
" my_tramp:"
|
" my_tramp:"
|
||||||
" pushq %rbp\n"
|
" pushq %rbp\n"
|
||||||
" movq %rsp, %rbp\n"
|
" movq %rsp, %rbp\n"
|
||||||
|
@ -27,6 +28,7 @@ asm (
|
||||||
" popq %rdi\n"
|
" popq %rdi\n"
|
||||||
" leave\n"
|
" leave\n"
|
||||||
" ret\n"
|
" ret\n"
|
||||||
|
" .size my_tramp, .-my_tramp\n"
|
||||||
" .popsection\n"
|
" .popsection\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ extern void my_tramp(void *);
|
||||||
|
|
||||||
asm (
|
asm (
|
||||||
" .pushsection .text, \"ax\", @progbits\n"
|
" .pushsection .text, \"ax\", @progbits\n"
|
||||||
|
" .type my_tramp, @function\n"
|
||||||
" my_tramp:"
|
" my_tramp:"
|
||||||
" pushq %rbp\n"
|
" pushq %rbp\n"
|
||||||
" movq %rsp, %rbp\n"
|
" movq %rsp, %rbp\n"
|
||||||
|
@ -21,6 +22,7 @@ asm (
|
||||||
" popq %rdi\n"
|
" popq %rdi\n"
|
||||||
" leave\n"
|
" leave\n"
|
||||||
" ret\n"
|
" ret\n"
|
||||||
|
" .size my_tramp, .-my_tramp\n"
|
||||||
" .popsection\n"
|
" .popsection\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,29 @@ modpost_link()
|
||||||
${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${objects}
|
${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${objects}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
objtool_link()
|
||||||
|
{
|
||||||
|
local objtoolopt;
|
||||||
|
|
||||||
|
if [ -n "${CONFIG_VMLINUX_VALIDATION}" ]; then
|
||||||
|
objtoolopt="check"
|
||||||
|
if [ -z "${CONFIG_FRAME_POINTER}" ]; then
|
||||||
|
objtoolopt="${objtoolopt} --no-fp"
|
||||||
|
fi
|
||||||
|
if [ -n "${CONFIG_GCOV_KERNEL}" ]; then
|
||||||
|
objtoolopt="${objtoolopt} --no-unreachable"
|
||||||
|
fi
|
||||||
|
if [ -n "${CONFIG_RETPOLINE}" ]; then
|
||||||
|
objtoolopt="${objtoolopt} --retpoline"
|
||||||
|
fi
|
||||||
|
if [ -n "${CONFIG_X86_SMAP}" ]; then
|
||||||
|
objtoolopt="${objtoolopt} --uaccess"
|
||||||
|
fi
|
||||||
|
info OBJTOOL ${1}
|
||||||
|
tools/objtool/objtool ${objtoolopt} ${1}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Link of vmlinux
|
# Link of vmlinux
|
||||||
# ${1} - output file
|
# ${1} - output file
|
||||||
# ${2}, ${3}, ... - optional extra .o files
|
# ${2}, ${3}, ... - optional extra .o files
|
||||||
|
@ -251,6 +274,7 @@ ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init need-builtin=1
|
||||||
#link vmlinux.o
|
#link vmlinux.o
|
||||||
info LD vmlinux.o
|
info LD vmlinux.o
|
||||||
modpost_link vmlinux.o
|
modpost_link vmlinux.o
|
||||||
|
objtool_link vmlinux.o
|
||||||
|
|
||||||
# modpost vmlinux.o to check for section mismatches
|
# modpost vmlinux.o to check for section mismatches
|
||||||
${MAKE} -f "${srctree}/scripts/Makefile.modpost" MODPOST_VMLINUX=1
|
${MAKE} -f "${srctree}/scripts/Makefile.modpost" MODPOST_VMLINUX=1
|
||||||
|
|
|
@ -58,8 +58,7 @@
|
||||||
#define ORC_TYPE_CALL 0
|
#define ORC_TYPE_CALL 0
|
||||||
#define ORC_TYPE_REGS 1
|
#define ORC_TYPE_REGS 1
|
||||||
#define ORC_TYPE_REGS_IRET 2
|
#define ORC_TYPE_REGS_IRET 2
|
||||||
#define UNWIND_HINT_TYPE_SAVE 3
|
#define UNWIND_HINT_TYPE_RET_OFFSET 3
|
||||||
#define UNWIND_HINT_TYPE_RESTORE 4
|
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
objtool-y += arch/$(SRCARCH)/
|
objtool-y += arch/$(SRCARCH)/
|
||||||
|
|
||||||
|
objtool-y += weak.o
|
||||||
|
|
||||||
|
objtool-$(SUBCMD_CHECK) += check.o
|
||||||
|
objtool-$(SUBCMD_CHECK) += special.o
|
||||||
|
objtool-$(SUBCMD_ORC) += check.o
|
||||||
|
objtool-$(SUBCMD_ORC) += orc_gen.o
|
||||||
|
objtool-$(SUBCMD_ORC) += orc_dump.o
|
||||||
|
|
||||||
objtool-y += builtin-check.o
|
objtool-y += builtin-check.o
|
||||||
objtool-y += builtin-orc.o
|
objtool-y += builtin-orc.o
|
||||||
objtool-y += check.o
|
|
||||||
objtool-y += orc_gen.o
|
|
||||||
objtool-y += orc_dump.o
|
|
||||||
objtool-y += elf.o
|
objtool-y += elf.o
|
||||||
objtool-y += special.o
|
|
||||||
objtool-y += objtool.o
|
objtool-y += objtool.o
|
||||||
|
|
||||||
objtool-y += libstring.o
|
objtool-y += libstring.o
|
||||||
|
|
|
@ -289,6 +289,47 @@ they mean, and suggestions for how to fix them.
|
||||||
might be corrupt due to a gcc bug. For more details, see:
|
might be corrupt due to a gcc bug. For more details, see:
|
||||||
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646
|
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646
|
||||||
|
|
||||||
|
9. file.o: warning: objtool: funcA() call to funcB() with UACCESS enabled
|
||||||
|
|
||||||
|
This means that an unexpected call to a non-whitelisted function exists
|
||||||
|
outside of arch-specific guards.
|
||||||
|
X86: SMAP (stac/clac): __uaccess_begin()/__uaccess_end()
|
||||||
|
ARM: PAN: uaccess_enable()/uaccess_disable()
|
||||||
|
|
||||||
|
These functions should be called to denote a minimal critical section around
|
||||||
|
access to __user variables. See also: https://lwn.net/Articles/517475/
|
||||||
|
|
||||||
|
The intention of the warning is to prevent calls to funcB() from eventually
|
||||||
|
calling schedule(), potentially leaking the AC flags state, and not
|
||||||
|
restoring them correctly.
|
||||||
|
|
||||||
|
It also helps verify that there are no unexpected calls to funcB() which may
|
||||||
|
access user space pages with protections against doing so disabled.
|
||||||
|
|
||||||
|
To fix, either:
|
||||||
|
1) remove explicit calls to funcB() from funcA().
|
||||||
|
2) add the correct guards before and after calls to low level functions like
|
||||||
|
__get_user_size()/__put_user_size().
|
||||||
|
3) add funcB to uaccess_safe_builtin whitelist in tools/objtool/check.c, if
|
||||||
|
funcB obviously does not call schedule(), and is marked notrace (since
|
||||||
|
function tracing inserts additional calls, which is not obvious from the
|
||||||
|
sources).
|
||||||
|
|
||||||
|
10. file.o: warning: func()+0x5c: alternative modifies stack
|
||||||
|
|
||||||
|
This means that an alternative includes instructions that modify the
|
||||||
|
stack. The problem is that there is only one ORC unwind table, this means
|
||||||
|
that the ORC unwind entries must be valid for each of the alternatives.
|
||||||
|
The easiest way to enforce this is to ensure alternatives do not contain
|
||||||
|
any ORC entries, which in turn implies the above constraint.
|
||||||
|
|
||||||
|
11. file.o: warning: unannotated intra-function call
|
||||||
|
|
||||||
|
This warning means that a direct call is done to a destination which
|
||||||
|
is not at the beginning of a function. If this is a legit call, you
|
||||||
|
can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL
|
||||||
|
directive right before the call.
|
||||||
|
|
||||||
|
|
||||||
If the error doesn't seem to make sense, it could be a bug in objtool.
|
If the error doesn't seem to make sense, it could be a bug in objtool.
|
||||||
Feel free to ask the objtool maintainer for help.
|
Feel free to ask the objtool maintainer for help.
|
||||||
|
|
|
@ -35,7 +35,8 @@ all: $(OBJTOOL)
|
||||||
|
|
||||||
INCLUDES := -I$(srctree)/tools/include \
|
INCLUDES := -I$(srctree)/tools/include \
|
||||||
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
|
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
|
||||||
-I$(srctree)/tools/arch/$(SRCARCH)/include
|
-I$(srctree)/tools/arch/$(SRCARCH)/include \
|
||||||
|
-I$(srctree)/tools/objtool/arch/$(SRCARCH)/include
|
||||||
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed
|
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed
|
||||||
CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
|
CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
|
||||||
LDFLAGS += $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
|
LDFLAGS += $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
|
||||||
|
@ -45,14 +46,24 @@ elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(CC) $(CFLAGS) -x c -E -
|
||||||
CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
|
CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
|
||||||
|
|
||||||
AWK = awk
|
AWK = awk
|
||||||
|
|
||||||
|
SUBCMD_CHECK := n
|
||||||
|
SUBCMD_ORC := n
|
||||||
|
|
||||||
|
ifeq ($(SRCARCH),x86)
|
||||||
|
SUBCMD_CHECK := y
|
||||||
|
SUBCMD_ORC := y
|
||||||
|
endif
|
||||||
|
|
||||||
|
export SUBCMD_CHECK SUBCMD_ORC
|
||||||
export srctree OUTPUT CFLAGS SRCARCH AWK
|
export srctree OUTPUT CFLAGS SRCARCH AWK
|
||||||
include $(srctree)/tools/build/Makefile.include
|
include $(srctree)/tools/build/Makefile.include
|
||||||
|
|
||||||
$(OBJTOOL_IN): fixdep FORCE
|
$(OBJTOOL_IN): fixdep FORCE
|
||||||
|
@$(CONFIG_SHELL) ./sync-check.sh
|
||||||
@$(MAKE) $(build)=objtool
|
@$(MAKE) $(build)=objtool
|
||||||
|
|
||||||
$(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
|
$(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
|
||||||
@$(CONFIG_SHELL) ./sync-check.sh
|
|
||||||
$(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
|
$(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include "elf.h"
|
#include "objtool.h"
|
||||||
#include "cfi.h"
|
#include "cfi.h"
|
||||||
|
|
||||||
|
#include <asm/orc_types.h>
|
||||||
|
|
||||||
enum insn_type {
|
enum insn_type {
|
||||||
INSN_JUMP_CONDITIONAL,
|
INSN_JUMP_CONDITIONAL,
|
||||||
INSN_JUMP_UNCONDITIONAL,
|
INSN_JUMP_UNCONDITIONAL,
|
||||||
|
@ -20,7 +22,6 @@ enum insn_type {
|
||||||
INSN_CALL_DYNAMIC,
|
INSN_CALL_DYNAMIC,
|
||||||
INSN_RETURN,
|
INSN_RETURN,
|
||||||
INSN_CONTEXT_SWITCH,
|
INSN_CONTEXT_SWITCH,
|
||||||
INSN_STACK,
|
|
||||||
INSN_BUG,
|
INSN_BUG,
|
||||||
INSN_NOP,
|
INSN_NOP,
|
||||||
INSN_STAC,
|
INSN_STAC,
|
||||||
|
@ -64,15 +65,23 @@ struct op_src {
|
||||||
struct stack_op {
|
struct stack_op {
|
||||||
struct op_dest dest;
|
struct op_dest dest;
|
||||||
struct op_src src;
|
struct op_src src;
|
||||||
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
void arch_initial_func_cfi_state(struct cfi_state *state);
|
struct instruction;
|
||||||
|
|
||||||
int arch_decode_instruction(struct elf *elf, struct section *sec,
|
void arch_initial_func_cfi_state(struct cfi_init_state *state);
|
||||||
|
|
||||||
|
int arch_decode_instruction(const struct elf *elf, const struct section *sec,
|
||||||
unsigned long offset, unsigned int maxlen,
|
unsigned long offset, unsigned int maxlen,
|
||||||
unsigned int *len, enum insn_type *type,
|
unsigned int *len, enum insn_type *type,
|
||||||
unsigned long *immediate, struct stack_op *op);
|
unsigned long *immediate,
|
||||||
|
struct list_head *ops_list);
|
||||||
|
|
||||||
bool arch_callee_saved_reg(unsigned char reg);
|
bool arch_callee_saved_reg(unsigned char reg);
|
||||||
|
|
||||||
|
unsigned long arch_jump_destination(struct instruction *insn);
|
||||||
|
|
||||||
|
unsigned long arch_dest_rela_offset(int addend);
|
||||||
|
|
||||||
#endif /* _ARCH_H */
|
#endif /* _ARCH_H */
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "../../../arch/x86/lib/inat.c"
|
#include "../../../arch/x86/lib/inat.c"
|
||||||
#include "../../../arch/x86/lib/insn.c"
|
#include "../../../arch/x86/lib/insn.c"
|
||||||
|
|
||||||
|
#include "../../check.h"
|
||||||
#include "../../elf.h"
|
#include "../../elf.h"
|
||||||
#include "../../arch.h"
|
#include "../../arch.h"
|
||||||
#include "../../warn.h"
|
#include "../../warn.h"
|
||||||
|
@ -26,7 +27,7 @@ static unsigned char op_to_cfi_reg[][2] = {
|
||||||
{CFI_DI, CFI_R15},
|
{CFI_DI, CFI_R15},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int is_x86_64(struct elf *elf)
|
static int is_x86_64(const struct elf *elf)
|
||||||
{
|
{
|
||||||
switch (elf->ehdr.e_machine) {
|
switch (elf->ehdr.e_machine) {
|
||||||
case EM_X86_64:
|
case EM_X86_64:
|
||||||
|
@ -66,16 +67,34 @@ bool arch_callee_saved_reg(unsigned char reg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int arch_decode_instruction(struct elf *elf, struct section *sec,
|
unsigned long arch_dest_rela_offset(int addend)
|
||||||
|
{
|
||||||
|
return addend + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long arch_jump_destination(struct instruction *insn)
|
||||||
|
{
|
||||||
|
return insn->offset + insn->len + insn->immediate;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ADD_OP(op) \
|
||||||
|
if (!(op = calloc(1, sizeof(*op)))) \
|
||||||
|
return -1; \
|
||||||
|
else for (list_add_tail(&op->list, ops_list); op; op = NULL)
|
||||||
|
|
||||||
|
int arch_decode_instruction(const struct elf *elf, const struct section *sec,
|
||||||
unsigned long offset, unsigned int maxlen,
|
unsigned long offset, unsigned int maxlen,
|
||||||
unsigned int *len, enum insn_type *type,
|
unsigned int *len, enum insn_type *type,
|
||||||
unsigned long *immediate, struct stack_op *op)
|
unsigned long *immediate,
|
||||||
|
struct list_head *ops_list)
|
||||||
{
|
{
|
||||||
struct insn insn;
|
struct insn insn;
|
||||||
int x86_64, sign;
|
int x86_64, sign;
|
||||||
unsigned char op1, op2, rex = 0, rex_b = 0, rex_r = 0, rex_w = 0,
|
unsigned char op1, op2, rex = 0, rex_b = 0, rex_r = 0, rex_w = 0,
|
||||||
rex_x = 0, modrm = 0, modrm_mod = 0, modrm_rm = 0,
|
rex_x = 0, modrm = 0, modrm_mod = 0, modrm_rm = 0,
|
||||||
modrm_reg = 0, sib = 0;
|
modrm_reg = 0, sib = 0;
|
||||||
|
struct stack_op *op = NULL;
|
||||||
|
struct symbol *sym;
|
||||||
|
|
||||||
x86_64 = is_x86_64(elf);
|
x86_64 = is_x86_64(elf);
|
||||||
if (x86_64 == -1)
|
if (x86_64 == -1)
|
||||||
|
@ -85,7 +104,7 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
insn_get_length(&insn);
|
insn_get_length(&insn);
|
||||||
|
|
||||||
if (!insn_complete(&insn)) {
|
if (!insn_complete(&insn)) {
|
||||||
WARN_FUNC("can't decode instruction", sec, offset);
|
WARN("can't decode instruction at %s:0x%lx", sec->name, offset);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,40 +142,44 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) {
|
if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) {
|
||||||
|
|
||||||
/* add/sub reg, %rsp */
|
/* add/sub reg, %rsp */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_ADD;
|
op->src.type = OP_SRC_ADD;
|
||||||
op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = CFI_SP;
|
op->dest.reg = CFI_SP;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x50 ... 0x57:
|
case 0x50 ... 0x57:
|
||||||
|
|
||||||
/* push reg */
|
/* push reg */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_REG;
|
op->src.type = OP_SRC_REG;
|
||||||
op->src.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
|
op->src.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
|
||||||
op->dest.type = OP_DEST_PUSH;
|
op->dest.type = OP_DEST_PUSH;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x58 ... 0x5f:
|
case 0x58 ... 0x5f:
|
||||||
|
|
||||||
/* pop reg */
|
/* pop reg */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_POP;
|
op->src.type = OP_SRC_POP;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
|
op->dest.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x68:
|
case 0x68:
|
||||||
case 0x6a:
|
case 0x6a:
|
||||||
/* push immediate */
|
/* push immediate */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_CONST;
|
op->src.type = OP_SRC_CONST;
|
||||||
op->dest.type = OP_DEST_PUSH;
|
op->dest.type = OP_DEST_PUSH;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x70 ... 0x7f:
|
case 0x70 ... 0x7f:
|
||||||
|
@ -170,12 +193,13 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
|
|
||||||
if (modrm == 0xe4) {
|
if (modrm == 0xe4) {
|
||||||
/* and imm, %rsp */
|
/* and imm, %rsp */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_AND;
|
op->src.type = OP_SRC_AND;
|
||||||
op->src.reg = CFI_SP;
|
op->src.reg = CFI_SP;
|
||||||
op->src.offset = insn.immediate.value;
|
op->src.offset = insn.immediate.value;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = CFI_SP;
|
op->dest.reg = CFI_SP;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,34 +211,37 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* add/sub imm, %rsp */
|
/* add/sub imm, %rsp */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_ADD;
|
op->src.type = OP_SRC_ADD;
|
||||||
op->src.reg = CFI_SP;
|
op->src.reg = CFI_SP;
|
||||||
op->src.offset = insn.immediate.value * sign;
|
op->src.offset = insn.immediate.value * sign;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = CFI_SP;
|
op->dest.reg = CFI_SP;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x89:
|
case 0x89:
|
||||||
if (rex_w && !rex_r && modrm_mod == 3 && modrm_reg == 4) {
|
if (rex_w && !rex_r && modrm_mod == 3 && modrm_reg == 4) {
|
||||||
|
|
||||||
/* mov %rsp, reg */
|
/* mov %rsp, reg */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_REG;
|
op->src.type = OP_SRC_REG;
|
||||||
op->src.reg = CFI_SP;
|
op->src.reg = CFI_SP;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b];
|
op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b];
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) {
|
if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) {
|
||||||
|
|
||||||
/* mov reg, %rsp */
|
/* mov reg, %rsp */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_REG;
|
op->src.type = OP_SRC_REG;
|
||||||
op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = CFI_SP;
|
op->dest.reg = CFI_SP;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,23 +251,25 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
(modrm_mod == 1 || modrm_mod == 2) && modrm_rm == 5) {
|
(modrm_mod == 1 || modrm_mod == 2) && modrm_rm == 5) {
|
||||||
|
|
||||||
/* mov reg, disp(%rbp) */
|
/* mov reg, disp(%rbp) */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_REG;
|
op->src.type = OP_SRC_REG;
|
||||||
op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
||||||
op->dest.type = OP_DEST_REG_INDIRECT;
|
op->dest.type = OP_DEST_REG_INDIRECT;
|
||||||
op->dest.reg = CFI_BP;
|
op->dest.reg = CFI_BP;
|
||||||
op->dest.offset = insn.displacement.value;
|
op->dest.offset = insn.displacement.value;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) {
|
} else if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) {
|
||||||
|
|
||||||
/* mov reg, disp(%rsp) */
|
/* mov reg, disp(%rsp) */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_REG;
|
op->src.type = OP_SRC_REG;
|
||||||
op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
||||||
op->dest.type = OP_DEST_REG_INDIRECT;
|
op->dest.type = OP_DEST_REG_INDIRECT;
|
||||||
op->dest.reg = CFI_SP;
|
op->dest.reg = CFI_SP;
|
||||||
op->dest.offset = insn.displacement.value;
|
op->dest.offset = insn.displacement.value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -248,31 +277,33 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
if (rex_w && !rex_b && modrm_mod == 1 && modrm_rm == 5) {
|
if (rex_w && !rex_b && modrm_mod == 1 && modrm_rm == 5) {
|
||||||
|
|
||||||
/* mov disp(%rbp), reg */
|
/* mov disp(%rbp), reg */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_REG_INDIRECT;
|
op->src.type = OP_SRC_REG_INDIRECT;
|
||||||
op->src.reg = CFI_BP;
|
op->src.reg = CFI_BP;
|
||||||
op->src.offset = insn.displacement.value;
|
op->src.offset = insn.displacement.value;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
||||||
|
}
|
||||||
|
|
||||||
} else if (rex_w && !rex_b && sib == 0x24 &&
|
} else if (rex_w && !rex_b && sib == 0x24 &&
|
||||||
modrm_mod != 3 && modrm_rm == 4) {
|
modrm_mod != 3 && modrm_rm == 4) {
|
||||||
|
|
||||||
/* mov disp(%rsp), reg */
|
/* mov disp(%rsp), reg */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_REG_INDIRECT;
|
op->src.type = OP_SRC_REG_INDIRECT;
|
||||||
op->src.reg = CFI_SP;
|
op->src.reg = CFI_SP;
|
||||||
op->src.offset = insn.displacement.value;
|
op->src.offset = insn.displacement.value;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x8d:
|
case 0x8d:
|
||||||
if (sib == 0x24 && rex_w && !rex_b && !rex_x) {
|
if (sib == 0x24 && rex_w && !rex_b && !rex_x) {
|
||||||
|
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
if (!insn.displacement.value) {
|
if (!insn.displacement.value) {
|
||||||
/* lea (%rsp), reg */
|
/* lea (%rsp), reg */
|
||||||
op->src.type = OP_SRC_REG;
|
op->src.type = OP_SRC_REG;
|
||||||
|
@ -284,16 +315,18 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
op->src.reg = CFI_SP;
|
op->src.reg = CFI_SP;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
|
||||||
|
}
|
||||||
|
|
||||||
} else if (rex == 0x48 && modrm == 0x65) {
|
} else if (rex == 0x48 && modrm == 0x65) {
|
||||||
|
|
||||||
/* lea disp(%rbp), %rsp */
|
/* lea disp(%rbp), %rsp */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_ADD;
|
op->src.type = OP_SRC_ADD;
|
||||||
op->src.reg = CFI_BP;
|
op->src.reg = CFI_BP;
|
||||||
op->src.offset = insn.displacement.value;
|
op->src.offset = insn.displacement.value;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = CFI_SP;
|
op->dest.reg = CFI_SP;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (rex == 0x49 && modrm == 0x62 &&
|
} else if (rex == 0x49 && modrm == 0x62 &&
|
||||||
insn.displacement.value == -8) {
|
insn.displacement.value == -8) {
|
||||||
|
@ -304,12 +337,13 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
* Restoring rsp back to its original value after a
|
* Restoring rsp back to its original value after a
|
||||||
* stack realignment.
|
* stack realignment.
|
||||||
*/
|
*/
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_ADD;
|
op->src.type = OP_SRC_ADD;
|
||||||
op->src.reg = CFI_R10;
|
op->src.reg = CFI_R10;
|
||||||
op->src.offset = -8;
|
op->src.offset = -8;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = CFI_SP;
|
op->dest.reg = CFI_SP;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (rex == 0x49 && modrm == 0x65 &&
|
} else if (rex == 0x49 && modrm == 0x65 &&
|
||||||
insn.displacement.value == -16) {
|
insn.displacement.value == -16) {
|
||||||
|
@ -320,21 +354,23 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
* Restoring rsp back to its original value after a
|
* Restoring rsp back to its original value after a
|
||||||
* stack realignment.
|
* stack realignment.
|
||||||
*/
|
*/
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_ADD;
|
op->src.type = OP_SRC_ADD;
|
||||||
op->src.reg = CFI_R13;
|
op->src.reg = CFI_R13;
|
||||||
op->src.offset = -16;
|
op->src.offset = -16;
|
||||||
op->dest.type = OP_DEST_REG;
|
op->dest.type = OP_DEST_REG;
|
||||||
op->dest.reg = CFI_SP;
|
op->dest.reg = CFI_SP;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x8f:
|
case 0x8f:
|
||||||
/* pop to mem */
|
/* pop to mem */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_POP;
|
op->src.type = OP_SRC_POP;
|
||||||
op->dest.type = OP_DEST_MEM;
|
op->dest.type = OP_DEST_MEM;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x90:
|
case 0x90:
|
||||||
|
@ -343,16 +379,18 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
|
|
||||||
case 0x9c:
|
case 0x9c:
|
||||||
/* pushf */
|
/* pushf */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_CONST;
|
op->src.type = OP_SRC_CONST;
|
||||||
op->dest.type = OP_DEST_PUSHF;
|
op->dest.type = OP_DEST_PUSHF;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x9d:
|
case 0x9d:
|
||||||
/* popf */
|
/* popf */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_POPF;
|
op->src.type = OP_SRC_POPF;
|
||||||
op->dest.type = OP_DEST_MEM;
|
op->dest.type = OP_DEST_MEM;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0f:
|
case 0x0f:
|
||||||
|
@ -387,17 +425,19 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
} else if (op2 == 0xa0 || op2 == 0xa8) {
|
} else if (op2 == 0xa0 || op2 == 0xa8) {
|
||||||
|
|
||||||
/* push fs/gs */
|
/* push fs/gs */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_CONST;
|
op->src.type = OP_SRC_CONST;
|
||||||
op->dest.type = OP_DEST_PUSH;
|
op->dest.type = OP_DEST_PUSH;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (op2 == 0xa1 || op2 == 0xa9) {
|
} else if (op2 == 0xa1 || op2 == 0xa9) {
|
||||||
|
|
||||||
/* pop fs/gs */
|
/* pop fs/gs */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_POP;
|
op->src.type = OP_SRC_POP;
|
||||||
op->dest.type = OP_DEST_MEM;
|
op->dest.type = OP_DEST_MEM;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -409,7 +449,7 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
* mov bp, sp
|
* mov bp, sp
|
||||||
* pop bp
|
* pop bp
|
||||||
*/
|
*/
|
||||||
*type = INSN_STACK;
|
ADD_OP(op)
|
||||||
op->dest.type = OP_DEST_LEAVE;
|
op->dest.type = OP_DEST_LEAVE;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -429,14 +469,41 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
*type = INSN_RETURN;
|
*type = INSN_RETURN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 0xcf: /* iret */
|
||||||
|
/*
|
||||||
|
* Handle sync_core(), which has an IRET to self.
|
||||||
|
* All other IRET are in STT_NONE entry code.
|
||||||
|
*/
|
||||||
|
sym = find_symbol_containing(sec, offset);
|
||||||
|
if (sym && sym->type == STT_FUNC) {
|
||||||
|
ADD_OP(op) {
|
||||||
|
/* add $40, %rsp */
|
||||||
|
op->src.type = OP_SRC_ADD;
|
||||||
|
op->src.reg = CFI_SP;
|
||||||
|
op->src.offset = 5*8;
|
||||||
|
op->dest.type = OP_DEST_REG;
|
||||||
|
op->dest.reg = CFI_SP;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fallthrough */
|
||||||
|
|
||||||
case 0xca: /* retf */
|
case 0xca: /* retf */
|
||||||
case 0xcb: /* retf */
|
case 0xcb: /* retf */
|
||||||
case 0xcf: /* iret */
|
|
||||||
*type = INSN_CONTEXT_SWITCH;
|
*type = INSN_CONTEXT_SWITCH;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xe8:
|
case 0xe8:
|
||||||
*type = INSN_CALL;
|
*type = INSN_CALL;
|
||||||
|
/*
|
||||||
|
* For the impact on the stack, a CALL behaves like
|
||||||
|
* a PUSH of an immediate value (the return address).
|
||||||
|
*/
|
||||||
|
ADD_OP(op) {
|
||||||
|
op->src.type = OP_SRC_CONST;
|
||||||
|
op->dest.type = OP_DEST_PUSH;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xfc:
|
case 0xfc:
|
||||||
|
@ -464,10 +531,11 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
else if (modrm_reg == 6) {
|
else if (modrm_reg == 6) {
|
||||||
|
|
||||||
/* push from mem */
|
/* push from mem */
|
||||||
*type = INSN_STACK;
|
ADD_OP(op) {
|
||||||
op->src.type = OP_SRC_CONST;
|
op->src.type = OP_SRC_CONST;
|
||||||
op->dest.type = OP_DEST_PUSH;
|
op->dest.type = OP_DEST_PUSH;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -480,7 +548,7 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void arch_initial_func_cfi_state(struct cfi_state *state)
|
void arch_initial_func_cfi_state(struct cfi_init_state *state)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|
25
tools/objtool/arch/x86/include/cfi_regs.h
Normal file
25
tools/objtool/arch/x86/include/cfi_regs.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
|
#ifndef _OBJTOOL_CFI_REGS_H
|
||||||
|
#define _OBJTOOL_CFI_REGS_H
|
||||||
|
|
||||||
|
#define CFI_AX 0
|
||||||
|
#define CFI_DX 1
|
||||||
|
#define CFI_CX 2
|
||||||
|
#define CFI_BX 3
|
||||||
|
#define CFI_SI 4
|
||||||
|
#define CFI_DI 5
|
||||||
|
#define CFI_BP 6
|
||||||
|
#define CFI_SP 7
|
||||||
|
#define CFI_R8 8
|
||||||
|
#define CFI_R9 9
|
||||||
|
#define CFI_R10 10
|
||||||
|
#define CFI_R11 11
|
||||||
|
#define CFI_R12 12
|
||||||
|
#define CFI_R13 13
|
||||||
|
#define CFI_R14 14
|
||||||
|
#define CFI_R15 15
|
||||||
|
#define CFI_RA 16
|
||||||
|
#define CFI_NUM_REGS 17
|
||||||
|
|
||||||
|
#endif /* _OBJTOOL_CFI_REGS_H */
|
|
@ -14,10 +14,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <subcmd/parse-options.h>
|
#include <subcmd/parse-options.h>
|
||||||
|
#include <string.h>
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "check.h"
|
#include "objtool.h"
|
||||||
|
|
||||||
bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
|
bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
|
||||||
|
|
||||||
static const char * const check_usage[] = {
|
static const char * const check_usage[] = {
|
||||||
"objtool check [<options>] file.o",
|
"objtool check [<options>] file.o",
|
||||||
|
@ -32,12 +33,14 @@ const struct option check_options[] = {
|
||||||
OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
|
OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
|
||||||
OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
|
OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
|
||||||
OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
|
OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
|
||||||
|
OPT_BOOLEAN('d', "duplicate", &validate_dup, "duplicate validation for vmlinux.o"),
|
||||||
|
OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
|
|
||||||
int cmd_check(int argc, const char **argv)
|
int cmd_check(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
const char *objname;
|
const char *objname, *s;
|
||||||
|
|
||||||
argc = parse_options(argc, argv, check_options, check_usage, 0);
|
argc = parse_options(argc, argv, check_options, check_usage, 0);
|
||||||
|
|
||||||
|
@ -46,5 +49,9 @@ int cmd_check(int argc, const char **argv)
|
||||||
|
|
||||||
objname = argv[0];
|
objname = argv[0];
|
||||||
|
|
||||||
|
s = strstr(objname, "vmlinux.o");
|
||||||
|
if (s && !s[9])
|
||||||
|
vmlinux = true;
|
||||||
|
|
||||||
return check(objname, false);
|
return check(objname, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "check.h"
|
#include "objtool.h"
|
||||||
|
|
||||||
|
|
||||||
static const char *orc_usage[] = {
|
static const char *orc_usage[] = {
|
||||||
"objtool orc generate [<options>] file.o",
|
"objtool orc generate [<options>] file.o",
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <subcmd/parse-options.h>
|
#include <subcmd/parse-options.h>
|
||||||
|
|
||||||
extern const struct option check_options[];
|
extern const struct option check_options[];
|
||||||
extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
|
extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
|
||||||
|
|
||||||
extern int cmd_check(int argc, const char **argv);
|
extern int cmd_check(int argc, const char **argv);
|
||||||
extern int cmd_orc(int argc, const char **argv);
|
extern int cmd_orc(int argc, const char **argv);
|
||||||
|
|
|
@ -6,38 +6,33 @@
|
||||||
#ifndef _OBJTOOL_CFI_H
|
#ifndef _OBJTOOL_CFI_H
|
||||||
#define _OBJTOOL_CFI_H
|
#define _OBJTOOL_CFI_H
|
||||||
|
|
||||||
|
#include "cfi_regs.h"
|
||||||
|
|
||||||
#define CFI_UNDEFINED -1
|
#define CFI_UNDEFINED -1
|
||||||
#define CFI_CFA -2
|
#define CFI_CFA -2
|
||||||
#define CFI_SP_INDIRECT -3
|
#define CFI_SP_INDIRECT -3
|
||||||
#define CFI_BP_INDIRECT -4
|
#define CFI_BP_INDIRECT -4
|
||||||
|
|
||||||
#define CFI_AX 0
|
|
||||||
#define CFI_DX 1
|
|
||||||
#define CFI_CX 2
|
|
||||||
#define CFI_BX 3
|
|
||||||
#define CFI_SI 4
|
|
||||||
#define CFI_DI 5
|
|
||||||
#define CFI_BP 6
|
|
||||||
#define CFI_SP 7
|
|
||||||
#define CFI_R8 8
|
|
||||||
#define CFI_R9 9
|
|
||||||
#define CFI_R10 10
|
|
||||||
#define CFI_R11 11
|
|
||||||
#define CFI_R12 12
|
|
||||||
#define CFI_R13 13
|
|
||||||
#define CFI_R14 14
|
|
||||||
#define CFI_R15 15
|
|
||||||
#define CFI_RA 16
|
|
||||||
#define CFI_NUM_REGS 17
|
|
||||||
|
|
||||||
struct cfi_reg {
|
struct cfi_reg {
|
||||||
int base;
|
int base;
|
||||||
int offset;
|
int offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cfi_state {
|
struct cfi_init_state {
|
||||||
struct cfi_reg cfa;
|
|
||||||
struct cfi_reg regs[CFI_NUM_REGS];
|
struct cfi_reg regs[CFI_NUM_REGS];
|
||||||
|
struct cfi_reg cfa;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cfi_state {
|
||||||
|
struct cfi_reg regs[CFI_NUM_REGS];
|
||||||
|
struct cfi_reg vals[CFI_NUM_REGS];
|
||||||
|
struct cfi_reg cfa;
|
||||||
|
int stack_size;
|
||||||
|
int drap_reg, drap_offset;
|
||||||
|
unsigned char type;
|
||||||
|
bool bp_scratch;
|
||||||
|
bool drap;
|
||||||
|
bool end;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _OBJTOOL_CFI_H */
|
#endif /* _OBJTOOL_CFI_H */
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,22 +7,16 @@
|
||||||
#define _CHECK_H
|
#define _CHECK_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "elf.h"
|
|
||||||
#include "cfi.h"
|
#include "cfi.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "orc.h"
|
|
||||||
#include <linux/hashtable.h>
|
|
||||||
|
|
||||||
struct insn_state {
|
struct insn_state {
|
||||||
struct cfi_reg cfa;
|
struct cfi_state cfi;
|
||||||
struct cfi_reg regs[CFI_NUM_REGS];
|
|
||||||
int stack_size;
|
|
||||||
unsigned char type;
|
|
||||||
bool bp_scratch;
|
|
||||||
bool drap, end, uaccess, df;
|
|
||||||
unsigned int uaccess_stack;
|
unsigned int uaccess_stack;
|
||||||
int drap_reg, drap_offset;
|
bool uaccess;
|
||||||
struct cfi_reg vals[CFI_NUM_REGS];
|
bool df;
|
||||||
|
bool noinstr;
|
||||||
|
s8 instr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct instruction {
|
struct instruction {
|
||||||
|
@ -33,29 +27,24 @@ struct instruction {
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
enum insn_type type;
|
enum insn_type type;
|
||||||
unsigned long immediate;
|
unsigned long immediate;
|
||||||
bool alt_group, dead_end, ignore, hint, save, restore, ignore_alts;
|
bool dead_end, ignore, ignore_alts;
|
||||||
|
bool hint;
|
||||||
bool retpoline_safe;
|
bool retpoline_safe;
|
||||||
|
s8 instr;
|
||||||
u8 visited;
|
u8 visited;
|
||||||
|
u8 ret_offset;
|
||||||
|
int alt_group;
|
||||||
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 rela *jump_table;
|
||||||
struct list_head alts;
|
struct list_head alts;
|
||||||
struct symbol *func;
|
struct symbol *func;
|
||||||
struct stack_op stack_op;
|
struct list_head stack_ops;
|
||||||
struct insn_state state;
|
struct cfi_state cfi;
|
||||||
struct orc_entry orc;
|
struct orc_entry orc;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct objtool_file {
|
|
||||||
struct elf *elf;
|
|
||||||
struct list_head insn_list;
|
|
||||||
DECLARE_HASHTABLE(insn_hash, 20);
|
|
||||||
bool ignore_unreachables, c_file, hints, rodata;
|
|
||||||
};
|
|
||||||
|
|
||||||
int check(const char *objname, bool orc);
|
|
||||||
|
|
||||||
struct instruction *find_insn(struct objtool_file *file,
|
struct instruction *find_insn(struct objtool_file *file,
|
||||||
struct section *sec, unsigned long offset);
|
struct section *sec, unsigned long offset);
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,22 @@ static inline u32 str_hash(const char *str)
|
||||||
return jhash(str, strlen(str), 0);
|
return jhash(str, strlen(str), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int elf_hash_bits(void)
|
||||||
|
{
|
||||||
|
return vmlinux ? ELF_HASH_BITS : 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define elf_hash_add(hashtable, node, key) \
|
||||||
|
hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())])
|
||||||
|
|
||||||
|
static void elf_hash_init(struct hlist_head *table)
|
||||||
|
{
|
||||||
|
__hash_init(table, 1U << elf_hash_bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
#define elf_hash_for_each_possible(name, obj, member, key) \
|
||||||
|
hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member)
|
||||||
|
|
||||||
static void rb_add(struct rb_root *tree, struct rb_node *node,
|
static void rb_add(struct rb_root *tree, struct rb_node *node,
|
||||||
int (*cmp)(struct rb_node *, const struct rb_node *))
|
int (*cmp)(struct rb_node *, const struct rb_node *))
|
||||||
{
|
{
|
||||||
|
@ -45,7 +61,7 @@ static void rb_add(struct rb_root *tree, struct rb_node *node,
|
||||||
rb_insert_color(node, tree);
|
rb_insert_color(node, tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rb_node *rb_find_first(struct rb_root *tree, const void *key,
|
static struct rb_node *rb_find_first(const struct rb_root *tree, const void *key,
|
||||||
int (*cmp)(const void *key, const struct rb_node *))
|
int (*cmp)(const void *key, const struct rb_node *))
|
||||||
{
|
{
|
||||||
struct rb_node *node = tree->rb_node;
|
struct rb_node *node = tree->rb_node;
|
||||||
|
@ -111,11 +127,11 @@ static int symbol_by_offset(const void *key, const struct rb_node *node)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct section *find_section_by_name(struct elf *elf, const char *name)
|
struct section *find_section_by_name(const struct elf *elf, const char *name)
|
||||||
{
|
{
|
||||||
struct section *sec;
|
struct section *sec;
|
||||||
|
|
||||||
hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
|
elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
|
||||||
if (!strcmp(sec->name, name))
|
if (!strcmp(sec->name, name))
|
||||||
return sec;
|
return sec;
|
||||||
|
|
||||||
|
@ -127,7 +143,7 @@ static struct section *find_section_by_index(struct elf *elf,
|
||||||
{
|
{
|
||||||
struct section *sec;
|
struct section *sec;
|
||||||
|
|
||||||
hash_for_each_possible(elf->section_hash, sec, hash, idx)
|
elf_hash_for_each_possible(elf->section_hash, sec, hash, idx)
|
||||||
if (sec->idx == idx)
|
if (sec->idx == idx)
|
||||||
return sec;
|
return sec;
|
||||||
|
|
||||||
|
@ -138,7 +154,7 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
|
||||||
{
|
{
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
|
|
||||||
hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
|
elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
|
||||||
if (sym->idx == idx)
|
if (sym->idx == idx)
|
||||||
return sym;
|
return sym;
|
||||||
|
|
||||||
|
@ -173,7 +189,7 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
|
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset)
|
||||||
{
|
{
|
||||||
struct rb_node *node;
|
struct rb_node *node;
|
||||||
|
|
||||||
|
@ -201,18 +217,18 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
|
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)
|
||||||
{
|
{
|
||||||
struct symbol *sym;
|
struct symbol *sym;
|
||||||
|
|
||||||
hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
|
elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
|
||||||
if (!strcmp(sym->name, name))
|
if (!strcmp(sym->name, name))
|
||||||
return sym;
|
return sym;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
|
struct rela *find_rela_by_dest_range(const struct elf *elf, struct section *sec,
|
||||||
unsigned long offset, unsigned int len)
|
unsigned long offset, unsigned int len)
|
||||||
{
|
{
|
||||||
struct rela *rela, *r = NULL;
|
struct rela *rela, *r = NULL;
|
||||||
|
@ -224,7 +240,7 @@ struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
|
||||||
sec = sec->rela;
|
sec = sec->rela;
|
||||||
|
|
||||||
for_offset_range(o, offset, offset + len) {
|
for_offset_range(o, offset, offset + len) {
|
||||||
hash_for_each_possible(elf->rela_hash, rela, hash,
|
elf_hash_for_each_possible(elf->rela_hash, rela, hash,
|
||||||
sec_offset_hash(sec, o)) {
|
sec_offset_hash(sec, o)) {
|
||||||
if (rela->sec != sec)
|
if (rela->sec != sec)
|
||||||
continue;
|
continue;
|
||||||
|
@ -241,7 +257,7 @@ struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset)
|
struct rela *find_rela_by_dest(const struct elf *elf, struct section *sec, unsigned long offset)
|
||||||
{
|
{
|
||||||
return find_rela_by_dest_range(elf, sec, offset, 1);
|
return find_rela_by_dest_range(elf, sec, offset, 1);
|
||||||
}
|
}
|
||||||
|
@ -309,8 +325,8 @@ static int read_sections(struct elf *elf)
|
||||||
sec->len = sec->sh.sh_size;
|
sec->len = sec->sh.sh_size;
|
||||||
|
|
||||||
list_add_tail(&sec->list, &elf->sections);
|
list_add_tail(&sec->list, &elf->sections);
|
||||||
hash_add(elf->section_hash, &sec->hash, sec->idx);
|
elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
|
||||||
hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
|
elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stats)
|
if (stats)
|
||||||
|
@ -327,12 +343,14 @@ static int read_sections(struct elf *elf)
|
||||||
|
|
||||||
static int read_symbols(struct elf *elf)
|
static int read_symbols(struct elf *elf)
|
||||||
{
|
{
|
||||||
struct section *symtab, *sec;
|
struct section *symtab, *symtab_shndx, *sec;
|
||||||
struct symbol *sym, *pfunc;
|
struct symbol *sym, *pfunc;
|
||||||
struct list_head *entry;
|
struct list_head *entry;
|
||||||
struct rb_node *pnode;
|
struct rb_node *pnode;
|
||||||
int symbols_nr, i;
|
int symbols_nr, i;
|
||||||
char *coldstr;
|
char *coldstr;
|
||||||
|
Elf_Data *shndx_data = NULL;
|
||||||
|
Elf32_Word shndx;
|
||||||
|
|
||||||
symtab = find_section_by_name(elf, ".symtab");
|
symtab = find_section_by_name(elf, ".symtab");
|
||||||
if (!symtab) {
|
if (!symtab) {
|
||||||
|
@ -340,6 +358,10 @@ static int read_symbols(struct elf *elf)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
|
||||||
|
if (symtab_shndx)
|
||||||
|
shndx_data = symtab_shndx->data;
|
||||||
|
|
||||||
symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
|
symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
|
||||||
|
|
||||||
for (i = 0; i < symbols_nr; i++) {
|
for (i = 0; i < symbols_nr; i++) {
|
||||||
|
@ -353,8 +375,9 @@ static int read_symbols(struct elf *elf)
|
||||||
|
|
||||||
sym->idx = i;
|
sym->idx = i;
|
||||||
|
|
||||||
if (!gelf_getsym(symtab->data, i, &sym->sym)) {
|
if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym,
|
||||||
WARN_ELF("gelf_getsym");
|
&shndx)) {
|
||||||
|
WARN_ELF("gelf_getsymshndx");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,10 +391,13 @@ static int read_symbols(struct elf *elf)
|
||||||
sym->type = GELF_ST_TYPE(sym->sym.st_info);
|
sym->type = GELF_ST_TYPE(sym->sym.st_info);
|
||||||
sym->bind = GELF_ST_BIND(sym->sym.st_info);
|
sym->bind = GELF_ST_BIND(sym->sym.st_info);
|
||||||
|
|
||||||
if (sym->sym.st_shndx > SHN_UNDEF &&
|
if ((sym->sym.st_shndx > SHN_UNDEF &&
|
||||||
sym->sym.st_shndx < SHN_LORESERVE) {
|
sym->sym.st_shndx < SHN_LORESERVE) ||
|
||||||
sym->sec = find_section_by_index(elf,
|
(shndx_data && sym->sym.st_shndx == SHN_XINDEX)) {
|
||||||
sym->sym.st_shndx);
|
if (sym->sym.st_shndx != SHN_XINDEX)
|
||||||
|
shndx = sym->sym.st_shndx;
|
||||||
|
|
||||||
|
sym->sec = find_section_by_index(elf, shndx);
|
||||||
if (!sym->sec) {
|
if (!sym->sec) {
|
||||||
WARN("couldn't find section for symbol %s",
|
WARN("couldn't find section for symbol %s",
|
||||||
sym->name);
|
sym->name);
|
||||||
|
@ -394,8 +420,8 @@ static int read_symbols(struct elf *elf)
|
||||||
else
|
else
|
||||||
entry = &sym->sec->symbol_list;
|
entry = &sym->sec->symbol_list;
|
||||||
list_add(&sym->list, entry);
|
list_add(&sym->list, entry);
|
||||||
hash_add(elf->symbol_hash, &sym->hash, sym->idx);
|
elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx);
|
||||||
hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
|
elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stats)
|
if (stats)
|
||||||
|
@ -456,6 +482,14 @@ err:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void elf_add_rela(struct elf *elf, struct rela *rela)
|
||||||
|
{
|
||||||
|
struct section *sec = rela->sec;
|
||||||
|
|
||||||
|
list_add_tail(&rela->list, &sec->rela_list);
|
||||||
|
elf_hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
|
||||||
|
}
|
||||||
|
|
||||||
static int read_relas(struct elf *elf)
|
static int read_relas(struct elf *elf)
|
||||||
{
|
{
|
||||||
struct section *sec;
|
struct section *sec;
|
||||||
|
@ -503,8 +537,7 @@ static int read_relas(struct elf *elf)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_add_tail(&rela->list, &sec->rela_list);
|
elf_add_rela(elf, rela);
|
||||||
hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
|
|
||||||
nr_rela++;
|
nr_rela++;
|
||||||
}
|
}
|
||||||
max_rela = max(max_rela, nr_rela);
|
max_rela = max(max_rela, nr_rela);
|
||||||
|
@ -519,7 +552,7 @@ static int read_relas(struct elf *elf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct elf *elf_read(const char *name, int flags)
|
struct elf *elf_open_read(const char *name, int flags)
|
||||||
{
|
{
|
||||||
struct elf *elf;
|
struct elf *elf;
|
||||||
Elf_Cmd cmd;
|
Elf_Cmd cmd;
|
||||||
|
@ -531,15 +564,16 @@ struct elf *elf_read(const char *name, int flags)
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memset(elf, 0, sizeof(*elf));
|
memset(elf, 0, offsetof(struct elf, sections));
|
||||||
|
|
||||||
hash_init(elf->symbol_hash);
|
|
||||||
hash_init(elf->symbol_name_hash);
|
|
||||||
hash_init(elf->section_hash);
|
|
||||||
hash_init(elf->section_name_hash);
|
|
||||||
hash_init(elf->rela_hash);
|
|
||||||
INIT_LIST_HEAD(&elf->sections);
|
INIT_LIST_HEAD(&elf->sections);
|
||||||
|
|
||||||
|
elf_hash_init(elf->symbol_hash);
|
||||||
|
elf_hash_init(elf->symbol_name_hash);
|
||||||
|
elf_hash_init(elf->section_hash);
|
||||||
|
elf_hash_init(elf->section_name_hash);
|
||||||
|
elf_hash_init(elf->rela_hash);
|
||||||
|
|
||||||
elf->fd = open(name, flags);
|
elf->fd = open(name, flags);
|
||||||
if (elf->fd == -1) {
|
if (elf->fd == -1) {
|
||||||
fprintf(stderr, "objtool: Can't open '%s': %s\n",
|
fprintf(stderr, "objtool: Can't open '%s': %s\n",
|
||||||
|
@ -676,8 +710,8 @@ struct section *elf_create_section(struct elf *elf, const char *name,
|
||||||
shstrtab->changed = true;
|
shstrtab->changed = true;
|
||||||
|
|
||||||
list_add_tail(&sec->list, &elf->sections);
|
list_add_tail(&sec->list, &elf->sections);
|
||||||
hash_add(elf->section_hash, &sec->hash, sec->idx);
|
elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
|
||||||
hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
|
elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
|
||||||
|
|
||||||
return sec;
|
return sec;
|
||||||
}
|
}
|
||||||
|
@ -745,7 +779,7 @@ int elf_rebuild_rela_section(struct section *sec)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int elf_write(struct elf *elf)
|
int elf_write(const struct elf *elf)
|
||||||
{
|
{
|
||||||
struct section *sec;
|
struct section *sec;
|
||||||
Elf_Scn *s;
|
Elf_Scn *s;
|
||||||
|
|
|
@ -39,7 +39,7 @@ struct section {
|
||||||
char *name;
|
char *name;
|
||||||
int idx;
|
int idx;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
bool changed, text, rodata;
|
bool changed, text, rodata, noinstr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct symbol {
|
struct symbol {
|
||||||
|
@ -70,17 +70,19 @@ struct rela {
|
||||||
bool jump_table_start;
|
bool jump_table_start;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define ELF_HASH_BITS 20
|
||||||
|
|
||||||
struct elf {
|
struct elf {
|
||||||
Elf *elf;
|
Elf *elf;
|
||||||
GElf_Ehdr ehdr;
|
GElf_Ehdr ehdr;
|
||||||
int fd;
|
int fd;
|
||||||
char *name;
|
char *name;
|
||||||
struct list_head sections;
|
struct list_head sections;
|
||||||
DECLARE_HASHTABLE(symbol_hash, 20);
|
DECLARE_HASHTABLE(symbol_hash, ELF_HASH_BITS);
|
||||||
DECLARE_HASHTABLE(symbol_name_hash, 20);
|
DECLARE_HASHTABLE(symbol_name_hash, ELF_HASH_BITS);
|
||||||
DECLARE_HASHTABLE(section_hash, 16);
|
DECLARE_HASHTABLE(section_hash, ELF_HASH_BITS);
|
||||||
DECLARE_HASHTABLE(section_name_hash, 16);
|
DECLARE_HASHTABLE(section_name_hash, ELF_HASH_BITS);
|
||||||
DECLARE_HASHTABLE(rela_hash, 20);
|
DECLARE_HASHTABLE(rela_hash, ELF_HASH_BITS);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define OFFSET_STRIDE_BITS 4
|
#define OFFSET_STRIDE_BITS 4
|
||||||
|
@ -112,22 +114,23 @@ static inline u32 rela_hash(struct rela *rela)
|
||||||
return sec_offset_hash(rela->sec, rela->offset);
|
return sec_offset_hash(rela->sec, rela->offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct elf *elf_read(const char *name, int flags);
|
struct elf *elf_open_read(const char *name, int flags);
|
||||||
struct section *find_section_by_name(struct elf *elf, const char *name);
|
struct section *elf_create_section(struct elf *elf, const char *name, size_t entsize, int nr);
|
||||||
|
struct section *elf_create_rela_section(struct elf *elf, struct section *base);
|
||||||
|
void elf_add_rela(struct elf *elf, struct rela *rela);
|
||||||
|
int elf_write(const struct elf *elf);
|
||||||
|
void elf_close(struct elf *elf);
|
||||||
|
|
||||||
|
struct section *find_section_by_name(const struct elf *elf, const char *name);
|
||||||
struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
|
struct symbol *find_func_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_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(const struct elf *elf, const char *name);
|
||||||
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
|
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
|
||||||
struct rela *find_rela_by_dest(struct elf *elf, struct section *sec, unsigned long offset);
|
struct rela *find_rela_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
|
||||||
struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
|
struct rela *find_rela_by_dest_range(const struct elf *elf, struct section *sec,
|
||||||
unsigned long offset, unsigned int len);
|
unsigned long offset, unsigned int len);
|
||||||
struct symbol *find_func_containing(struct section *sec, unsigned long offset);
|
struct symbol *find_func_containing(struct section *sec, unsigned long offset);
|
||||||
struct section *elf_create_section(struct elf *elf, const char *name, size_t
|
|
||||||
entsize, int nr);
|
|
||||||
struct section *elf_create_rela_section(struct elf *elf, struct section *base);
|
|
||||||
int elf_rebuild_rela_section(struct section *sec);
|
int elf_rebuild_rela_section(struct section *sec);
|
||||||
int elf_write(struct elf *elf);
|
|
||||||
void elf_close(struct elf *elf);
|
|
||||||
|
|
||||||
#define for_each_sec(file, sec) \
|
#define for_each_sec(file, sec) \
|
||||||
list_for_each_entry(sec, &file->elf->sections, list)
|
list_for_each_entry(sec, &file->elf->sections, list)
|
||||||
|
|
|
@ -58,7 +58,9 @@ static void cmd_usage(void)
|
||||||
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
|
if (!help)
|
||||||
exit(129);
|
exit(129);
|
||||||
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_options(int *argc, const char ***argv)
|
static void handle_options(int *argc, const char ***argv)
|
||||||
|
|
27
tools/objtool/objtool.h
Normal file
27
tools/objtool/objtool.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Matt Helsley <mhelsley@vmware.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _OBJTOOL_H
|
||||||
|
#define _OBJTOOL_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/hashtable.h>
|
||||||
|
|
||||||
|
#include "elf.h"
|
||||||
|
|
||||||
|
struct objtool_file {
|
||||||
|
struct elf *elf;
|
||||||
|
struct list_head insn_list;
|
||||||
|
DECLARE_HASHTABLE(insn_hash, 20);
|
||||||
|
bool ignore_unreachables, c_file, hints, rodata;
|
||||||
|
};
|
||||||
|
|
||||||
|
int check(const char *objname, bool orc);
|
||||||
|
int orc_dump(const char *objname);
|
||||||
|
int create_orc(struct objtool_file *file);
|
||||||
|
int create_orc_sections(struct objtool_file *file);
|
||||||
|
|
||||||
|
#endif /* _OBJTOOL_H */
|
|
@ -1,18 +0,0 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _ORC_H
|
|
||||||
#define _ORC_H
|
|
||||||
|
|
||||||
#include <asm/orc_types.h>
|
|
||||||
|
|
||||||
struct objtool_file;
|
|
||||||
|
|
||||||
int create_orc(struct objtool_file *file);
|
|
||||||
int create_orc_sections(struct objtool_file *file);
|
|
||||||
|
|
||||||
int orc_dump(const char *objname);
|
|
||||||
|
|
||||||
#endif /* _ORC_H */
|
|
|
@ -4,7 +4,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "orc.h"
|
#include <asm/orc_types.h>
|
||||||
|
#include "objtool.h"
|
||||||
#include "warn.h"
|
#include "warn.h"
|
||||||
|
|
||||||
static const char *reg_name(unsigned int reg)
|
static const char *reg_name(unsigned int reg)
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "orc.h"
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
#include "warn.h"
|
#include "warn.h"
|
||||||
|
|
||||||
|
@ -16,10 +15,10 @@ int create_orc(struct objtool_file *file)
|
||||||
|
|
||||||
for_each_insn(file, insn) {
|
for_each_insn(file, insn) {
|
||||||
struct orc_entry *orc = &insn->orc;
|
struct orc_entry *orc = &insn->orc;
|
||||||
struct cfi_reg *cfa = &insn->state.cfa;
|
struct cfi_reg *cfa = &insn->cfi.cfa;
|
||||||
struct cfi_reg *bp = &insn->state.regs[CFI_BP];
|
struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
|
||||||
|
|
||||||
orc->end = insn->state.end;
|
orc->end = insn->cfi.end;
|
||||||
|
|
||||||
if (cfa->base == CFI_UNDEFINED) {
|
if (cfa->base == CFI_UNDEFINED) {
|
||||||
orc->sp_reg = ORC_REG_UNDEFINED;
|
orc->sp_reg = ORC_REG_UNDEFINED;
|
||||||
|
@ -75,7 +74,7 @@ int create_orc(struct objtool_file *file)
|
||||||
|
|
||||||
orc->sp_offset = cfa->offset;
|
orc->sp_offset = cfa->offset;
|
||||||
orc->bp_offset = bp->offset;
|
orc->bp_offset = bp->offset;
|
||||||
orc->type = insn->state.type;
|
orc->type = insn->cfi.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -130,8 +129,7 @@ static int create_orc_entry(struct elf *elf, struct section *u_sec, struct secti
|
||||||
rela->offset = idx * sizeof(int);
|
rela->offset = idx * sizeof(int);
|
||||||
rela->sec = ip_relasec;
|
rela->sec = ip_relasec;
|
||||||
|
|
||||||
list_add_tail(&rela->list, &ip_relasec->rela_list);
|
elf_add_rela(elf, rela);
|
||||||
hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
40
tools/objtool/weak.c
Normal file
40
tools/objtool/weak.c
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Matt Helsley <mhelsley@vmware.com>
|
||||||
|
* Weak definitions necessary to compile objtool without
|
||||||
|
* some subcommands (e.g. check, orc).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "objtool.h"
|
||||||
|
|
||||||
|
#define __weak __attribute__((weak))
|
||||||
|
|
||||||
|
#define UNSUPPORTED(name) \
|
||||||
|
({ \
|
||||||
|
fprintf(stderr, "error: objtool: " name " not implemented\n"); \
|
||||||
|
return ENOSYS; \
|
||||||
|
})
|
||||||
|
|
||||||
|
const char __weak *objname;
|
||||||
|
|
||||||
|
int __weak check(const char *_objname, bool orc)
|
||||||
|
{
|
||||||
|
UNSUPPORTED("check subcommand");
|
||||||
|
}
|
||||||
|
|
||||||
|
int __weak orc_dump(const char *_objname)
|
||||||
|
{
|
||||||
|
UNSUPPORTED("orc");
|
||||||
|
}
|
||||||
|
|
||||||
|
int __weak create_orc(struct objtool_file *file)
|
||||||
|
{
|
||||||
|
UNSUPPORTED("orc");
|
||||||
|
}
|
||||||
|
|
||||||
|
int __weak create_orc_sections(struct objtool_file *file)
|
||||||
|
{
|
||||||
|
UNSUPPORTED("orc");
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue