mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02: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
 | 
			
		||||
	movdqu IV, 0x30(OUTP)
 | 
			
		||||
 | 
			
		||||
	CALL_NOSPEC %r11
 | 
			
		||||
	CALL_NOSPEC r11
 | 
			
		||||
 | 
			
		||||
	movdqu 0x00(OUTP), INC
 | 
			
		||||
	pxor INC, STATE1
 | 
			
		||||
| 
						 | 
				
			
			@ -2803,7 +2803,7 @@ SYM_FUNC_START(aesni_xts_crypt8)
 | 
			
		|||
	_aesni_gf128mul_x_ble()
 | 
			
		||||
	movups IV, (IVP)
 | 
			
		||||
 | 
			
		||||
	CALL_NOSPEC %r11
 | 
			
		||||
	CALL_NOSPEC r11
 | 
			
		||||
 | 
			
		||||
	movdqu 0x40(OUTP), INC
 | 
			
		||||
	pxor INC, STATE1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1228,7 +1228,7 @@ SYM_FUNC_START_LOCAL(camellia_xts_crypt_16way)
 | 
			
		|||
	vpxor 14 * 16(%rax), %xmm15, %xmm14;
 | 
			
		||||
	vpxor 15 * 16(%rax), %xmm15, %xmm15;
 | 
			
		||||
 | 
			
		||||
	CALL_NOSPEC %r9;
 | 
			
		||||
	CALL_NOSPEC r9;
 | 
			
		||||
 | 
			
		||||
	addq $(16 * 16), %rsp;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1339,7 +1339,7 @@ SYM_FUNC_START_LOCAL(camellia_xts_crypt_32way)
 | 
			
		|||
	vpxor 14 * 32(%rax), %ymm15, %ymm14;
 | 
			
		||||
	vpxor 15 * 32(%rax), %ymm15, %ymm15;
 | 
			
		||||
 | 
			
		||||
	CALL_NOSPEC %r9;
 | 
			
		||||
	CALL_NOSPEC r9;
 | 
			
		||||
 | 
			
		||||
	addq $(16 * 32), %rsp;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,7 +75,7 @@
 | 
			
		|||
 | 
			
		||||
.text
 | 
			
		||||
SYM_FUNC_START(crc_pcl)
 | 
			
		||||
#define    bufp		%rdi
 | 
			
		||||
#define    bufp		rdi
 | 
			
		||||
#define    bufp_dw	%edi
 | 
			
		||||
#define    bufp_w	%di
 | 
			
		||||
#define    bufp_b	%dil
 | 
			
		||||
| 
						 | 
				
			
			@ -105,9 +105,9 @@ SYM_FUNC_START(crc_pcl)
 | 
			
		|||
	## 1) ALIGN:
 | 
			
		||||
	################################################################
 | 
			
		||||
 | 
			
		||||
	mov     bufp, bufptmp		# rdi = *buf
 | 
			
		||||
	neg     bufp
 | 
			
		||||
	and     $7, bufp		# calculate the unalignment amount of
 | 
			
		||||
	mov     %bufp, bufptmp		# rdi = *buf
 | 
			
		||||
	neg     %bufp
 | 
			
		||||
	and     $7, %bufp		# calculate the unalignment amount of
 | 
			
		||||
					# the address
 | 
			
		||||
	je      proc_block		# Skip if aligned
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -123,13 +123,13 @@ SYM_FUNC_START(crc_pcl)
 | 
			
		|||
do_align:
 | 
			
		||||
	#### Calculate CRC of unaligned bytes of the buffer (if any)
 | 
			
		||||
	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
 | 
			
		||||
	sub     bufp, len		# update buffer length
 | 
			
		||||
	sub     %bufp, len		# update buffer length
 | 
			
		||||
align_loop:
 | 
			
		||||
	crc32b  %bl, crc_init_dw 	# compute crc32 of 1-byte
 | 
			
		||||
	shr     $8, tmp			# get next byte
 | 
			
		||||
	dec     bufp
 | 
			
		||||
	dec     %bufp
 | 
			
		||||
	jne     align_loop
 | 
			
		||||
 | 
			
		||||
proc_block:
 | 
			
		||||
| 
						 | 
				
			
			@ -169,10 +169,10 @@ continue_block:
 | 
			
		|||
	xor     crc2, crc2
 | 
			
		||||
 | 
			
		||||
	## branch into array
 | 
			
		||||
	lea	jump_table(%rip), bufp
 | 
			
		||||
	movzxw  (bufp, %rax, 2), len
 | 
			
		||||
	lea	crc_array(%rip), bufp
 | 
			
		||||
	lea     (bufp, len, 1), bufp
 | 
			
		||||
	lea	jump_table(%rip), %bufp
 | 
			
		||||
	movzxw  (%bufp, %rax, 2), len
 | 
			
		||||
	lea	crc_array(%rip), %bufp
 | 
			
		||||
	lea     (%bufp, len, 1), %bufp
 | 
			
		||||
	JMP_NOSPEC bufp
 | 
			
		||||
 | 
			
		||||
	################################################################
 | 
			
		||||
| 
						 | 
				
			
			@ -218,9 +218,9 @@ LABEL crc_ %i
 | 
			
		|||
	## 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
 | 
			
		||||
	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)
 | 
			
		||||
	subq    %rax, tmp			# tmp -= rax*24
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -816,7 +816,7 @@ SYM_CODE_START(ret_from_fork)
 | 
			
		|||
 | 
			
		||||
	/* kernel thread */
 | 
			
		||||
1:	movl	%edi, %eax
 | 
			
		||||
	CALL_NOSPEC %ebx
 | 
			
		||||
	CALL_NOSPEC ebx
 | 
			
		||||
	/*
 | 
			
		||||
	 * A kernel thread is allowed to return here after successfully
 | 
			
		||||
	 * 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
 | 
			
		||||
	movl	%esp, %eax			# pt_regs pointer
 | 
			
		||||
	CALL_NOSPEC %edi
 | 
			
		||||
	CALL_NOSPEC edi
 | 
			
		||||
	jmp	ret_from_exception
 | 
			
		||||
SYM_CODE_END(common_exception_read_cr2)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1522,7 +1522,7 @@ SYM_CODE_START_LOCAL_NOALIGN(common_exception)
 | 
			
		|||
 | 
			
		||||
	TRACE_IRQS_OFF
 | 
			
		||||
	movl	%esp, %eax			# pt_regs pointer
 | 
			
		||||
	CALL_NOSPEC %edi
 | 
			
		||||
	CALL_NOSPEC edi
 | 
			
		||||
	jmp	ret_from_exception
 | 
			
		||||
SYM_CODE_END(common_exception)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -348,7 +348,7 @@ SYM_CODE_START(ret_from_fork)
 | 
			
		|||
	/* kernel thread */
 | 
			
		||||
	UNWIND_HINT_EMPTY
 | 
			
		||||
	movq	%r12, %rdi
 | 
			
		||||
	CALL_NOSPEC %rbx
 | 
			
		||||
	CALL_NOSPEC rbx
 | 
			
		||||
	/*
 | 
			
		||||
	 * A kernel thread is allowed to return here after successfully
 | 
			
		||||
	 * 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
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_RETPOLINE
 | 
			
		||||
#ifdef CONFIG_X86_32
 | 
			
		||||
#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_e ## reg(void);
 | 
			
		||||
#else
 | 
			
		||||
#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_r ## reg(void);
 | 
			
		||||
INDIRECT_THUNK(8)
 | 
			
		||||
INDIRECT_THUNK(9)
 | 
			
		||||
INDIRECT_THUNK(10)
 | 
			
		||||
INDIRECT_THUNK(11)
 | 
			
		||||
INDIRECT_THUNK(12)
 | 
			
		||||
INDIRECT_THUNK(13)
 | 
			
		||||
INDIRECT_THUNK(14)
 | 
			
		||||
INDIRECT_THUNK(15)
 | 
			
		||||
#endif
 | 
			
		||||
INDIRECT_THUNK(ax)
 | 
			
		||||
INDIRECT_THUNK(bx)
 | 
			
		||||
INDIRECT_THUNK(cx)
 | 
			
		||||
INDIRECT_THUNK(dx)
 | 
			
		||||
INDIRECT_THUNK(si)
 | 
			
		||||
INDIRECT_THUNK(di)
 | 
			
		||||
INDIRECT_THUNK(bp)
 | 
			
		||||
 | 
			
		||||
#define DECL_INDIRECT_THUNK(reg) \
 | 
			
		||||
	extern asmlinkage void __x86_indirect_thunk_ ## reg (void);
 | 
			
		||||
 | 
			
		||||
#define DECL_RETPOLINE(reg) \
 | 
			
		||||
	extern asmlinkage void __x86_retpoline_ ## reg (void);
 | 
			
		||||
 | 
			
		||||
#undef GEN
 | 
			
		||||
#define GEN(reg) DECL_INDIRECT_THUNK(reg)
 | 
			
		||||
#include <asm/GEN-for-each-reg.h>
 | 
			
		||||
 | 
			
		||||
#undef GEN
 | 
			
		||||
#define GEN(reg) DECL_RETPOLINE(reg)
 | 
			
		||||
#include <asm/GEN-for-each-reg.h>
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_RETPOLINE */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,20 +4,13 @@
 | 
			
		|||
#define _ASM_X86_NOSPEC_BRANCH_H_
 | 
			
		||||
 | 
			
		||||
#include <linux/static_key.h>
 | 
			
		||||
#include <linux/frame.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/alternative.h>
 | 
			
		||||
#include <asm/alternative-asm.h>
 | 
			
		||||
#include <asm/cpufeatures.h>
 | 
			
		||||
#include <asm/msr-index.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
 | 
			
		||||
#include <asm/unwind_hints.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Fill the CPU return stack buffer.
 | 
			
		||||
| 
						 | 
				
			
			@ -46,21 +39,25 @@
 | 
			
		|||
#define __FILL_RETURN_BUFFER(reg, nr, sp)	\
 | 
			
		||||
	mov	$(nr/2), reg;			\
 | 
			
		||||
771:						\
 | 
			
		||||
	ANNOTATE_INTRA_FUNCTION_CALL;		\
 | 
			
		||||
	call	772f;				\
 | 
			
		||||
773:	/* speculation trap */			\
 | 
			
		||||
	UNWIND_HINT_EMPTY;			\
 | 
			
		||||
	pause;					\
 | 
			
		||||
	lfence;					\
 | 
			
		||||
	jmp	773b;				\
 | 
			
		||||
772:						\
 | 
			
		||||
	ANNOTATE_INTRA_FUNCTION_CALL;		\
 | 
			
		||||
	call	774f;				\
 | 
			
		||||
775:	/* speculation trap */			\
 | 
			
		||||
	UNWIND_HINT_EMPTY;			\
 | 
			
		||||
	pause;					\
 | 
			
		||||
	lfence;					\
 | 
			
		||||
	jmp	775b;				\
 | 
			
		||||
774:						\
 | 
			
		||||
	add	$(BITS_PER_LONG/8) * 2, sp;	\
 | 
			
		||||
	dec	reg;				\
 | 
			
		||||
	jnz	771b;				\
 | 
			
		||||
	add	$(BITS_PER_LONG/8) * nr, sp;
 | 
			
		||||
	jnz	771b;
 | 
			
		||||
 | 
			
		||||
#ifdef __ASSEMBLY__
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,34 +73,6 @@
 | 
			
		|||
	.popsection
 | 
			
		||||
.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
 | 
			
		||||
 * indirect jmp/call which may be susceptible to the Spectre variant 2
 | 
			
		||||
| 
						 | 
				
			
			@ -111,23 +80,21 @@
 | 
			
		|||
 */
 | 
			
		||||
.macro JMP_NOSPEC reg:req
 | 
			
		||||
#ifdef CONFIG_RETPOLINE
 | 
			
		||||
	ANNOTATE_NOSPEC_ALTERNATIVE
 | 
			
		||||
	ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; jmp *\reg),	\
 | 
			
		||||
		__stringify(RETPOLINE_JMP \reg), X86_FEATURE_RETPOLINE,	\
 | 
			
		||||
		__stringify(lfence; ANNOTATE_RETPOLINE_SAFE; jmp *\reg), X86_FEATURE_RETPOLINE_AMD
 | 
			
		||||
	ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; jmp *%\reg), \
 | 
			
		||||
		      __stringify(jmp __x86_retpoline_\reg), X86_FEATURE_RETPOLINE, \
 | 
			
		||||
		      __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; jmp *%\reg), X86_FEATURE_RETPOLINE_AMD
 | 
			
		||||
#else
 | 
			
		||||
	jmp	*\reg
 | 
			
		||||
	jmp	*%\reg
 | 
			
		||||
#endif
 | 
			
		||||
.endm
 | 
			
		||||
 | 
			
		||||
.macro CALL_NOSPEC reg:req
 | 
			
		||||
#ifdef CONFIG_RETPOLINE
 | 
			
		||||
	ANNOTATE_NOSPEC_ALTERNATIVE
 | 
			
		||||
	ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; call *\reg),	\
 | 
			
		||||
		__stringify(RETPOLINE_CALL \reg), X86_FEATURE_RETPOLINE,\
 | 
			
		||||
		__stringify(lfence; ANNOTATE_RETPOLINE_SAFE; call *\reg), X86_FEATURE_RETPOLINE_AMD
 | 
			
		||||
	ALTERNATIVE_2 __stringify(ANNOTATE_RETPOLINE_SAFE; call *%\reg), \
 | 
			
		||||
		      __stringify(call __x86_retpoline_\reg), X86_FEATURE_RETPOLINE, \
 | 
			
		||||
		      __stringify(lfence; ANNOTATE_RETPOLINE_SAFE; call *%\reg), X86_FEATURE_RETPOLINE_AMD
 | 
			
		||||
#else
 | 
			
		||||
	call	*\reg
 | 
			
		||||
	call	*%\reg
 | 
			
		||||
#endif
 | 
			
		||||
.endm
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -137,10 +104,8 @@
 | 
			
		|||
  */
 | 
			
		||||
.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req
 | 
			
		||||
#ifdef CONFIG_RETPOLINE
 | 
			
		||||
	ANNOTATE_NOSPEC_ALTERNATIVE
 | 
			
		||||
	ALTERNATIVE "jmp .Lskip_rsb_\@",				\
 | 
			
		||||
		__stringify(__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP))	\
 | 
			
		||||
		\ftr
 | 
			
		||||
	ALTERNATIVE "jmp .Lskip_rsb_\@", "", \ftr
 | 
			
		||||
	__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP)
 | 
			
		||||
.Lskip_rsb_\@:
 | 
			
		||||
#endif
 | 
			
		||||
.endm
 | 
			
		||||
| 
						 | 
				
			
			@ -161,16 +126,16 @@
 | 
			
		|||
 * which is ensured when CONFIG_RETPOLINE is defined.
 | 
			
		||||
 */
 | 
			
		||||
# define CALL_NOSPEC						\
 | 
			
		||||
	ANNOTATE_NOSPEC_ALTERNATIVE				\
 | 
			
		||||
	ALTERNATIVE_2(						\
 | 
			
		||||
	ANNOTATE_RETPOLINE_SAFE					\
 | 
			
		||||
	"call *%[thunk_target]\n",				\
 | 
			
		||||
	"call __x86_indirect_thunk_%V[thunk_target]\n",		\
 | 
			
		||||
	"call __x86_retpoline_%V[thunk_target]\n",		\
 | 
			
		||||
	X86_FEATURE_RETPOLINE,					\
 | 
			
		||||
	"lfence;\n"						\
 | 
			
		||||
	ANNOTATE_RETPOLINE_SAFE					\
 | 
			
		||||
	"call *%[thunk_target]\n",				\
 | 
			
		||||
	X86_FEATURE_RETPOLINE_AMD)
 | 
			
		||||
 | 
			
		||||
# define THUNK_TARGET(addr) [thunk_target] "r" (addr)
 | 
			
		||||
 | 
			
		||||
#else /* CONFIG_X86_32 */
 | 
			
		||||
| 
						 | 
				
			
			@ -180,7 +145,6 @@
 | 
			
		|||
 * here, anyway.
 | 
			
		||||
 */
 | 
			
		||||
# define CALL_NOSPEC						\
 | 
			
		||||
	ANNOTATE_NOSPEC_ALTERNATIVE				\
 | 
			
		||||
	ALTERNATIVE_2(						\
 | 
			
		||||
	ANNOTATE_RETPOLINE_SAFE					\
 | 
			
		||||
	"call *%[thunk_target]\n",				\
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,8 +58,7 @@
 | 
			
		|||
#define ORC_TYPE_CALL			0
 | 
			
		||||
#define ORC_TYPE_REGS			1
 | 
			
		||||
#define ORC_TYPE_REGS_IRET		2
 | 
			
		||||
#define UNWIND_HINT_TYPE_SAVE		3
 | 
			
		||||
#define UNWIND_HINT_TYPE_RESTORE	4
 | 
			
		||||
#define UNWIND_HINT_TYPE_RET_OFFSET	3
 | 
			
		||||
 | 
			
		||||
#ifndef __ASSEMBLY__
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -728,7 +728,6 @@ static inline void sync_core(void)
 | 
			
		|||
	unsigned int tmp;
 | 
			
		||||
 | 
			
		||||
	asm volatile (
 | 
			
		||||
		UNWIND_HINT_SAVE
 | 
			
		||||
		"mov %%ss, %0\n\t"
 | 
			
		||||
		"pushq %q0\n\t"
 | 
			
		||||
		"pushq %%rsp\n\t"
 | 
			
		||||
| 
						 | 
				
			
			@ -738,7 +737,6 @@ static inline void sync_core(void)
 | 
			
		|||
		"pushq %q0\n\t"
 | 
			
		||||
		"pushq $1f\n\t"
 | 
			
		||||
		"iretq\n\t"
 | 
			
		||||
		UNWIND_HINT_RESTORE
 | 
			
		||||
		"1:"
 | 
			
		||||
		: "=&r" (tmp), ASM_CALL_CONSTRAINT : : "cc", "memory");
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,8 +57,10 @@ static __always_inline unsigned long smap_save(void)
 | 
			
		|||
{
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
 | 
			
		||||
	asm volatile (ALTERNATIVE("", "pushf; pop %0; " __ASM_CLAC,
 | 
			
		||||
				  X86_FEATURE_SMAP)
 | 
			
		||||
	asm volatile ("# smap_save\n\t"
 | 
			
		||||
		      ALTERNATIVE("jmp 1f", "", X86_FEATURE_SMAP)
 | 
			
		||||
		      "pushf; pop %0; " __ASM_CLAC "\n\t"
 | 
			
		||||
		      "1:"
 | 
			
		||||
		      : "=rm" (flags) : : "memory", "cc");
 | 
			
		||||
 | 
			
		||||
	return flags;
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +68,10 @@ static __always_inline unsigned long smap_save(void)
 | 
			
		|||
 | 
			
		||||
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");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -86,32 +86,15 @@
 | 
			
		|||
	UNWIND_HINT sp_offset=\sp_offset
 | 
			
		||||
.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
 | 
			
		||||
 | 
			
		||||
.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 /* _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 */
 | 
			
		||||
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_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;
 | 
			
		||||
	} else {
 | 
			
		||||
		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;
 | 
			
		||||
		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))
 | 
			
		||||
		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
 | 
			
		||||
	 * 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;
 | 
			
		||||
		} else {
 | 
			
		||||
			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 = size + RET_SIZE + sizeof(void *);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -189,5 +189,5 @@ return_to_handler:
 | 
			
		|||
	movl	%eax, %ecx
 | 
			
		||||
	popl	%edx
 | 
			
		||||
	popl	%eax
 | 
			
		||||
	JMP_NOSPEC %ecx
 | 
			
		||||
	JMP_NOSPEC ecx
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@
 | 
			
		|||
#endif /* CONFIG_FRAME_POINTER */
 | 
			
		||||
 | 
			
		||||
/* 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +77,7 @@
 | 
			
		|||
	/*
 | 
			
		||||
	 * 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 %rcx, RCX(%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
 | 
			
		||||
	 * 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
 | 
			
		||||
SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
 | 
			
		||||
	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)
 | 
			
		||||
	retq
 | 
			
		||||
SYM_FUNC_END(ftrace_caller)
 | 
			
		||||
SYM_FUNC_END(ftrace_epilogue)
 | 
			
		||||
 | 
			
		||||
SYM_FUNC_START(ftrace_regs_caller)
 | 
			
		||||
	/* Save the current flags before any operations that can change them */
 | 
			
		||||
	pushfq
 | 
			
		||||
 | 
			
		||||
	UNWIND_HINT_SAVE
 | 
			
		||||
 | 
			
		||||
	/* added 8 bytes to save flags */
 | 
			
		||||
	save_mcount_regs 8
 | 
			
		||||
	/* 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 %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
 | 
			
		||||
	cmpq	$0, %rax
 | 
			
		||||
	je	1f
 | 
			
		||||
	testq	%rax, %rax
 | 
			
		||||
	jz	1f
 | 
			
		||||
 | 
			
		||||
	/* Swap the flags with orig_rax */
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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 */
 | 
			
		||||
	popfq
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -268,7 +267,6 @@ SYM_INNER_LABEL(ftrace_regs_call, SYM_L_GLOBAL)
 | 
			
		|||
	 * to the return.
 | 
			
		||||
	 */
 | 
			
		||||
SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL)
 | 
			
		||||
 | 
			
		||||
	jmp ftrace_epilogue
 | 
			
		||||
 | 
			
		||||
SYM_FUNC_END(ftrace_regs_caller)
 | 
			
		||||
| 
						 | 
				
			
			@ -303,7 +301,7 @@ trace:
 | 
			
		|||
	 * function tracing is enabled.
 | 
			
		||||
	 */
 | 
			
		||||
	movq ftrace_trace_function, %r8
 | 
			
		||||
	CALL_NOSPEC %r8
 | 
			
		||||
	CALL_NOSPEC r8
 | 
			
		||||
	restore_mcount_regs
 | 
			
		||||
 | 
			
		||||
	jmp fgraph_trace
 | 
			
		||||
| 
						 | 
				
			
			@ -340,6 +338,6 @@ SYM_CODE_START(return_to_handler)
 | 
			
		|||
	movq 8(%rsp), %rdx
 | 
			
		||||
	movq (%rsp), %rax
 | 
			
		||||
	addq $24, %rsp
 | 
			
		||||
	JMP_NOSPEC %rdi
 | 
			
		||||
	JMP_NOSPEC rdi
 | 
			
		||||
SYM_CODE_END(return_to_handler)
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,7 +153,7 @@ SYM_FUNC_START(csum_partial)
 | 
			
		|||
	negl %ebx
 | 
			
		||||
	lea 45f(%ebx,%ebx,2), %ebx
 | 
			
		||||
	testl %esi, %esi
 | 
			
		||||
	JMP_NOSPEC %ebx
 | 
			
		||||
	JMP_NOSPEC ebx
 | 
			
		||||
 | 
			
		||||
	# Handle 2-byte-aligned regions
 | 
			
		||||
20:	addw (%esi), %ax
 | 
			
		||||
| 
						 | 
				
			
			@ -436,7 +436,7 @@ SYM_FUNC_START(csum_partial_copy_generic)
 | 
			
		|||
	andl $-32,%edx
 | 
			
		||||
	lea 3f(%ebx,%ebx), %ebx
 | 
			
		||||
	testl %esi, %esi 
 | 
			
		||||
	JMP_NOSPEC %ebx
 | 
			
		||||
	JMP_NOSPEC ebx
 | 
			
		||||
1:	addl $64,%esi
 | 
			
		||||
	addl $64,%edi 
 | 
			
		||||
	SRC(movb -32(%edx),%bl)	; SRC(movb (%edx),%bl)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,15 +7,31 @@
 | 
			
		|||
#include <asm/alternative-asm.h>
 | 
			
		||||
#include <asm/export.h>
 | 
			
		||||
#include <asm/nospec-branch.h>
 | 
			
		||||
#include <asm/unwind_hints.h>
 | 
			
		||||
#include <asm/frame.h>
 | 
			
		||||
 | 
			
		||||
.macro THUNK reg
 | 
			
		||||
	.section .text.__x86.indirect_thunk
 | 
			
		||||
 | 
			
		||||
	.align 32
 | 
			
		||||
SYM_FUNC_START(__x86_indirect_thunk_\reg)
 | 
			
		||||
	CFI_STARTPROC
 | 
			
		||||
	JMP_NOSPEC %\reg
 | 
			
		||||
	CFI_ENDPROC
 | 
			
		||||
	JMP_NOSPEC \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
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -24,25 +40,24 @@ SYM_FUNC_END(__x86_indirect_thunk_\reg)
 | 
			
		|||
 * only see one instance of "__x86_indirect_thunk_\reg" rather
 | 
			
		||||
 * than one per register with the correct names. So we do it
 | 
			
		||||
 * 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(reg) __EXPORT_THUNK(__x86_indirect_thunk_ ## reg)
 | 
			
		||||
#define GENERATE_THUNK(reg) THUNK reg ; EXPORT_THUNK(reg)
 | 
			
		||||
 | 
			
		||||
GENERATE_THUNK(_ASM_AX)
 | 
			
		||||
GENERATE_THUNK(_ASM_BX)
 | 
			
		||||
GENERATE_THUNK(_ASM_CX)
 | 
			
		||||
GENERATE_THUNK(_ASM_DX)
 | 
			
		||||
GENERATE_THUNK(_ASM_SI)
 | 
			
		||||
GENERATE_THUNK(_ASM_DI)
 | 
			
		||||
GENERATE_THUNK(_ASM_BP)
 | 
			
		||||
#ifdef CONFIG_64BIT
 | 
			
		||||
GENERATE_THUNK(r8)
 | 
			
		||||
GENERATE_THUNK(r9)
 | 
			
		||||
GENERATE_THUNK(r10)
 | 
			
		||||
GENERATE_THUNK(r11)
 | 
			
		||||
GENERATE_THUNK(r12)
 | 
			
		||||
GENERATE_THUNK(r13)
 | 
			
		||||
GENERATE_THUNK(r14)
 | 
			
		||||
GENERATE_THUNK(r15)
 | 
			
		||||
#endif
 | 
			
		||||
#define __EXPORT_THUNK(sym)	_ASM_NOKPROBE(sym); EXPORT_SYMBOL(sym)
 | 
			
		||||
#define EXPORT_THUNK(reg)	__EXPORT_THUNK(__x86_indirect_thunk_ ## reg)
 | 
			
		||||
#define EXPORT_RETPOLINE(reg)  __EXPORT_THUNK(__x86_retpoline_ ## reg)
 | 
			
		||||
 | 
			
		||||
#undef GEN
 | 
			
		||||
#define GEN(reg) THUNK reg
 | 
			
		||||
#include <asm/GEN-for-each-reg.h>
 | 
			
		||||
 | 
			
		||||
#undef GEN
 | 
			
		||||
#define GEN(reg) EXPORT_THUNK(reg)
 | 
			
		||||
#include <asm/GEN-for-each-reg.h>
 | 
			
		||||
 | 
			
		||||
#undef GEN
 | 
			
		||||
#define GEN(reg) EXPORT_RETPOLINE(reg)
 | 
			
		||||
#include <asm/GEN-for-each-reg.h>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ SYM_FUNC_START(__efi_call)
 | 
			
		|||
	mov %r8, %r9
 | 
			
		||||
	mov %rcx, %r8
 | 
			
		||||
	mov %rsi, %rcx
 | 
			
		||||
	CALL_NOSPEC %rdi
 | 
			
		||||
	CALL_NOSPEC rdi
 | 
			
		||||
	leave
 | 
			
		||||
	ret
 | 
			
		||||
SYM_FUNC_END(__efi_call)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,9 +15,20 @@
 | 
			
		|||
	static void __used __section(.discard.func_stack_frame_non_standard) \
 | 
			
		||||
		*__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 */
 | 
			
		||||
 | 
			
		||||
#define STACK_FRAME_NON_STANDARD(func)
 | 
			
		||||
#define ANNOTATE_INTRA_FUNCTION_CALL
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_STACK_VALIDATION */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -369,6 +369,11 @@ config STACK_VALIDATION
 | 
			
		|||
	  For more information, see
 | 
			
		||||
	  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
 | 
			
		||||
	bool "Force weak per-cpu definitions"
 | 
			
		||||
	depends on DEBUG_KERNEL
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,18 +20,22 @@ static unsigned long my_ip = (unsigned long)schedule;
 | 
			
		|||
 | 
			
		||||
asm (
 | 
			
		||||
"	.pushsection    .text, \"ax\", @progbits\n"
 | 
			
		||||
"	.type		my_tramp1, @function\n"
 | 
			
		||||
"   my_tramp1:"
 | 
			
		||||
"	pushq %rbp\n"
 | 
			
		||||
"	movq %rsp, %rbp\n"
 | 
			
		||||
"	call my_direct_func1\n"
 | 
			
		||||
"	leave\n"
 | 
			
		||||
"	.size		my_tramp1, .-my_tramp1\n"
 | 
			
		||||
"	ret\n"
 | 
			
		||||
"	.type		my_tramp2, @function\n"
 | 
			
		||||
"   my_tramp2:"
 | 
			
		||||
"	pushq %rbp\n"
 | 
			
		||||
"	movq %rsp, %rbp\n"
 | 
			
		||||
"	call my_direct_func2\n"
 | 
			
		||||
"	leave\n"
 | 
			
		||||
"	ret\n"
 | 
			
		||||
"	.size		my_tramp2, .-my_tramp2\n"
 | 
			
		||||
"	.popsection\n"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ extern void my_tramp(void *);
 | 
			
		|||
 | 
			
		||||
asm (
 | 
			
		||||
"	.pushsection    .text, \"ax\", @progbits\n"
 | 
			
		||||
"	.type		my_tramp, @function\n"
 | 
			
		||||
"   my_tramp:"
 | 
			
		||||
"	pushq %rbp\n"
 | 
			
		||||
"	movq %rsp, %rbp\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +28,7 @@ asm (
 | 
			
		|||
"	popq %rdi\n"
 | 
			
		||||
"	leave\n"
 | 
			
		||||
"	ret\n"
 | 
			
		||||
"	.size		my_tramp, .-my_tramp\n"
 | 
			
		||||
"	.popsection\n"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@ extern void my_tramp(void *);
 | 
			
		|||
 | 
			
		||||
asm (
 | 
			
		||||
"	.pushsection    .text, \"ax\", @progbits\n"
 | 
			
		||||
"	.type		my_tramp, @function\n"
 | 
			
		||||
"   my_tramp:"
 | 
			
		||||
"	pushq %rbp\n"
 | 
			
		||||
"	movq %rsp, %rbp\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +22,7 @@ asm (
 | 
			
		|||
"	popq %rdi\n"
 | 
			
		||||
"	leave\n"
 | 
			
		||||
"	ret\n"
 | 
			
		||||
"	.size		my_tramp, .-my_tramp\n"
 | 
			
		||||
"	.popsection\n"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,6 +55,29 @@ modpost_link()
 | 
			
		|||
	${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
 | 
			
		||||
# ${1} - output file
 | 
			
		||||
# ${2}, ${3}, ... - optional extra .o files
 | 
			
		||||
| 
						 | 
				
			
			@ -251,6 +274,7 @@ ${MAKE} -f "${srctree}/scripts/Makefile.build" obj=init need-builtin=1
 | 
			
		|||
#link vmlinux.o
 | 
			
		||||
info LD vmlinux.o
 | 
			
		||||
modpost_link vmlinux.o
 | 
			
		||||
objtool_link vmlinux.o
 | 
			
		||||
 | 
			
		||||
# modpost vmlinux.o to check for section mismatches
 | 
			
		||||
${MAKE} -f "${srctree}/scripts/Makefile.modpost" MODPOST_VMLINUX=1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,8 +58,7 @@
 | 
			
		|||
#define ORC_TYPE_CALL			0
 | 
			
		||||
#define ORC_TYPE_REGS			1
 | 
			
		||||
#define ORC_TYPE_REGS_IRET		2
 | 
			
		||||
#define UNWIND_HINT_TYPE_SAVE		3
 | 
			
		||||
#define UNWIND_HINT_TYPE_RESTORE	4
 | 
			
		||||
#define UNWIND_HINT_TYPE_RET_OFFSET	3
 | 
			
		||||
 | 
			
		||||
#ifndef __ASSEMBLY__
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,16 @@
 | 
			
		|||
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-orc.o
 | 
			
		||||
objtool-y += check.o
 | 
			
		||||
objtool-y += orc_gen.o
 | 
			
		||||
objtool-y += orc_dump.o
 | 
			
		||||
objtool-y += elf.o
 | 
			
		||||
objtool-y += special.o
 | 
			
		||||
objtool-y += objtool.o
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
      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.
 | 
			
		||||
Feel free to ask the objtool maintainer for help.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,8 @@ all: $(OBJTOOL)
 | 
			
		|||
 | 
			
		||||
INCLUDES := -I$(srctree)/tools/include \
 | 
			
		||||
	    -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
 | 
			
		||||
CFLAGS   := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
include $(srctree)/tools/build/Makefile.include
 | 
			
		||||
 | 
			
		||||
$(OBJTOOL_IN): fixdep FORCE
 | 
			
		||||
	@$(CONFIG_SHELL) ./sync-check.sh
 | 
			
		||||
	@$(MAKE) $(build)=objtool
 | 
			
		||||
 | 
			
		||||
$(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
 | 
			
		||||
	@$(CONFIG_SHELL) ./sync-check.sh
 | 
			
		||||
	$(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,9 +8,11 @@
 | 
			
		|||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <linux/list.h>
 | 
			
		||||
#include "elf.h"
 | 
			
		||||
#include "objtool.h"
 | 
			
		||||
#include "cfi.h"
 | 
			
		||||
 | 
			
		||||
#include <asm/orc_types.h>
 | 
			
		||||
 | 
			
		||||
enum insn_type {
 | 
			
		||||
	INSN_JUMP_CONDITIONAL,
 | 
			
		||||
	INSN_JUMP_UNCONDITIONAL,
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +22,6 @@ enum insn_type {
 | 
			
		|||
	INSN_CALL_DYNAMIC,
 | 
			
		||||
	INSN_RETURN,
 | 
			
		||||
	INSN_CONTEXT_SWITCH,
 | 
			
		||||
	INSN_STACK,
 | 
			
		||||
	INSN_BUG,
 | 
			
		||||
	INSN_NOP,
 | 
			
		||||
	INSN_STAC,
 | 
			
		||||
| 
						 | 
				
			
			@ -64,15 +65,23 @@ struct op_src {
 | 
			
		|||
struct stack_op {
 | 
			
		||||
	struct op_dest dest;
 | 
			
		||||
	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 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);
 | 
			
		||||
 | 
			
		||||
unsigned long arch_jump_destination(struct instruction *insn);
 | 
			
		||||
 | 
			
		||||
unsigned long arch_dest_rela_offset(int addend);
 | 
			
		||||
 | 
			
		||||
#endif /* _ARCH_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@
 | 
			
		|||
#include "../../../arch/x86/lib/inat.c"
 | 
			
		||||
#include "../../../arch/x86/lib/insn.c"
 | 
			
		||||
 | 
			
		||||
#include "../../check.h"
 | 
			
		||||
#include "../../elf.h"
 | 
			
		||||
#include "../../arch.h"
 | 
			
		||||
#include "../../warn.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +27,7 @@ static unsigned char op_to_cfi_reg[][2] = {
 | 
			
		|||
	{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) {
 | 
			
		||||
	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 int *len, enum insn_type *type,
 | 
			
		||||
			    unsigned long *immediate, struct stack_op *op)
 | 
			
		||||
			    unsigned long *immediate,
 | 
			
		||||
			    struct list_head *ops_list)
 | 
			
		||||
{
 | 
			
		||||
	struct insn insn;
 | 
			
		||||
	int x86_64, sign;
 | 
			
		||||
	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,
 | 
			
		||||
		      modrm_reg = 0, sib = 0;
 | 
			
		||||
	struct stack_op *op = NULL;
 | 
			
		||||
	struct symbol *sym;
 | 
			
		||||
 | 
			
		||||
	x86_64 = is_x86_64(elf);
 | 
			
		||||
	if (x86_64 == -1)
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +104,7 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
	insn_get_length(&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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
 | 
			
		||||
			/* add/sub reg, %rsp */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_ADD;
 | 
			
		||||
			op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = CFI_SP;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_ADD;
 | 
			
		||||
				op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = CFI_SP;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0x50 ... 0x57:
 | 
			
		||||
 | 
			
		||||
		/* push reg */
 | 
			
		||||
		*type = INSN_STACK;
 | 
			
		||||
		op->src.type = OP_SRC_REG;
 | 
			
		||||
		op->src.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
 | 
			
		||||
		op->dest.type = OP_DEST_PUSH;
 | 
			
		||||
		ADD_OP(op) {
 | 
			
		||||
			op->src.type = OP_SRC_REG;
 | 
			
		||||
			op->src.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
 | 
			
		||||
			op->dest.type = OP_DEST_PUSH;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0x58 ... 0x5f:
 | 
			
		||||
 | 
			
		||||
		/* pop reg */
 | 
			
		||||
		*type = INSN_STACK;
 | 
			
		||||
		op->src.type = OP_SRC_POP;
 | 
			
		||||
		op->dest.type = OP_DEST_REG;
 | 
			
		||||
		op->dest.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
 | 
			
		||||
		ADD_OP(op) {
 | 
			
		||||
			op->src.type = OP_SRC_POP;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0x68:
 | 
			
		||||
	case 0x6a:
 | 
			
		||||
		/* push immediate */
 | 
			
		||||
		*type = INSN_STACK;
 | 
			
		||||
		op->src.type = OP_SRC_CONST;
 | 
			
		||||
		op->dest.type = OP_DEST_PUSH;
 | 
			
		||||
		ADD_OP(op) {
 | 
			
		||||
			op->src.type = OP_SRC_CONST;
 | 
			
		||||
			op->dest.type = OP_DEST_PUSH;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0x70 ... 0x7f:
 | 
			
		||||
| 
						 | 
				
			
			@ -170,12 +193,13 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
 | 
			
		||||
		if (modrm == 0xe4) {
 | 
			
		||||
			/* and imm, %rsp */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_AND;
 | 
			
		||||
			op->src.reg = CFI_SP;
 | 
			
		||||
			op->src.offset = insn.immediate.value;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = CFI_SP;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_AND;
 | 
			
		||||
				op->src.reg = CFI_SP;
 | 
			
		||||
				op->src.offset = insn.immediate.value;
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = CFI_SP;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -187,34 +211,37 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
			break;
 | 
			
		||||
 | 
			
		||||
		/* add/sub imm, %rsp */
 | 
			
		||||
		*type = INSN_STACK;
 | 
			
		||||
		op->src.type = OP_SRC_ADD;
 | 
			
		||||
		op->src.reg = CFI_SP;
 | 
			
		||||
		op->src.offset = insn.immediate.value * sign;
 | 
			
		||||
		op->dest.type = OP_DEST_REG;
 | 
			
		||||
		op->dest.reg = CFI_SP;
 | 
			
		||||
		ADD_OP(op) {
 | 
			
		||||
			op->src.type = OP_SRC_ADD;
 | 
			
		||||
			op->src.reg = CFI_SP;
 | 
			
		||||
			op->src.offset = insn.immediate.value * sign;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = CFI_SP;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0x89:
 | 
			
		||||
		if (rex_w && !rex_r && modrm_mod == 3 && modrm_reg == 4) {
 | 
			
		||||
 | 
			
		||||
			/* mov %rsp, reg */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_REG;
 | 
			
		||||
			op->src.reg = CFI_SP;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b];
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_REG;
 | 
			
		||||
				op->src.reg = CFI_SP;
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b];
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) {
 | 
			
		||||
 | 
			
		||||
			/* mov reg, %rsp */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_REG;
 | 
			
		||||
			op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = CFI_SP;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_REG;
 | 
			
		||||
				op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = CFI_SP;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -224,22 +251,24 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
		    (modrm_mod == 1 || modrm_mod == 2) && modrm_rm == 5) {
 | 
			
		||||
 | 
			
		||||
			/* mov reg, disp(%rbp) */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_REG;
 | 
			
		||||
			op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
			op->dest.type = OP_DEST_REG_INDIRECT;
 | 
			
		||||
			op->dest.reg = CFI_BP;
 | 
			
		||||
			op->dest.offset = insn.displacement.value;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_REG;
 | 
			
		||||
				op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
				op->dest.type = OP_DEST_REG_INDIRECT;
 | 
			
		||||
				op->dest.reg = CFI_BP;
 | 
			
		||||
				op->dest.offset = insn.displacement.value;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) {
 | 
			
		||||
 | 
			
		||||
			/* mov reg, disp(%rsp) */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_REG;
 | 
			
		||||
			op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
			op->dest.type = OP_DEST_REG_INDIRECT;
 | 
			
		||||
			op->dest.reg = CFI_SP;
 | 
			
		||||
			op->dest.offset = insn.displacement.value;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_REG;
 | 
			
		||||
				op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
				op->dest.type = OP_DEST_REG_INDIRECT;
 | 
			
		||||
				op->dest.reg = CFI_SP;
 | 
			
		||||
				op->dest.offset = insn.displacement.value;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			@ -248,23 +277,25 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
		if (rex_w && !rex_b && modrm_mod == 1 && modrm_rm == 5) {
 | 
			
		||||
 | 
			
		||||
			/* mov disp(%rbp), reg */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_REG_INDIRECT;
 | 
			
		||||
			op->src.reg = CFI_BP;
 | 
			
		||||
			op->src.offset = insn.displacement.value;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_REG_INDIRECT;
 | 
			
		||||
				op->src.reg = CFI_BP;
 | 
			
		||||
				op->src.offset = insn.displacement.value;
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else if (rex_w && !rex_b && sib == 0x24 &&
 | 
			
		||||
			   modrm_mod != 3 && modrm_rm == 4) {
 | 
			
		||||
 | 
			
		||||
			/* mov disp(%rsp), reg */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_REG_INDIRECT;
 | 
			
		||||
			op->src.reg = CFI_SP;
 | 
			
		||||
			op->src.offset = insn.displacement.value;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_REG_INDIRECT;
 | 
			
		||||
				op->src.reg = CFI_SP;
 | 
			
		||||
				op->src.offset = insn.displacement.value;
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			@ -272,28 +303,30 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
	case 0x8d:
 | 
			
		||||
		if (sib == 0x24 && rex_w && !rex_b && !rex_x) {
 | 
			
		||||
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			if (!insn.displacement.value) {
 | 
			
		||||
				/* lea (%rsp), reg */
 | 
			
		||||
				op->src.type = OP_SRC_REG;
 | 
			
		||||
			} else {
 | 
			
		||||
				/* lea disp(%rsp), reg */
 | 
			
		||||
				op->src.type = OP_SRC_ADD;
 | 
			
		||||
				op->src.offset = insn.displacement.value;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				if (!insn.displacement.value) {
 | 
			
		||||
					/* lea (%rsp), reg */
 | 
			
		||||
					op->src.type = OP_SRC_REG;
 | 
			
		||||
				} else {
 | 
			
		||||
					/* lea disp(%rsp), reg */
 | 
			
		||||
					op->src.type = OP_SRC_ADD;
 | 
			
		||||
					op->src.offset = insn.displacement.value;
 | 
			
		||||
				}
 | 
			
		||||
				op->src.reg = CFI_SP;
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
			}
 | 
			
		||||
			op->src.reg = CFI_SP;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
 | 
			
		||||
 | 
			
		||||
		} else if (rex == 0x48 && modrm == 0x65) {
 | 
			
		||||
 | 
			
		||||
			/* lea disp(%rbp), %rsp */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_ADD;
 | 
			
		||||
			op->src.reg = CFI_BP;
 | 
			
		||||
			op->src.offset = insn.displacement.value;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = CFI_SP;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_ADD;
 | 
			
		||||
				op->src.reg = CFI_BP;
 | 
			
		||||
				op->src.offset = insn.displacement.value;
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = CFI_SP;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else if (rex == 0x49 && modrm == 0x62 &&
 | 
			
		||||
			   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
 | 
			
		||||
			 * stack realignment.
 | 
			
		||||
			 */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_ADD;
 | 
			
		||||
			op->src.reg = CFI_R10;
 | 
			
		||||
			op->src.offset = -8;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = CFI_SP;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_ADD;
 | 
			
		||||
				op->src.reg = CFI_R10;
 | 
			
		||||
				op->src.offset = -8;
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = CFI_SP;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else if (rex == 0x49 && modrm == 0x65 &&
 | 
			
		||||
			   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
 | 
			
		||||
			 * stack realignment.
 | 
			
		||||
			 */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_ADD;
 | 
			
		||||
			op->src.reg = CFI_R13;
 | 
			
		||||
			op->src.offset = -16;
 | 
			
		||||
			op->dest.type = OP_DEST_REG;
 | 
			
		||||
			op->dest.reg = CFI_SP;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_ADD;
 | 
			
		||||
				op->src.reg = CFI_R13;
 | 
			
		||||
				op->src.offset = -16;
 | 
			
		||||
				op->dest.type = OP_DEST_REG;
 | 
			
		||||
				op->dest.reg = CFI_SP;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0x8f:
 | 
			
		||||
		/* pop to mem */
 | 
			
		||||
		*type = INSN_STACK;
 | 
			
		||||
		op->src.type = OP_SRC_POP;
 | 
			
		||||
		op->dest.type = OP_DEST_MEM;
 | 
			
		||||
		ADD_OP(op) {
 | 
			
		||||
			op->src.type = OP_SRC_POP;
 | 
			
		||||
			op->dest.type = OP_DEST_MEM;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0x90:
 | 
			
		||||
| 
						 | 
				
			
			@ -343,16 +379,18 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
 | 
			
		||||
	case 0x9c:
 | 
			
		||||
		/* pushf */
 | 
			
		||||
		*type = INSN_STACK;
 | 
			
		||||
		op->src.type = OP_SRC_CONST;
 | 
			
		||||
		op->dest.type = OP_DEST_PUSHF;
 | 
			
		||||
		ADD_OP(op) {
 | 
			
		||||
			op->src.type = OP_SRC_CONST;
 | 
			
		||||
			op->dest.type = OP_DEST_PUSHF;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0x9d:
 | 
			
		||||
		/* popf */
 | 
			
		||||
		*type = INSN_STACK;
 | 
			
		||||
		op->src.type = OP_SRC_POPF;
 | 
			
		||||
		op->dest.type = OP_DEST_MEM;
 | 
			
		||||
		ADD_OP(op) {
 | 
			
		||||
			op->src.type = OP_SRC_POPF;
 | 
			
		||||
			op->dest.type = OP_DEST_MEM;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0x0f:
 | 
			
		||||
| 
						 | 
				
			
			@ -387,16 +425,18 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
		} else if (op2 == 0xa0 || op2 == 0xa8) {
 | 
			
		||||
 | 
			
		||||
			/* push fs/gs */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_CONST;
 | 
			
		||||
			op->dest.type = OP_DEST_PUSH;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_CONST;
 | 
			
		||||
				op->dest.type = OP_DEST_PUSH;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else if (op2 == 0xa1 || op2 == 0xa9) {
 | 
			
		||||
 | 
			
		||||
			/* pop fs/gs */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_POP;
 | 
			
		||||
			op->dest.type = OP_DEST_MEM;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_POP;
 | 
			
		||||
				op->dest.type = OP_DEST_MEM;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			@ -409,8 +449,8 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
		 * mov bp, sp
 | 
			
		||||
		 * pop bp
 | 
			
		||||
		 */
 | 
			
		||||
		*type = INSN_STACK;
 | 
			
		||||
		op->dest.type = OP_DEST_LEAVE;
 | 
			
		||||
		ADD_OP(op)
 | 
			
		||||
			op->dest.type = OP_DEST_LEAVE;
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -429,14 +469,41 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
		*type = INSN_RETURN;
 | 
			
		||||
		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 0xcb: /* retf */
 | 
			
		||||
	case 0xcf: /* iret */
 | 
			
		||||
		*type = INSN_CONTEXT_SWITCH;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 0xe8:
 | 
			
		||||
		*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;
 | 
			
		||||
 | 
			
		||||
	case 0xfc:
 | 
			
		||||
| 
						 | 
				
			
			@ -464,9 +531,10 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
		else if (modrm_reg == 6) {
 | 
			
		||||
 | 
			
		||||
			/* push from mem */
 | 
			
		||||
			*type = INSN_STACK;
 | 
			
		||||
			op->src.type = OP_SRC_CONST;
 | 
			
		||||
			op->dest.type = OP_DEST_PUSH;
 | 
			
		||||
			ADD_OP(op) {
 | 
			
		||||
				op->src.type = OP_SRC_CONST;
 | 
			
		||||
				op->dest.type = OP_DEST_PUSH;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			@ -480,7 +548,7 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
 | 
			
		|||
	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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										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 <string.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[] = {
 | 
			
		||||
	"objtool check [<options>] file.o",
 | 
			
		||||
| 
						 | 
				
			
			@ -32,12 +33,14 @@ const struct option check_options[] = {
 | 
			
		|||
	OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
 | 
			
		||||
	OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
 | 
			
		||||
	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(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,5 +49,9 @@ int cmd_check(int argc, const char **argv)
 | 
			
		|||
 | 
			
		||||
	objname = argv[0];
 | 
			
		||||
 | 
			
		||||
	s = strstr(objname, "vmlinux.o");
 | 
			
		||||
	if (s && !s[9])
 | 
			
		||||
		vmlinux = true;
 | 
			
		||||
 | 
			
		||||
	return check(objname, false);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,8 +14,7 @@
 | 
			
		|||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "builtin.h"
 | 
			
		||||
#include "check.h"
 | 
			
		||||
 | 
			
		||||
#include "objtool.h"
 | 
			
		||||
 | 
			
		||||
static const char *orc_usage[] = {
 | 
			
		||||
	"objtool orc generate [<options>] file.o",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@
 | 
			
		|||
#include <subcmd/parse-options.h>
 | 
			
		||||
 | 
			
		||||
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_orc(int argc, const char **argv);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,38 +6,33 @@
 | 
			
		|||
#ifndef _OBJTOOL_CFI_H
 | 
			
		||||
#define _OBJTOOL_CFI_H
 | 
			
		||||
 | 
			
		||||
#include "cfi_regs.h"
 | 
			
		||||
 | 
			
		||||
#define CFI_UNDEFINED		-1
 | 
			
		||||
#define CFI_CFA			-2
 | 
			
		||||
#define CFI_SP_INDIRECT		-3
 | 
			
		||||
#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 {
 | 
			
		||||
	int base;
 | 
			
		||||
	int offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct cfi_state {
 | 
			
		||||
	struct cfi_reg cfa;
 | 
			
		||||
struct cfi_init_state {
 | 
			
		||||
	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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -7,22 +7,16 @@
 | 
			
		|||
#define _CHECK_H
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include "elf.h"
 | 
			
		||||
#include "cfi.h"
 | 
			
		||||
#include "arch.h"
 | 
			
		||||
#include "orc.h"
 | 
			
		||||
#include <linux/hashtable.h>
 | 
			
		||||
 | 
			
		||||
struct insn_state {
 | 
			
		||||
	struct cfi_reg cfa;
 | 
			
		||||
	struct cfi_reg regs[CFI_NUM_REGS];
 | 
			
		||||
	int stack_size;
 | 
			
		||||
	unsigned char type;
 | 
			
		||||
	bool bp_scratch;
 | 
			
		||||
	bool drap, end, uaccess, df;
 | 
			
		||||
	struct cfi_state cfi;
 | 
			
		||||
	unsigned int uaccess_stack;
 | 
			
		||||
	int drap_reg, drap_offset;
 | 
			
		||||
	struct cfi_reg vals[CFI_NUM_REGS];
 | 
			
		||||
	bool uaccess;
 | 
			
		||||
	bool df;
 | 
			
		||||
	bool noinstr;
 | 
			
		||||
	s8 instr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct instruction {
 | 
			
		||||
| 
						 | 
				
			
			@ -33,29 +27,24 @@ struct instruction {
 | 
			
		|||
	unsigned int len;
 | 
			
		||||
	enum insn_type type;
 | 
			
		||||
	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;
 | 
			
		||||
	s8 instr;
 | 
			
		||||
	u8 visited;
 | 
			
		||||
	u8 ret_offset;
 | 
			
		||||
	int alt_group;
 | 
			
		||||
	struct symbol *call_dest;
 | 
			
		||||
	struct instruction *jump_dest;
 | 
			
		||||
	struct instruction *first_jump_src;
 | 
			
		||||
	struct rela *jump_table;
 | 
			
		||||
	struct list_head alts;
 | 
			
		||||
	struct symbol *func;
 | 
			
		||||
	struct stack_op stack_op;
 | 
			
		||||
	struct insn_state state;
 | 
			
		||||
	struct list_head stack_ops;
 | 
			
		||||
	struct cfi_state cfi;
 | 
			
		||||
	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 section *sec, unsigned long offset);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,22 @@ static inline u32 str_hash(const char *str)
 | 
			
		|||
	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,
 | 
			
		||||
		   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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 *))
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
	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))
 | 
			
		||||
			return sec;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -127,7 +143,7 @@ static struct section *find_section_by_index(struct elf *elf,
 | 
			
		|||
{
 | 
			
		||||
	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)
 | 
			
		||||
			return sec;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -138,7 +154,7 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
 | 
			
		|||
{
 | 
			
		||||
	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)
 | 
			
		||||
			return sym;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +189,7 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
 | 
			
		|||
	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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -201,18 +217,18 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset)
 | 
			
		|||
	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;
 | 
			
		||||
 | 
			
		||||
	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))
 | 
			
		||||
			return sym;
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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)) {
 | 
			
		||||
			if (rela->sec != sec)
 | 
			
		||||
				continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +257,7 @@ struct rela *find_rela_by_dest_range(struct elf *elf, struct section *sec,
 | 
			
		|||
	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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -309,8 +325,8 @@ static int read_sections(struct elf *elf)
 | 
			
		|||
		sec->len = sec->sh.sh_size;
 | 
			
		||||
 | 
			
		||||
		list_add_tail(&sec->list, &elf->sections);
 | 
			
		||||
		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_hash, &sec->hash, sec->idx);
 | 
			
		||||
		elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (stats)
 | 
			
		||||
| 
						 | 
				
			
			@ -327,12 +343,14 @@ static int read_sections(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 list_head *entry;
 | 
			
		||||
	struct rb_node *pnode;
 | 
			
		||||
	int symbols_nr, i;
 | 
			
		||||
	char *coldstr;
 | 
			
		||||
	Elf_Data *shndx_data = NULL;
 | 
			
		||||
	Elf32_Word shndx;
 | 
			
		||||
 | 
			
		||||
	symtab = find_section_by_name(elf, ".symtab");
 | 
			
		||||
	if (!symtab) {
 | 
			
		||||
| 
						 | 
				
			
			@ -340,6 +358,10 @@ static int read_symbols(struct elf *elf)
 | 
			
		|||
		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;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < symbols_nr; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -353,8 +375,9 @@ static int read_symbols(struct elf *elf)
 | 
			
		|||
 | 
			
		||||
		sym->idx = i;
 | 
			
		||||
 | 
			
		||||
		if (!gelf_getsym(symtab->data, i, &sym->sym)) {
 | 
			
		||||
			WARN_ELF("gelf_getsym");
 | 
			
		||||
		if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym,
 | 
			
		||||
				      &shndx)) {
 | 
			
		||||
			WARN_ELF("gelf_getsymshndx");
 | 
			
		||||
			goto err;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -368,10 +391,13 @@ static int read_symbols(struct elf *elf)
 | 
			
		|||
		sym->type = GELF_ST_TYPE(sym->sym.st_info);
 | 
			
		||||
		sym->bind = GELF_ST_BIND(sym->sym.st_info);
 | 
			
		||||
 | 
			
		||||
		if (sym->sym.st_shndx > SHN_UNDEF &&
 | 
			
		||||
		    sym->sym.st_shndx < SHN_LORESERVE) {
 | 
			
		||||
			sym->sec = find_section_by_index(elf,
 | 
			
		||||
							 sym->sym.st_shndx);
 | 
			
		||||
		if ((sym->sym.st_shndx > SHN_UNDEF &&
 | 
			
		||||
		     sym->sym.st_shndx < SHN_LORESERVE) ||
 | 
			
		||||
		    (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) {
 | 
			
		||||
			if (sym->sym.st_shndx != SHN_XINDEX)
 | 
			
		||||
				shndx = sym->sym.st_shndx;
 | 
			
		||||
 | 
			
		||||
			sym->sec = find_section_by_index(elf, shndx);
 | 
			
		||||
			if (!sym->sec) {
 | 
			
		||||
				WARN("couldn't find section for symbol %s",
 | 
			
		||||
				     sym->name);
 | 
			
		||||
| 
						 | 
				
			
			@ -394,8 +420,8 @@ static int read_symbols(struct elf *elf)
 | 
			
		|||
		else
 | 
			
		||||
			entry = &sym->sec->symbol_list;
 | 
			
		||||
		list_add(&sym->list, entry);
 | 
			
		||||
		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_hash, &sym->hash, sym->idx);
 | 
			
		||||
		elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (stats)
 | 
			
		||||
| 
						 | 
				
			
			@ -456,6 +482,14 @@ static int read_symbols(struct elf *elf)
 | 
			
		|||
	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)
 | 
			
		||||
{
 | 
			
		||||
	struct section *sec;
 | 
			
		||||
| 
						 | 
				
			
			@ -503,8 +537,7 @@ static int read_relas(struct elf *elf)
 | 
			
		|||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			list_add_tail(&rela->list, &sec->rela_list);
 | 
			
		||||
			hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
 | 
			
		||||
			elf_add_rela(elf, rela);
 | 
			
		||||
			nr_rela++;
 | 
			
		||||
		}
 | 
			
		||||
		max_rela = max(max_rela, nr_rela);
 | 
			
		||||
| 
						 | 
				
			
			@ -519,7 +552,7 @@ static int read_relas(struct elf *elf)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct elf *elf_read(const char *name, int flags)
 | 
			
		||||
struct elf *elf_open_read(const char *name, int flags)
 | 
			
		||||
{
 | 
			
		||||
	struct elf *elf;
 | 
			
		||||
	Elf_Cmd cmd;
 | 
			
		||||
| 
						 | 
				
			
			@ -531,15 +564,16 @@ struct elf *elf_read(const char *name, int flags)
 | 
			
		|||
		perror("malloc");
 | 
			
		||||
		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);
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	if (elf->fd == -1) {
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
	list_add_tail(&sec->list, &elf->sections);
 | 
			
		||||
	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_hash, &sec->hash, sec->idx);
 | 
			
		||||
	elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
 | 
			
		||||
 | 
			
		||||
	return sec;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -745,7 +779,7 @@ int elf_rebuild_rela_section(struct section *sec)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int elf_write(struct elf *elf)
 | 
			
		||||
int elf_write(const struct elf *elf)
 | 
			
		||||
{
 | 
			
		||||
	struct section *sec;
 | 
			
		||||
	Elf_Scn *s;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ struct section {
 | 
			
		|||
	char *name;
 | 
			
		||||
	int idx;
 | 
			
		||||
	unsigned int len;
 | 
			
		||||
	bool changed, text, rodata;
 | 
			
		||||
	bool changed, text, rodata, noinstr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct symbol {
 | 
			
		||||
| 
						 | 
				
			
			@ -70,17 +70,19 @@ struct rela {
 | 
			
		|||
	bool jump_table_start;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define ELF_HASH_BITS	20
 | 
			
		||||
 | 
			
		||||
struct elf {
 | 
			
		||||
	Elf *elf;
 | 
			
		||||
	GElf_Ehdr ehdr;
 | 
			
		||||
	int fd;
 | 
			
		||||
	char *name;
 | 
			
		||||
	struct list_head sections;
 | 
			
		||||
	DECLARE_HASHTABLE(symbol_hash, 20);
 | 
			
		||||
	DECLARE_HASHTABLE(symbol_name_hash, 20);
 | 
			
		||||
	DECLARE_HASHTABLE(section_hash, 16);
 | 
			
		||||
	DECLARE_HASHTABLE(section_name_hash, 16);
 | 
			
		||||
	DECLARE_HASHTABLE(rela_hash, 20);
 | 
			
		||||
	DECLARE_HASHTABLE(symbol_hash, ELF_HASH_BITS);
 | 
			
		||||
	DECLARE_HASHTABLE(symbol_name_hash, ELF_HASH_BITS);
 | 
			
		||||
	DECLARE_HASHTABLE(section_hash, ELF_HASH_BITS);
 | 
			
		||||
	DECLARE_HASHTABLE(section_name_hash, ELF_HASH_BITS);
 | 
			
		||||
	DECLARE_HASHTABLE(rela_hash, ELF_HASH_BITS);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct elf *elf_read(const char *name, int flags);
 | 
			
		||||
struct section *find_section_by_name(struct elf *elf, const char *name);
 | 
			
		||||
struct elf *elf_open_read(const char *name, int flags);
 | 
			
		||||
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_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_containing(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_range(struct elf *elf, struct section *sec,
 | 
			
		||||
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
 | 
			
		||||
struct symbol *find_symbol_containing(const 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(const struct elf *elf, struct section *sec,
 | 
			
		||||
				     unsigned long offset, unsigned int len);
 | 
			
		||||
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_write(struct elf *elf);
 | 
			
		||||
void elf_close(struct elf *elf);
 | 
			
		||||
 | 
			
		||||
#define for_each_sec(file, sec)						\
 | 
			
		||||
	list_for_each_entry(sec, &file->elf->sections, list)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,7 +58,9 @@ static void cmd_usage(void)
 | 
			
		|||
 | 
			
		||||
	printf("\n");
 | 
			
		||||
 | 
			
		||||
	exit(129);
 | 
			
		||||
	if (!help)
 | 
			
		||||
		exit(129);
 | 
			
		||||
	exit(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 "orc.h"
 | 
			
		||||
#include <asm/orc_types.h>
 | 
			
		||||
#include "objtool.h"
 | 
			
		||||
#include "warn.h"
 | 
			
		||||
 | 
			
		||||
static const char *reg_name(unsigned int reg)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,6 @@
 | 
			
		|||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "orc.h"
 | 
			
		||||
#include "check.h"
 | 
			
		||||
#include "warn.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -16,10 +15,10 @@ int create_orc(struct objtool_file *file)
 | 
			
		|||
 | 
			
		||||
	for_each_insn(file, insn) {
 | 
			
		||||
		struct orc_entry *orc = &insn->orc;
 | 
			
		||||
		struct cfi_reg *cfa = &insn->state.cfa;
 | 
			
		||||
		struct cfi_reg *bp = &insn->state.regs[CFI_BP];
 | 
			
		||||
		struct cfi_reg *cfa = &insn->cfi.cfa;
 | 
			
		||||
		struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
 | 
			
		||||
 | 
			
		||||
		orc->end = insn->state.end;
 | 
			
		||||
		orc->end = insn->cfi.end;
 | 
			
		||||
 | 
			
		||||
		if (cfa->base == CFI_UNDEFINED) {
 | 
			
		||||
			orc->sp_reg = ORC_REG_UNDEFINED;
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +74,7 @@ int create_orc(struct objtool_file *file)
 | 
			
		|||
 | 
			
		||||
		orc->sp_offset = cfa->offset;
 | 
			
		||||
		orc->bp_offset = bp->offset;
 | 
			
		||||
		orc->type = insn->state.type;
 | 
			
		||||
		orc->type = insn->cfi.type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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->sec = ip_relasec;
 | 
			
		||||
 | 
			
		||||
	list_add_tail(&rela->list, &ip_relasec->rela_list);
 | 
			
		||||
	hash_add(elf->rela_hash, &rela->hash, rela_hash(rela));
 | 
			
		||||
	elf_add_rela(elf, rela);
 | 
			
		||||
 | 
			
		||||
	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…
	
		Reference in a new issue