mirror of
https://github.com/hardkernel/linux.git
synced 2026-03-24 19:40:21 +09:00
Merge 4.9.106 into android-4.9
Changes in 4.9.106
objtool: Improve detection of BUG() and other dead ends
objtool: Move checking code to check.c
tools lib: Add for_each_clear_bit macro
tools: add more bitmap functions
tools: enable endian checks for all sparse builds
tools include: Introduce linux/compiler-gcc.h
radix tree test suite: Remove types.h
tools include: Adopt __compiletime_error
tools include: Introduce atomic_cmpxchg_{relaxed,release}()
tools include: Add UINT_MAX def to kernel.h
tools include: Adopt kernel's refcount.h
perf tools: Force fixdep compilation at the start of the build
perf tools: Move headers check into bash script
tools include uapi: Grab copies of stat.h and fcntl.h
tools include: Introduce linux/bug.h, from the kernel sources
tools include: Adopt __same_type() and __must_be_array() from the kernel
tools include: Move ARRAY_SIZE() to linux/kernel.h
tools include: Drop ARRAY_SIZE() definition from linux/hashtable.h
tools include: Include missing headers for fls() and types in linux/log2.h
objtool: sync up with the 4.14.47 version of objtool
objtool: Support GCC 8's cold subfunctions
objtool: Support GCC 8 switch tables
objtool: Detect RIP-relative switch table references
objtool: Detect RIP-relative switch table references, part 2
objtool: Fix "noreturn" detection for recursive sibling calls
objtool, x86: Add several functions and files to the objtool whitelist
perf/tools: header file sync up
objtool: header file sync-up
x86/xen: Add unwind hint annotations to xen_setup_gdt
objtool: Enclose contents of unreachable() macro in a block
Linux 4.9.106
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
||||
VERSION = 4
|
||||
PATCHLEVEL = 9
|
||||
SUBLEVEL = 105
|
||||
SUBLEVEL = 106
|
||||
EXTRAVERSION =
|
||||
NAME = Roaring Lionus
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# Arch-specific CryptoAPI modules.
|
||||
#
|
||||
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
|
||||
avx_supported := $(call as-instr,vpxor %xmm0$(comma)%xmm0$(comma)%xmm0,yes,no)
|
||||
avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\
|
||||
$(comma)4)$(comma)%ymm2,yes,no)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# Arch-specific CryptoAPI modules.
|
||||
#
|
||||
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
|
||||
avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\
|
||||
$(comma)4)$(comma)%ymm2,yes,no)
|
||||
ifeq ($(avx2_supported),yes)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# Arch-specific CryptoAPI modules.
|
||||
#
|
||||
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
|
||||
avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\
|
||||
$(comma)4)$(comma)%ymm2,yes,no)
|
||||
ifeq ($(avx2_supported),yes)
|
||||
|
||||
107
arch/x86/include/asm/orc_types.h
Normal file
107
arch/x86/include/asm/orc_types.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _ORC_TYPES_H
|
||||
#define _ORC_TYPES_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
/*
|
||||
* The ORC_REG_* registers are base registers which are used to find other
|
||||
* registers on the stack.
|
||||
*
|
||||
* ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
|
||||
* address of the previous frame: the caller's SP before it called the current
|
||||
* function.
|
||||
*
|
||||
* ORC_REG_UNDEFINED means the corresponding register's value didn't change in
|
||||
* the current frame.
|
||||
*
|
||||
* The most commonly used base registers are SP and BP -- which the previous SP
|
||||
* is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is
|
||||
* usually based on.
|
||||
*
|
||||
* The rest of the base registers are needed for special cases like entry code
|
||||
* and GCC realigned stacks.
|
||||
*/
|
||||
#define ORC_REG_UNDEFINED 0
|
||||
#define ORC_REG_PREV_SP 1
|
||||
#define ORC_REG_DX 2
|
||||
#define ORC_REG_DI 3
|
||||
#define ORC_REG_BP 4
|
||||
#define ORC_REG_SP 5
|
||||
#define ORC_REG_R10 6
|
||||
#define ORC_REG_R13 7
|
||||
#define ORC_REG_BP_INDIRECT 8
|
||||
#define ORC_REG_SP_INDIRECT 9
|
||||
#define ORC_REG_MAX 15
|
||||
|
||||
/*
|
||||
* ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
|
||||
* caller's SP right before it made the call). Used for all callable
|
||||
* functions, i.e. all C code and all callable asm functions.
|
||||
*
|
||||
* ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
|
||||
* to a fully populated pt_regs from a syscall, interrupt, or exception.
|
||||
*
|
||||
* ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
|
||||
* points to the iret return frame.
|
||||
*
|
||||
* The UNWIND_HINT macros are used only for the unwind_hint struct. They
|
||||
* aren't used in struct orc_entry due to size and complexity constraints.
|
||||
* Objtool converts them to real types when it converts the hints to orc
|
||||
* entries.
|
||||
*/
|
||||
#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
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/*
|
||||
* This struct is more or less a vastly simplified version of the DWARF Call
|
||||
* Frame Information standard. It contains only the necessary parts of DWARF
|
||||
* CFI, simplified for ease of access by the in-kernel unwinder. It tells the
|
||||
* unwinder how to find the previous SP and BP (and sometimes entry regs) on
|
||||
* the stack for a given code address. Each instance of the struct corresponds
|
||||
* to one or more code locations.
|
||||
*/
|
||||
struct orc_entry {
|
||||
s16 sp_offset;
|
||||
s16 bp_offset;
|
||||
unsigned sp_reg:4;
|
||||
unsigned bp_reg:4;
|
||||
unsigned type:2;
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct is used by asm and inline asm code to manually annotate the
|
||||
* location of registers on the stack for the ORC unwinder.
|
||||
*
|
||||
* Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
|
||||
*/
|
||||
struct unwind_hint {
|
||||
u32 ip;
|
||||
s16 sp_offset;
|
||||
u8 sp_reg;
|
||||
u8 type;
|
||||
};
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* _ORC_TYPES_H */
|
||||
103
arch/x86/include/asm/unwind_hints.h
Normal file
103
arch/x86/include/asm/unwind_hints.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef _ASM_X86_UNWIND_HINTS_H
|
||||
#define _ASM_X86_UNWIND_HINTS_H
|
||||
|
||||
#include "orc_types.h"
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
/*
|
||||
* In asm, there are two kinds of code: normal C-type callable functions and
|
||||
* the rest. The normal callable functions can be called by other code, and
|
||||
* don't do anything unusual with the stack. Such normal callable functions
|
||||
* are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this
|
||||
* category. In this case, no special debugging annotations are needed because
|
||||
* objtool can automatically generate the ORC data for the ORC unwinder to read
|
||||
* at runtime.
|
||||
*
|
||||
* Anything which doesn't fall into the above category, such as syscall and
|
||||
* interrupt handlers, tends to not be called directly by other functions, and
|
||||
* often does unusual non-C-function-type things with the stack pointer. Such
|
||||
* code needs to be annotated such that objtool can understand it. The
|
||||
* following CFI hint macros are for this type of code.
|
||||
*
|
||||
* These macros provide hints to objtool about the state of the stack at each
|
||||
* instruction. Objtool starts from the hints and follows the code flow,
|
||||
* making automatic CFI adjustments when it sees pushes and pops, filling out
|
||||
* the debuginfo as necessary. It will also warn if it sees any
|
||||
* inconsistencies.
|
||||
*/
|
||||
.macro UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=0 type=ORC_TYPE_CALL
|
||||
#ifdef CONFIG_STACK_VALIDATION
|
||||
.Lunwind_hint_ip_\@:
|
||||
.pushsection .discard.unwind_hints
|
||||
/* struct unwind_hint */
|
||||
.long .Lunwind_hint_ip_\@ - .
|
||||
.short \sp_offset
|
||||
.byte \sp_reg
|
||||
.byte \type
|
||||
.popsection
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_EMPTY
|
||||
UNWIND_HINT sp_reg=ORC_REG_UNDEFINED
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 iret=0
|
||||
.if \base == %rsp && \indirect
|
||||
.set sp_reg, ORC_REG_SP_INDIRECT
|
||||
.elseif \base == %rsp
|
||||
.set sp_reg, ORC_REG_SP
|
||||
.elseif \base == %rbp
|
||||
.set sp_reg, ORC_REG_BP
|
||||
.elseif \base == %rdi
|
||||
.set sp_reg, ORC_REG_DI
|
||||
.elseif \base == %rdx
|
||||
.set sp_reg, ORC_REG_DX
|
||||
.elseif \base == %r10
|
||||
.set sp_reg, ORC_REG_R10
|
||||
.else
|
||||
.error "UNWIND_HINT_REGS: bad base register"
|
||||
.endif
|
||||
|
||||
.set sp_offset, \offset
|
||||
|
||||
.if \iret
|
||||
.set type, ORC_TYPE_REGS_IRET
|
||||
.elseif \extra == 0
|
||||
.set type, ORC_TYPE_REGS_IRET
|
||||
.set sp_offset, \offset + (16*8)
|
||||
.else
|
||||
.set type, ORC_TYPE_REGS
|
||||
.endif
|
||||
|
||||
UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0
|
||||
UNWIND_HINT_REGS base=\base offset=\offset iret=1
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_FUNC sp_offset=8
|
||||
UNWIND_HINT sp_offset=\sp_offset
|
||||
.endm
|
||||
|
||||
#else /* !__ASSEMBLY__ */
|
||||
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type) \
|
||||
"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" \
|
||||
".popsection\n\t"
|
||||
|
||||
#define UNWIND_HINT_SAVE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_SAVE)
|
||||
|
||||
#define UNWIND_HINT_RESTORE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_RESTORE)
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* _ASM_X86_UNWIND_HINTS_H */
|
||||
@@ -29,6 +29,7 @@ OBJECT_FILES_NON_STANDARD_head_$(BITS).o := y
|
||||
OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y
|
||||
OBJECT_FILES_NON_STANDARD_mcount_$(BITS).o := y
|
||||
OBJECT_FILES_NON_STANDARD_test_nx.o := y
|
||||
OBJECT_FILES_NON_STANDARD_paravirt_patch_$(BITS).o := y
|
||||
|
||||
# If instrumentation of this dir is enabled, boot hangs during first second.
|
||||
# Probably could be more selective here, but note that files related to irqs,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
OBJECT_FILES_NON_STANDARD_wakeup_$(BITS).o := y
|
||||
|
||||
obj-$(CONFIG_ACPI) += boot.o
|
||||
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o
|
||||
obj-$(CONFIG_ACPI_APEI) += apei.o
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/frame.h>
|
||||
|
||||
#include <asm/text-patching.h>
|
||||
#include <asm/cacheflush.h>
|
||||
@@ -91,6 +92,7 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
|
||||
}
|
||||
|
||||
asm (
|
||||
"optprobe_template_func:\n"
|
||||
".global optprobe_template_entry\n"
|
||||
"optprobe_template_entry:\n"
|
||||
#ifdef CONFIG_X86_64
|
||||
@@ -128,7 +130,12 @@ asm (
|
||||
" popf\n"
|
||||
#endif
|
||||
".global optprobe_template_end\n"
|
||||
"optprobe_template_end:\n");
|
||||
"optprobe_template_end:\n"
|
||||
".type optprobe_template_func, @function\n"
|
||||
".size optprobe_template_func, .-optprobe_template_func\n");
|
||||
|
||||
void optprobe_template_func(void);
|
||||
STACK_FRAME_NON_STANDARD(optprobe_template_func);
|
||||
|
||||
#define TMPL_MOVE_IDX \
|
||||
((long)&optprobe_template_val - (long)&optprobe_template_entry)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tboot.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/frame.h>
|
||||
#include <acpi/reboot.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/apic.h>
|
||||
@@ -127,6 +128,7 @@ void __noreturn machine_real_restart(unsigned int type)
|
||||
#ifdef CONFIG_APM_MODULE
|
||||
EXPORT_SYMBOL(machine_real_restart);
|
||||
#endif
|
||||
STACK_FRAME_NON_STANDARD(machine_real_restart);
|
||||
|
||||
/*
|
||||
* Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
|
||||
|
||||
@@ -353,6 +353,7 @@ SECTIONS
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame)
|
||||
*(__func_stack_frame_non_standard)
|
||||
*(__unreachable)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/amd-iommu.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/frame.h>
|
||||
|
||||
#include <asm/apic.h>
|
||||
#include <asm/perf_event.h>
|
||||
@@ -5111,6 +5112,7 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
|
||||
|
||||
mark_all_clean(svm->vmcb);
|
||||
}
|
||||
STACK_FRAME_NON_STANDARD(svm_vcpu_run);
|
||||
|
||||
static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root)
|
||||
{
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tboot.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/nospec.h>
|
||||
#include "kvm_cache_regs.h"
|
||||
#include "x86.h"
|
||||
@@ -8697,6 +8698,7 @@ static void vmx_handle_external_intr(struct kvm_vcpu *vcpu)
|
||||
);
|
||||
}
|
||||
}
|
||||
STACK_FRAME_NON_STANDARD(vmx_handle_external_intr);
|
||||
|
||||
static bool vmx_has_emulated_msr(int index)
|
||||
{
|
||||
@@ -9137,6 +9139,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
|
||||
vmx_recover_nmi_blocking(vmx);
|
||||
vmx_complete_interrupts(vmx);
|
||||
}
|
||||
STACK_FRAME_NON_STANDARD(vmx_vcpu_run);
|
||||
|
||||
static void vmx_load_vmcs01(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
.macro op_safe_regs op
|
||||
ENTRY(\op\()_safe_regs)
|
||||
pushq %rbx
|
||||
pushq %rbp
|
||||
pushq %r12
|
||||
movq %rdi, %r10 /* Save pointer */
|
||||
xorl %r11d, %r11d /* Return value */
|
||||
movl (%rdi), %eax
|
||||
movl 4(%rdi), %ecx
|
||||
movl 8(%rdi), %edx
|
||||
movl 12(%rdi), %ebx
|
||||
movl 20(%rdi), %ebp
|
||||
movl 20(%rdi), %r12d
|
||||
movl 24(%rdi), %esi
|
||||
movl 28(%rdi), %edi
|
||||
1: \op
|
||||
@@ -29,10 +29,10 @@ ENTRY(\op\()_safe_regs)
|
||||
movl %ecx, 4(%r10)
|
||||
movl %edx, 8(%r10)
|
||||
movl %ebx, 12(%r10)
|
||||
movl %ebp, 20(%r10)
|
||||
movl %r12d, 20(%r10)
|
||||
movl %esi, 24(%r10)
|
||||
movl %edi, 28(%r10)
|
||||
popq %rbp
|
||||
popq %r12
|
||||
popq %rbx
|
||||
ret
|
||||
3:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#
|
||||
# Arch-specific network modules
|
||||
#
|
||||
OBJECT_FILES_NON_STANDARD_bpf_jit.o += y
|
||||
|
||||
obj-$(CONFIG_BPF_JIT) += bpf_jit.o bpf_jit_comp.o
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
OBJECT_FILES_NON_STANDARD_efi_thunk_$(BITS).o := y
|
||||
OBJECT_FILES_NON_STANDARD_efi_stub_$(BITS).o := y
|
||||
|
||||
obj-$(CONFIG_EFI) += quirks.o efi.o efi_$(BITS).o efi_stub_$(BITS).o
|
||||
obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
OBJECT_FILES_NON_STANDARD_hibernate_asm_$(BITS).o := y
|
||||
|
||||
# __restore_processor_state() restores %gs after S3 resume and so should not
|
||||
# itself be stack-protected
|
||||
nostackp := $(call cc-option, -fno-stack-protector)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
OBJECT_FILES_NON_STANDARD_xen-asm_$(BITS).o := y
|
||||
OBJECT_FILES_NON_STANDARD_xen-pvh.o := y
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
# Do not profile debug and lowlevel utilities
|
||||
CFLAGS_REMOVE_spinlock.o = -pg
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
#include <asm/mwait.h>
|
||||
#include <asm/pci_x86.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/unwind_hints.h>
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
#include <linux/acpi.h>
|
||||
@@ -1452,10 +1453,12 @@ static void __ref xen_setup_gdt(int cpu)
|
||||
* GDT. The new GDT has __KERNEL_CS with CS.L = 1
|
||||
* and we are jumping to reload it.
|
||||
*/
|
||||
asm volatile ("pushq %0\n"
|
||||
asm volatile (UNWIND_HINT_SAVE
|
||||
"pushq %0\n"
|
||||
"leaq 1f(%%rip),%0\n"
|
||||
"pushq %0\n"
|
||||
"lretq\n"
|
||||
UNWIND_HINT_RESTORE
|
||||
"1:\n"
|
||||
: "=&r" (dummy) : "0" (__KERNEL_CS));
|
||||
|
||||
|
||||
@@ -207,6 +207,17 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_STACK_VALIDATION
|
||||
#define annotate_unreachable() ({ \
|
||||
asm("1:\t\n" \
|
||||
".pushsection __unreachable, \"a\"\t\n" \
|
||||
".long 1b\t\n" \
|
||||
".popsection\t\n"); \
|
||||
})
|
||||
#else
|
||||
#define annotate_unreachable()
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Mark a position in code as unreachable. This can be used to
|
||||
* suppress control flow warnings after asm blocks that transfer
|
||||
@@ -216,7 +227,8 @@
|
||||
* this in the preprocessor, but we can live with this because they're
|
||||
* unreleased. Really, we need to have autoconf for the kernel.
|
||||
*/
|
||||
#define unreachable() __builtin_unreachable()
|
||||
#define unreachable() \
|
||||
do { annotate_unreachable(); __builtin_unreachable(); } while (0)
|
||||
|
||||
/* Mark a function definition as prohibited from being cloned. */
|
||||
#define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/frame.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
@@ -878,7 +879,7 @@ int kexec_load_disabled;
|
||||
* only when panic_cpu holds the current CPU number; this is the only CPU
|
||||
* which processes crash_kexec routines.
|
||||
*/
|
||||
void __crash_kexec(struct pt_regs *regs)
|
||||
void __noclone __crash_kexec(struct pt_regs *regs)
|
||||
{
|
||||
/* Take the kexec_mutex here to prevent sys_kexec_load
|
||||
* running on one cpu from replacing the crash kernel
|
||||
@@ -900,6 +901,7 @@ void __crash_kexec(struct pt_regs *regs)
|
||||
mutex_unlock(&kexec_mutex);
|
||||
}
|
||||
}
|
||||
STACK_FRAME_NON_STANDARD(__crash_kexec);
|
||||
|
||||
void crash_kexec(struct pt_regs *regs)
|
||||
{
|
||||
|
||||
@@ -84,6 +84,13 @@ struct kvm_regs {
|
||||
#define KVM_VGIC_V2_DIST_SIZE 0x1000
|
||||
#define KVM_VGIC_V2_CPU_SIZE 0x2000
|
||||
|
||||
/* Supported VGICv3 address types */
|
||||
#define KVM_VGIC_V3_ADDR_TYPE_DIST 2
|
||||
#define KVM_VGIC_V3_ADDR_TYPE_REDIST 3
|
||||
|
||||
#define KVM_VGIC_V3_DIST_SIZE SZ_64K
|
||||
#define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K)
|
||||
|
||||
#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */
|
||||
#define KVM_ARM_VCPU_PSCI_0_2 1 /* CPU uses PSCI v0.2 */
|
||||
|
||||
@@ -166,6 +173,12 @@ struct kvm_arch_memory_slot {
|
||||
#define KVM_REG_ARM_VFP_FPINST 0x1009
|
||||
#define KVM_REG_ARM_VFP_FPINST2 0x100A
|
||||
|
||||
/* KVM-as-firmware specific pseudo-registers */
|
||||
#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT)
|
||||
#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \
|
||||
KVM_REG_ARM_FW | ((r) & 0xffff))
|
||||
#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
|
||||
|
||||
/* Device Control API: ARM VGIC */
|
||||
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
|
||||
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
|
||||
|
||||
@@ -195,6 +195,12 @@ struct kvm_arch_memory_slot {
|
||||
#define KVM_REG_ARM_TIMER_CNT ARM64_SYS_REG(3, 3, 14, 3, 2)
|
||||
#define KVM_REG_ARM_TIMER_CVAL ARM64_SYS_REG(3, 3, 14, 0, 2)
|
||||
|
||||
/* KVM-as-firmware specific pseudo-registers */
|
||||
#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT)
|
||||
#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \
|
||||
KVM_REG_ARM_FW | ((r) & 0xffff))
|
||||
#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0)
|
||||
|
||||
/* Device Control API: ARM VGIC */
|
||||
#define KVM_DEV_ARM_VGIC_GRP_ADDR 0
|
||||
#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1
|
||||
|
||||
@@ -596,6 +596,7 @@ struct kvm_get_htab_header {
|
||||
#define KVM_REG_PPC_TM_VSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U32 | 0x67)
|
||||
#define KVM_REG_PPC_TM_DSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x68)
|
||||
#define KVM_REG_PPC_TM_TAR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x69)
|
||||
#define KVM_REG_PPC_TM_XER (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x6a)
|
||||
|
||||
/* PPC64 eXternal Interrupt Controller Specification */
|
||||
#define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */
|
||||
|
||||
@@ -197,6 +197,7 @@ struct kvm_guest_debug_arch {
|
||||
#define KVM_SYNC_VRS (1UL << 6)
|
||||
#define KVM_SYNC_RICCB (1UL << 7)
|
||||
#define KVM_SYNC_FPRS (1UL << 8)
|
||||
#define KVM_SYNC_BPBC (1UL << 10)
|
||||
/* definition of registers in kvm_run */
|
||||
struct kvm_sync_regs {
|
||||
__u64 prefix; /* prefix register */
|
||||
@@ -217,7 +218,9 @@ struct kvm_sync_regs {
|
||||
};
|
||||
__u8 reserved[512]; /* for future vector expansion */
|
||||
__u32 fpc; /* valid on KVM_SYNC_VRS or KVM_SYNC_FPRS */
|
||||
__u8 padding[52]; /* riccb needs to be 64byte aligned */
|
||||
__u8 bpbc : 1; /* bp mode */
|
||||
__u8 reserved2 : 7;
|
||||
__u8 padding1[51]; /* riccb needs to be 64byte aligned */
|
||||
__u8 riccb[64]; /* runtime instrumentation controls block */
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/*
|
||||
* Defines x86 CPU feature bits
|
||||
*/
|
||||
#define NCAPINTS 18 /* N 32-bit words worth of info */
|
||||
#define NCAPINTS 19 /* N 32-bit words worth of info */
|
||||
#define NBUGINTS 1 /* N 32-bit bug flags */
|
||||
|
||||
/*
|
||||
@@ -189,17 +189,32 @@
|
||||
|
||||
#define X86_FEATURE_CPB ( 7*32+ 2) /* AMD Core Performance Boost */
|
||||
#define X86_FEATURE_EPB ( 7*32+ 3) /* IA32_ENERGY_PERF_BIAS support */
|
||||
#define X86_FEATURE_INVPCID_SINGLE ( 7*32+ 4) /* Effectively INVPCID && CR4.PCIDE=1 */
|
||||
|
||||
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
|
||||
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
|
||||
|
||||
#define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */
|
||||
#define X86_FEATURE_AVX512_4VNNIW (7*32+16) /* AVX-512 Neural Network Instructions */
|
||||
#define X86_FEATURE_AVX512_4FMAPS (7*32+17) /* AVX-512 Multiply Accumulation Single precision */
|
||||
#define X86_FEATURE_RETPOLINE ( 7*32+12) /* "" Generic Retpoline mitigation for Spectre variant 2 */
|
||||
#define X86_FEATURE_RETPOLINE_AMD ( 7*32+13) /* "" AMD Retpoline mitigation for Spectre variant 2 */
|
||||
|
||||
#define X86_FEATURE_MSR_SPEC_CTRL ( 7*32+16) /* "" MSR SPEC_CTRL is implemented */
|
||||
#define X86_FEATURE_SSBD ( 7*32+17) /* Speculative Store Bypass Disable */
|
||||
|
||||
#define X86_FEATURE_RSB_CTXSW ( 7*32+19) /* "" Fill RSB on context switches */
|
||||
|
||||
/* Because the ALTERNATIVE scheme is for members of the X86_FEATURE club... */
|
||||
#define X86_FEATURE_KAISER ( 7*32+31) /* CONFIG_PAGE_TABLE_ISOLATION w/o nokaiser */
|
||||
|
||||
#define X86_FEATURE_USE_IBPB ( 7*32+21) /* "" Indirect Branch Prediction Barrier enabled */
|
||||
#define X86_FEATURE_USE_IBRS_FW ( 7*32+22) /* "" Use IBRS during runtime firmware calls */
|
||||
#define X86_FEATURE_SPEC_STORE_BYPASS_DISABLE ( 7*32+23) /* "" Disable Speculative Store Bypass. */
|
||||
#define X86_FEATURE_LS_CFG_SSBD ( 7*32+24) /* "" AMD SSBD implementation */
|
||||
#define X86_FEATURE_IBRS ( 7*32+25) /* Indirect Branch Restricted Speculation */
|
||||
#define X86_FEATURE_IBPB ( 7*32+26) /* Indirect Branch Prediction Barrier */
|
||||
#define X86_FEATURE_STIBP ( 7*32+27) /* Single Thread Indirect Branch Predictors */
|
||||
#define X86_FEATURE_ZEN ( 7*32+28) /* "" CPU is AMD family 0x17 (Zen) */
|
||||
|
||||
|
||||
/* Virtualization flags: Linux defined, word 8 */
|
||||
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
|
||||
#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */
|
||||
@@ -231,6 +246,7 @@
|
||||
#define X86_FEATURE_SMAP ( 9*32+20) /* Supervisor Mode Access Prevention */
|
||||
#define X86_FEATURE_CLFLUSHOPT ( 9*32+23) /* CLFLUSHOPT instruction */
|
||||
#define X86_FEATURE_CLWB ( 9*32+24) /* CLWB instruction */
|
||||
#define X86_FEATURE_INTEL_PT ( 9*32+25) /* Intel Processor Trace */
|
||||
#define X86_FEATURE_AVX512PF ( 9*32+26) /* AVX-512 Prefetch */
|
||||
#define X86_FEATURE_AVX512ER ( 9*32+27) /* AVX-512 Exponential and Reciprocal */
|
||||
#define X86_FEATURE_AVX512CD ( 9*32+28) /* AVX-512 Conflict Detection */
|
||||
@@ -255,6 +271,10 @@
|
||||
/* AMD-defined CPU features, CPUID level 0x80000008 (ebx), word 13 */
|
||||
#define X86_FEATURE_CLZERO (13*32+0) /* CLZERO instruction */
|
||||
#define X86_FEATURE_IRPERF (13*32+1) /* Instructions Retired Count */
|
||||
#define X86_FEATURE_AMD_IBPB (13*32+12) /* Indirect Branch Prediction Barrier */
|
||||
#define X86_FEATURE_AMD_IBRS (13*32+14) /* Indirect Branch Restricted Speculation */
|
||||
#define X86_FEATURE_AMD_STIBP (13*32+15) /* Single Thread Indirect Branch Predictors */
|
||||
#define X86_FEATURE_VIRT_SSBD (13*32+25) /* Virtualized Speculative Store Bypass Disable */
|
||||
|
||||
/* Thermal and Power Management Leaf, CPUID level 0x00000006 (eax), word 14 */
|
||||
#define X86_FEATURE_DTHERM (14*32+ 0) /* Digital Thermal Sensor */
|
||||
@@ -290,6 +310,16 @@
|
||||
#define X86_FEATURE_SUCCOR (17*32+1) /* Uncorrectable error containment and recovery */
|
||||
#define X86_FEATURE_SMCA (17*32+3) /* Scalable MCA */
|
||||
|
||||
|
||||
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
|
||||
#define X86_FEATURE_AVX512_4VNNIW (18*32+ 2) /* AVX-512 Neural Network Instructions */
|
||||
#define X86_FEATURE_AVX512_4FMAPS (18*32+ 3) /* AVX-512 Multiply Accumulation Single precision */
|
||||
#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */
|
||||
#define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */
|
||||
#define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */
|
||||
#define X86_FEATURE_ARCH_CAPABILITIES (18*32+29) /* IA32_ARCH_CAPABILITIES MSR (Intel) */
|
||||
#define X86_FEATURE_SPEC_CTRL_SSBD (18*32+31) /* "" Speculative Store Bypass Disable */
|
||||
|
||||
/*
|
||||
* BUG word(s)
|
||||
*/
|
||||
@@ -314,4 +344,10 @@
|
||||
#define X86_BUG_NULL_SEG X86_BUG(10) /* Nulling a selector preserves the base */
|
||||
#define X86_BUG_SWAPGS_FENCE X86_BUG(11) /* SWAPGS without input dep on GS */
|
||||
#define X86_BUG_MONITOR X86_BUG(12) /* IPI required to wake up remote CPU */
|
||||
#define X86_BUG_AMD_E400 X86_BUG(13) /* CPU is among the affected by Erratum 400 */
|
||||
#define X86_BUG_CPU_MELTDOWN X86_BUG(14) /* CPU is affected by meltdown attack and needs kernel page table isolation */
|
||||
#define X86_BUG_SPECTRE_V1 X86_BUG(15) /* CPU is affected by Spectre variant 1 attack with conditional branches */
|
||||
#define X86_BUG_SPECTRE_V2 X86_BUG(16) /* CPU is affected by Spectre variant 2 attack with indirect branches */
|
||||
#define X86_BUG_SPEC_STORE_BYPASS X86_BUG(17) /* CPU is affected by speculative store bypass attack */
|
||||
|
||||
#endif /* _ASM_X86_CPUFEATURES_H */
|
||||
|
||||
@@ -21,11 +21,13 @@
|
||||
# define DISABLE_K6_MTRR (1<<(X86_FEATURE_K6_MTRR & 31))
|
||||
# define DISABLE_CYRIX_ARR (1<<(X86_FEATURE_CYRIX_ARR & 31))
|
||||
# define DISABLE_CENTAUR_MCR (1<<(X86_FEATURE_CENTAUR_MCR & 31))
|
||||
# define DISABLE_PCID 0
|
||||
#else
|
||||
# define DISABLE_VME 0
|
||||
# define DISABLE_K6_MTRR 0
|
||||
# define DISABLE_CYRIX_ARR 0
|
||||
# define DISABLE_CENTAUR_MCR 0
|
||||
# define DISABLE_PCID (1<<(X86_FEATURE_PCID & 31))
|
||||
#endif /* CONFIG_X86_64 */
|
||||
|
||||
#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
|
||||
@@ -43,7 +45,7 @@
|
||||
#define DISABLED_MASK1 0
|
||||
#define DISABLED_MASK2 0
|
||||
#define DISABLED_MASK3 (DISABLE_CYRIX_ARR|DISABLE_CENTAUR_MCR|DISABLE_K6_MTRR)
|
||||
#define DISABLED_MASK4 0
|
||||
#define DISABLED_MASK4 (DISABLE_PCID)
|
||||
#define DISABLED_MASK5 0
|
||||
#define DISABLED_MASK6 0
|
||||
#define DISABLED_MASK7 0
|
||||
@@ -57,6 +59,7 @@
|
||||
#define DISABLED_MASK15 0
|
||||
#define DISABLED_MASK16 (DISABLE_PKU|DISABLE_OSPKE)
|
||||
#define DISABLED_MASK17 0
|
||||
#define DISABLED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 18)
|
||||
#define DISABLED_MASK18 0
|
||||
#define DISABLED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 19)
|
||||
|
||||
#endif /* _ASM_X86_DISABLED_FEATURES_H */
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
#define REQUIRED_MASK15 0
|
||||
#define REQUIRED_MASK16 0
|
||||
#define REQUIRED_MASK17 0
|
||||
#define REQUIRED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 18)
|
||||
#define REQUIRED_MASK18 0
|
||||
#define REQUIRED_MASK_CHECK BUILD_BUG_ON_ZERO(NCAPINTS != 19)
|
||||
|
||||
#endif /* _ASM_X86_REQUIRED_FEATURES_H */
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
|
||||
#include <asm-generic/bitops/__ffs.h>
|
||||
#include <asm-generic/bitops/__ffz.h>
|
||||
#include <asm-generic/bitops/fls.h>
|
||||
#include <asm-generic/bitops/__fls.h>
|
||||
#include <asm-generic/bitops/fls64.h>
|
||||
|
||||
12
tools/include/asm-generic/bitops/__ffz.h
Normal file
12
tools/include/asm-generic/bitops/__ffz.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef _ASM_GENERIC_BITOPS_FFZ_H_
|
||||
#define _ASM_GENERIC_BITOPS_FFZ_H_
|
||||
|
||||
/*
|
||||
* ffz - find first zero in word.
|
||||
* @word: The word to search
|
||||
*
|
||||
* Undefined if no zero exists, so code should check against ~0UL first.
|
||||
*/
|
||||
#define ffz(x) __ffs(~(x))
|
||||
|
||||
#endif /* _ASM_GENERIC_BITOPS_FFZ_H_ */
|
||||
@@ -15,6 +15,21 @@ extern unsigned long find_next_bit(const unsigned long *addr, unsigned long
|
||||
size, unsigned long offset);
|
||||
#endif
|
||||
|
||||
#ifndef find_next_zero_bit
|
||||
|
||||
/**
|
||||
* find_next_zero_bit - find the next cleared bit in a memory region
|
||||
* @addr: The address to base the search on
|
||||
* @offset: The bitnumber to start searching at
|
||||
* @size: The bitmap size in bits
|
||||
*
|
||||
* Returns the bit number of the next zero bit
|
||||
* If no bits are zero, returns @size.
|
||||
*/
|
||||
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
|
||||
unsigned long offset);
|
||||
#endif
|
||||
|
||||
#ifndef find_first_bit
|
||||
|
||||
/**
|
||||
@@ -30,4 +45,17 @@ extern unsigned long find_first_bit(const unsigned long *addr,
|
||||
|
||||
#endif /* find_first_bit */
|
||||
|
||||
#ifndef find_first_zero_bit
|
||||
|
||||
/**
|
||||
* find_first_zero_bit - find the first cleared bit in a memory region
|
||||
* @addr: The address to start the search at
|
||||
* @size: The maximum number of bits to search
|
||||
*
|
||||
* Returns the bit number of the first cleared bit.
|
||||
* If no bits are zero, returns @size.
|
||||
*/
|
||||
unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size);
|
||||
#endif
|
||||
|
||||
#endif /*_TOOLS_LINUX_ASM_GENERIC_BITOPS_FIND_H_ */
|
||||
|
||||
@@ -3,4 +3,10 @@
|
||||
|
||||
#include <asm/atomic.h>
|
||||
|
||||
/* atomic_cmpxchg_relaxed */
|
||||
#ifndef atomic_cmpxchg_relaxed
|
||||
#define atomic_cmpxchg_relaxed atomic_cmpxchg
|
||||
#define atomic_cmpxchg_release atomic_cmpxchg
|
||||
#endif /* atomic_cmpxchg_relaxed */
|
||||
|
||||
#endif /* __TOOLS_LINUX_ATOMIC_H */
|
||||
|
||||
@@ -35,6 +35,32 @@ static inline void bitmap_zero(unsigned long *dst, int nbits)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void bitmap_fill(unsigned long *dst, unsigned int nbits)
|
||||
{
|
||||
unsigned int nlongs = BITS_TO_LONGS(nbits);
|
||||
if (!small_const_nbits(nbits)) {
|
||||
unsigned int len = (nlongs - 1) * sizeof(unsigned long);
|
||||
memset(dst, 0xff, len);
|
||||
}
|
||||
dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits);
|
||||
}
|
||||
|
||||
static inline int bitmap_empty(const unsigned long *src, unsigned nbits)
|
||||
{
|
||||
if (small_const_nbits(nbits))
|
||||
return ! (*src & BITMAP_LAST_WORD_MASK(nbits));
|
||||
|
||||
return find_first_bit(src, nbits) == nbits;
|
||||
}
|
||||
|
||||
static inline int bitmap_full(const unsigned long *src, unsigned int nbits)
|
||||
{
|
||||
if (small_const_nbits(nbits))
|
||||
return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits));
|
||||
|
||||
return find_first_zero_bit(src, nbits) == nbits;
|
||||
}
|
||||
|
||||
static inline int bitmap_weight(const unsigned long *src, int nbits)
|
||||
{
|
||||
if (small_const_nbits(nbits))
|
||||
|
||||
@@ -39,6 +39,11 @@ extern unsigned long __sw_hweight64(__u64 w);
|
||||
(bit) < (size); \
|
||||
(bit) = find_next_bit((addr), (size), (bit) + 1))
|
||||
|
||||
#define for_each_clear_bit(bit, addr, size) \
|
||||
for ((bit) = find_first_zero_bit((addr), (size)); \
|
||||
(bit) < (size); \
|
||||
(bit) = find_next_zero_bit((addr), (size), (bit) + 1))
|
||||
|
||||
/* same as for_each_set_bit() but use bit as value to start with */
|
||||
#define for_each_set_bit_from(bit, addr, size) \
|
||||
for ((bit) = find_next_bit((addr), (size), (bit)); \
|
||||
|
||||
10
tools/include/linux/bug.h
Normal file
10
tools/include/linux/bug.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _TOOLS_PERF_LINUX_BUG_H
|
||||
#define _TOOLS_PERF_LINUX_BUG_H
|
||||
|
||||
/* Force a compilation error if condition is true, but also produce a
|
||||
result (of value 0 and type size_t), so the expression can be used
|
||||
e.g. in a structure initializer (or where-ever else comma expressions
|
||||
aren't permitted). */
|
||||
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
|
||||
|
||||
#endif /* _TOOLS_PERF_LINUX_BUG_H */
|
||||
21
tools/include/linux/compiler-gcc.h
Normal file
21
tools/include/linux/compiler-gcc.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef _TOOLS_LINUX_COMPILER_H_
|
||||
#error "Please don't include <linux/compiler-gcc.h> directly, include <linux/compiler.h> instead."
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Common definitions for all gcc versions go here.
|
||||
*/
|
||||
#define GCC_VERSION (__GNUC__ * 10000 \
|
||||
+ __GNUC_MINOR__ * 100 \
|
||||
+ __GNUC_PATCHLEVEL__)
|
||||
|
||||
#if GCC_VERSION >= 70000 && !defined(__CHECKER__)
|
||||
# define __fallthrough __attribute__ ((fallthrough))
|
||||
#endif
|
||||
|
||||
#if GCC_VERSION >= 40300
|
||||
# define __compiletime_error(message) __attribute__((error(message)))
|
||||
#endif /* GCC_VERSION >= 40300 */
|
||||
|
||||
/* &a[0] degrades to a pointer: a different type from an array */
|
||||
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
|
||||
@@ -1,6 +1,14 @@
|
||||
#ifndef _TOOLS_LINUX_COMPILER_H_
|
||||
#define _TOOLS_LINUX_COMPILER_H_
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <linux/compiler-gcc.h>
|
||||
#endif
|
||||
|
||||
#ifndef __compiletime_error
|
||||
# define __compiletime_error(message)
|
||||
#endif
|
||||
|
||||
/* Optimization barrier */
|
||||
/* The "volatile" is due to gcc bugs */
|
||||
#define barrier() __asm__ __volatile__("": : :"memory")
|
||||
@@ -9,6 +17,11 @@
|
||||
# define __always_inline inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
/* Are two types/vars the same type (ignoring qualifiers)? */
|
||||
#ifndef __same_type
|
||||
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
/*
|
||||
* FIXME: Big hammer to get rid of tons of:
|
||||
@@ -21,6 +34,8 @@
|
||||
#endif
|
||||
|
||||
#define __user
|
||||
#define __rcu
|
||||
#define __read_mostly
|
||||
|
||||
#ifndef __attribute_const__
|
||||
# define __attribute_const__
|
||||
@@ -50,6 +65,8 @@
|
||||
# define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#endif
|
||||
|
||||
#define uninitialized_var(x) x = *(&(x))
|
||||
|
||||
#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
|
||||
|
||||
#include <linux/types.h>
|
||||
@@ -128,11 +145,7 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
|
||||
|
||||
|
||||
#ifndef __fallthrough
|
||||
# if defined(__GNUC__) && __GNUC__ >= 7
|
||||
# define __fallthrough __attribute__ ((fallthrough))
|
||||
# else
|
||||
# define __fallthrough
|
||||
# endif
|
||||
# define __fallthrough
|
||||
#endif
|
||||
|
||||
#endif /* _TOOLS_LINUX_COMPILER_H */
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
#include <linux/hash.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
||||
|
||||
#define DEFINE_HASHTABLE(name, bits) \
|
||||
struct hlist_head name[1 << (bits)] = \
|
||||
{ [0 ... ((1 << (bits)) - 1)] = HLIST_HEAD_INIT }
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#ifndef UINT_MAX
|
||||
#define UINT_MAX (~0U)
|
||||
#endif
|
||||
|
||||
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
|
||||
|
||||
@@ -72,6 +77,8 @@
|
||||
int vscnprintf(char *buf, size_t size, const char *fmt, va_list args);
|
||||
int scnprintf(char * buf, size_t size, const char * fmt, ...);
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
|
||||
|
||||
/*
|
||||
* This looks more complex than it should be. But we need to
|
||||
* get the type for the ~ right in round_down (it needs to be
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#ifndef _TOOLS_LINUX_LOG2_H
|
||||
#define _TOOLS_LINUX_LOG2_H
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* non-constant log of base 2 calculators
|
||||
* - the arch may override these in asm/bitops.h if they can be implemented
|
||||
|
||||
151
tools/include/linux/refcount.h
Normal file
151
tools/include/linux/refcount.h
Normal file
@@ -0,0 +1,151 @@
|
||||
#ifndef _TOOLS_LINUX_REFCOUNT_H
|
||||
#define _TOOLS_LINUX_REFCOUNT_H
|
||||
|
||||
/*
|
||||
* Variant of atomic_t specialized for reference counts.
|
||||
*
|
||||
* The interface matches the atomic_t interface (to aid in porting) but only
|
||||
* provides the few functions one should use for reference counting.
|
||||
*
|
||||
* It differs in that the counter saturates at UINT_MAX and will not move once
|
||||
* there. This avoids wrapping the counter and causing 'spurious'
|
||||
* use-after-free issues.
|
||||
*
|
||||
* Memory ordering rules are slightly relaxed wrt regular atomic_t functions
|
||||
* and provide only what is strictly required for refcounts.
|
||||
*
|
||||
* The increments are fully relaxed; these will not provide ordering. The
|
||||
* rationale is that whatever is used to obtain the object we're increasing the
|
||||
* reference count on will provide the ordering. For locked data structures,
|
||||
* its the lock acquire, for RCU/lockless data structures its the dependent
|
||||
* load.
|
||||
*
|
||||
* Do note that inc_not_zero() provides a control dependency which will order
|
||||
* future stores against the inc, this ensures we'll never modify the object
|
||||
* if we did not in fact acquire a reference.
|
||||
*
|
||||
* The decrements will provide release order, such that all the prior loads and
|
||||
* stores will be issued before, it also provides a control dependency, which
|
||||
* will order us against the subsequent free().
|
||||
*
|
||||
* The control dependency is against the load of the cmpxchg (ll/sc) that
|
||||
* succeeded. This means the stores aren't fully ordered, but this is fine
|
||||
* because the 1->0 transition indicates no concurrency.
|
||||
*
|
||||
* Note that the allocator is responsible for ordering things between free()
|
||||
* and alloc().
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define REFCOUNT_WARN(cond, str) (void)(cond)
|
||||
#define __refcount_check
|
||||
#else
|
||||
#define REFCOUNT_WARN(cond, str) BUG_ON(cond)
|
||||
#define __refcount_check __must_check
|
||||
#endif
|
||||
|
||||
typedef struct refcount_struct {
|
||||
atomic_t refs;
|
||||
} refcount_t;
|
||||
|
||||
#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }
|
||||
|
||||
static inline void refcount_set(refcount_t *r, unsigned int n)
|
||||
{
|
||||
atomic_set(&r->refs, n);
|
||||
}
|
||||
|
||||
static inline unsigned int refcount_read(const refcount_t *r)
|
||||
{
|
||||
return atomic_read(&r->refs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to atomic_inc_not_zero(), will saturate at UINT_MAX and WARN.
|
||||
*
|
||||
* Provides no memory ordering, it is assumed the caller has guaranteed the
|
||||
* object memory to be stable (RCU, etc.). It does provide a control dependency
|
||||
* and thereby orders future stores. See the comment on top.
|
||||
*/
|
||||
static inline __refcount_check
|
||||
bool refcount_inc_not_zero(refcount_t *r)
|
||||
{
|
||||
unsigned int old, new, val = atomic_read(&r->refs);
|
||||
|
||||
for (;;) {
|
||||
new = val + 1;
|
||||
|
||||
if (!val)
|
||||
return false;
|
||||
|
||||
if (unlikely(!new))
|
||||
return true;
|
||||
|
||||
old = atomic_cmpxchg_relaxed(&r->refs, val, new);
|
||||
if (old == val)
|
||||
break;
|
||||
|
||||
val = old;
|
||||
}
|
||||
|
||||
REFCOUNT_WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to atomic_inc(), will saturate at UINT_MAX and WARN.
|
||||
*
|
||||
* Provides no memory ordering, it is assumed the caller already has a
|
||||
* reference on the object, will WARN when this is not so.
|
||||
*/
|
||||
static inline void refcount_inc(refcount_t *r)
|
||||
{
|
||||
REFCOUNT_WARN(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to atomic_dec_and_test(), it will WARN on underflow and fail to
|
||||
* decrement when saturated at UINT_MAX.
|
||||
*
|
||||
* Provides release memory ordering, such that prior loads and stores are done
|
||||
* before, and provides a control dependency such that free() must come after.
|
||||
* See the comment on top.
|
||||
*/
|
||||
static inline __refcount_check
|
||||
bool refcount_sub_and_test(unsigned int i, refcount_t *r)
|
||||
{
|
||||
unsigned int old, new, val = atomic_read(&r->refs);
|
||||
|
||||
for (;;) {
|
||||
if (unlikely(val == UINT_MAX))
|
||||
return false;
|
||||
|
||||
new = val - i;
|
||||
if (new > val) {
|
||||
REFCOUNT_WARN(new > val, "refcount_t: underflow; use-after-free.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
old = atomic_cmpxchg_release(&r->refs, val, new);
|
||||
if (old == val)
|
||||
break;
|
||||
|
||||
val = old;
|
||||
}
|
||||
|
||||
return !new;
|
||||
}
|
||||
|
||||
static inline __refcount_check
|
||||
bool refcount_dec_and_test(refcount_t *r)
|
||||
{
|
||||
return refcount_sub_and_test(1, r);
|
||||
}
|
||||
|
||||
|
||||
#endif /* _ATOMIC_LINUX_REFCOUNT_H */
|
||||
5
tools/include/linux/spinlock.h
Normal file
5
tools/include/linux/spinlock.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#define spinlock_t pthread_mutex_t
|
||||
#define DEFINE_SPINLOCK(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
#define spin_lock_irqsave(x, f) (void)f, pthread_mutex_lock(x)
|
||||
#define spin_unlock_irqrestore(x, f) (void)f, pthread_mutex_unlock(x)
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
|
||||
#include <asm/types.h>
|
||||
#include <asm/posix_types.h>
|
||||
|
||||
struct page;
|
||||
struct kmem_cache;
|
||||
@@ -42,11 +43,7 @@ typedef __s8 s8;
|
||||
#else
|
||||
#define __bitwise__
|
||||
#endif
|
||||
#ifdef __CHECK_ENDIAN__
|
||||
#define __bitwise __bitwise__
|
||||
#else
|
||||
#define __bitwise
|
||||
#endif
|
||||
|
||||
#define __force
|
||||
#define __user
|
||||
|
||||
@@ -72,4 +72,9 @@
|
||||
#define MAP_HUGE_SHIFT 26
|
||||
#define MAP_HUGE_MASK 0x3f
|
||||
|
||||
#define PKEY_DISABLE_ACCESS 0x1
|
||||
#define PKEY_DISABLE_WRITE 0x2
|
||||
#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\
|
||||
PKEY_DISABLE_WRITE)
|
||||
|
||||
#endif /* __ASM_GENERIC_MMAN_COMMON_H */
|
||||
|
||||
@@ -73,6 +73,8 @@ enum bpf_cmd {
|
||||
BPF_PROG_LOAD,
|
||||
BPF_OBJ_PIN,
|
||||
BPF_OBJ_GET,
|
||||
BPF_PROG_ATTACH,
|
||||
BPF_PROG_DETACH,
|
||||
};
|
||||
|
||||
enum bpf_map_type {
|
||||
@@ -95,8 +97,24 @@ enum bpf_prog_type {
|
||||
BPF_PROG_TYPE_SCHED_ACT,
|
||||
BPF_PROG_TYPE_TRACEPOINT,
|
||||
BPF_PROG_TYPE_XDP,
|
||||
BPF_PROG_TYPE_PERF_EVENT,
|
||||
BPF_PROG_TYPE_CGROUP_SKB,
|
||||
};
|
||||
|
||||
enum bpf_attach_type {
|
||||
BPF_CGROUP_INET_INGRESS,
|
||||
BPF_CGROUP_INET_EGRESS,
|
||||
__MAX_BPF_ATTACH_TYPE
|
||||
};
|
||||
|
||||
#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
|
||||
|
||||
/* If BPF_F_ALLOW_OVERRIDE flag is used in BPF_PROG_ATTACH command
|
||||
* to the given target_fd cgroup the descendent cgroup will be able to
|
||||
* override effective bpf program that was inherited from this cgroup
|
||||
*/
|
||||
#define BPF_F_ALLOW_OVERRIDE (1U << 0)
|
||||
|
||||
#define BPF_PSEUDO_MAP_FD 1
|
||||
|
||||
/* flags for BPF_MAP_UPDATE_ELEM command */
|
||||
@@ -106,6 +124,10 @@ enum bpf_prog_type {
|
||||
|
||||
#define BPF_F_NO_PREALLOC (1U << 0)
|
||||
|
||||
/* Flags for accessing BPF object */
|
||||
#define BPF_F_RDONLY (1U << 3)
|
||||
#define BPF_F_WRONLY (1U << 4)
|
||||
|
||||
union bpf_attr {
|
||||
struct { /* anonymous struct used by BPF_MAP_CREATE command */
|
||||
__u32 map_type; /* one of enum bpf_map_type */
|
||||
@@ -139,6 +161,14 @@ union bpf_attr {
|
||||
struct { /* anonymous struct used by BPF_OBJ_* commands */
|
||||
__aligned_u64 pathname;
|
||||
__u32 bpf_fd;
|
||||
__u32 file_flags;
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
|
||||
__u32 target_fd; /* container object to attach to */
|
||||
__u32 attach_bpf_fd; /* eBPF program to attach */
|
||||
__u32 attach_type;
|
||||
__u32 attach_flags;
|
||||
};
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
@@ -376,40 +406,52 @@ enum bpf_func_id {
|
||||
BPF_FUNC_probe_write_user,
|
||||
|
||||
/**
|
||||
* int bpf_skb_change_tail(skb, len, flags)
|
||||
* The helper will resize the skb to the given new size, to be used f.e.
|
||||
* with control messages.
|
||||
* @skb: pointer to skb
|
||||
* @len: new skb length
|
||||
* @flags: reserved
|
||||
* Return: 0 on success or negative error
|
||||
* bpf_current_task_under_cgroup(map, index) - Check cgroup2 membership of current task
|
||||
* @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type
|
||||
* @index: index of the cgroup in the bpf_map
|
||||
* Return:
|
||||
* == 0 current failed the cgroup2 descendant test
|
||||
* == 1 current succeeded the cgroup2 descendant test
|
||||
* < 0 error
|
||||
*/
|
||||
BPF_FUNC_current_task_under_cgroup,
|
||||
|
||||
/**
|
||||
* bpf_skb_change_tail(skb, len, flags)
|
||||
* The helper will resize the skb to the given new size,
|
||||
* to be used f.e. with control messages.
|
||||
* @skb: pointer to skb
|
||||
* @len: new skb length
|
||||
* @flags: reserved
|
||||
* Return: 0 on success or negative error
|
||||
*/
|
||||
BPF_FUNC_skb_change_tail,
|
||||
|
||||
/**
|
||||
* int bpf_skb_pull_data(skb, len)
|
||||
* The helper will pull in non-linear data in case the skb is non-linear
|
||||
* and not all of len are part of the linear section. Only needed for
|
||||
* read/write with direct packet access.
|
||||
* @skb: pointer to skb
|
||||
* @len: len to make read/writeable
|
||||
* Return: 0 on success or negative error
|
||||
* bpf_skb_pull_data(skb, len)
|
||||
* The helper will pull in non-linear data in case the
|
||||
* skb is non-linear and not all of len are part of the
|
||||
* linear section. Only needed for read/write with direct
|
||||
* packet access.
|
||||
* @skb: pointer to skb
|
||||
* @len: len to make read/writeable
|
||||
* Return: 0 on success or negative error
|
||||
*/
|
||||
BPF_FUNC_skb_pull_data,
|
||||
|
||||
/**
|
||||
* s64 bpf_csum_update(skb, csum)
|
||||
* Adds csum into skb->csum in case of CHECKSUM_COMPLETE.
|
||||
* @skb: pointer to skb
|
||||
* @csum: csum to add
|
||||
* Return: csum on success or negative error
|
||||
* bpf_csum_update(skb, csum)
|
||||
* Adds csum into skb->csum in case of CHECKSUM_COMPLETE.
|
||||
* @skb: pointer to skb
|
||||
* @csum: csum to add
|
||||
* Return: csum on success or negative error
|
||||
*/
|
||||
BPF_FUNC_csum_update,
|
||||
|
||||
/**
|
||||
* void bpf_set_hash_invalid(skb)
|
||||
* Invalidate current skb->hash.
|
||||
* @skb: pointer to skb
|
||||
* bpf_set_hash_invalid(skb)
|
||||
* Invalidate current skb>hash.
|
||||
* @skb: pointer to skb
|
||||
*/
|
||||
BPF_FUNC_set_hash_invalid,
|
||||
|
||||
@@ -457,12 +499,11 @@ enum bpf_func_id {
|
||||
BPF_FUNC_probe_read_str,
|
||||
|
||||
/**
|
||||
* u64 bpf_get_socket_cookie(skb)
|
||||
* Get the cookie for the socket stored inside sk_buff.
|
||||
* @skb: pointer to skb
|
||||
* Return: 8 Bytes non-decreasing number on success or 0 if
|
||||
* the socket
|
||||
* field is missing inside sk_buff
|
||||
* u64 bpf_bpf_get_socket_cookie(skb)
|
||||
* Get the cookie for the socket stored inside sk_buff.
|
||||
* @skb: pointer to skb
|
||||
* Return: 8 Bytes non-decreasing number on success or 0 if the socket
|
||||
* field is missing inside sk_buff
|
||||
*/
|
||||
BPF_FUNC_get_socket_cookie,
|
||||
|
||||
@@ -470,7 +511,8 @@ enum bpf_func_id {
|
||||
* u32 bpf_get_socket_uid(skb)
|
||||
* Get the owner uid of the socket stored inside sk_buff.
|
||||
* @skb: pointer to skb
|
||||
* Return: uid of the socket owner on success or overflowuid if failed.
|
||||
* Return: uid of the socket owner on success or 0 if the socket pointer
|
||||
* inside sk_buff is NULL
|
||||
*/
|
||||
BPF_FUNC_get_socket_uid,
|
||||
|
||||
|
||||
67
tools/include/uapi/linux/fcntl.h
Normal file
67
tools/include/uapi/linux/fcntl.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef _UAPI_LINUX_FCNTL_H
|
||||
#define _UAPI_LINUX_FCNTL_H
|
||||
|
||||
#include <asm/fcntl.h>
|
||||
|
||||
#define F_SETLEASE (F_LINUX_SPECIFIC_BASE + 0)
|
||||
#define F_GETLEASE (F_LINUX_SPECIFIC_BASE + 1)
|
||||
|
||||
/*
|
||||
* Cancel a blocking posix lock; internal use only until we expose an
|
||||
* asynchronous lock api to userspace:
|
||||
*/
|
||||
#define F_CANCELLK (F_LINUX_SPECIFIC_BASE + 5)
|
||||
|
||||
/* Create a file descriptor with FD_CLOEXEC set. */
|
||||
#define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
|
||||
|
||||
/*
|
||||
* Request nofications on a directory.
|
||||
* See below for events that may be notified.
|
||||
*/
|
||||
#define F_NOTIFY (F_LINUX_SPECIFIC_BASE+2)
|
||||
|
||||
/*
|
||||
* Set and get of pipe page size array
|
||||
*/
|
||||
#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
|
||||
#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
|
||||
|
||||
/*
|
||||
* Set/Get seals
|
||||
*/
|
||||
#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
|
||||
#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
|
||||
|
||||
/*
|
||||
* Types of seals
|
||||
*/
|
||||
#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
|
||||
#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
|
||||
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
|
||||
#define F_SEAL_WRITE 0x0008 /* prevent writes */
|
||||
/* (1U << 31) is reserved for signed error codes */
|
||||
|
||||
/*
|
||||
* Types of directory notifications that may be requested.
|
||||
*/
|
||||
#define DN_ACCESS 0x00000001 /* File accessed */
|
||||
#define DN_MODIFY 0x00000002 /* File modified */
|
||||
#define DN_CREATE 0x00000004 /* File created */
|
||||
#define DN_DELETE 0x00000008 /* File removed */
|
||||
#define DN_RENAME 0x00000010 /* File renamed */
|
||||
#define DN_ATTRIB 0x00000020 /* File changed attibutes */
|
||||
#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
|
||||
|
||||
#define AT_FDCWD -100 /* Special value used to indicate
|
||||
openat should use the current
|
||||
working directory. */
|
||||
#define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */
|
||||
#define AT_REMOVEDIR 0x200 /* Remove directory instead of
|
||||
unlinking file. */
|
||||
#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
|
||||
#define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal automount traversal */
|
||||
#define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */
|
||||
|
||||
|
||||
#endif /* _UAPI_LINUX_FCNTL_H */
|
||||
45
tools/include/uapi/linux/stat.h
Normal file
45
tools/include/uapi/linux/stat.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef _UAPI_LINUX_STAT_H
|
||||
#define _UAPI_LINUX_STAT_H
|
||||
|
||||
|
||||
#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
|
||||
|
||||
#define S_IFMT 00170000
|
||||
#define S_IFSOCK 0140000
|
||||
#define S_IFLNK 0120000
|
||||
#define S_IFREG 0100000
|
||||
#define S_IFBLK 0060000
|
||||
#define S_IFDIR 0040000
|
||||
#define S_IFCHR 0020000
|
||||
#define S_IFIFO 0010000
|
||||
#define S_ISUID 0004000
|
||||
#define S_ISGID 0002000
|
||||
#define S_ISVTX 0001000
|
||||
|
||||
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
||||
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
|
||||
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
|
||||
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||
|
||||
#define S_IRWXU 00700
|
||||
#define S_IRUSR 00400
|
||||
#define S_IWUSR 00200
|
||||
#define S_IXUSR 00100
|
||||
|
||||
#define S_IRWXG 00070
|
||||
#define S_IRGRP 00040
|
||||
#define S_IWGRP 00020
|
||||
#define S_IXGRP 00010
|
||||
|
||||
#define S_IRWXO 00007
|
||||
#define S_IROTH 00004
|
||||
#define S_IWOTH 00002
|
||||
#define S_IXOTH 00001
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _UAPI_LINUX_STAT_H */
|
||||
@@ -82,3 +82,28 @@ unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef find_first_zero_bit
|
||||
/*
|
||||
* Find the first cleared bit in a memory region.
|
||||
*/
|
||||
unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size)
|
||||
{
|
||||
unsigned long idx;
|
||||
|
||||
for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
|
||||
if (addr[idx] != ~0UL)
|
||||
return min(idx * BITS_PER_LONG + ffz(addr[idx]), size);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef find_next_zero_bit
|
||||
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
|
||||
unsigned long offset)
|
||||
{
|
||||
return _find_next_bit(addr, size, offset, ~0UL);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
objtool-y += arch/$(SRCARCH)/
|
||||
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
|
||||
|
||||
@@ -11,9 +11,6 @@ analyzes every .o file and ensures the validity of its stack metadata.
|
||||
It enforces a set of rules on asm code and C inline assembly code so
|
||||
that stack traces can be reliable.
|
||||
|
||||
Currently it only checks frame pointer usage, but there are plans to add
|
||||
CFI validation for C files and CFI generation for asm files.
|
||||
|
||||
For each function, it recursively follows all possible code paths and
|
||||
validates the correct frame pointer state at each instruction.
|
||||
|
||||
@@ -23,6 +20,10 @@ alternative execution paths to a given instruction (or set of
|
||||
instructions). Similarly, it knows how to follow switch statements, for
|
||||
which gcc sometimes uses jump tables.
|
||||
|
||||
(Objtool also has an 'orc generate' subcommand which generates debuginfo
|
||||
for the ORC unwinder. See Documentation/x86/orc-unwinder.txt in the
|
||||
kernel tree for more details.)
|
||||
|
||||
|
||||
Why do we need stack metadata validation?
|
||||
-----------------------------------------
|
||||
@@ -93,62 +94,24 @@ a) More reliable stack traces for frame pointer enabled kernels
|
||||
or at the very end of the function after the stack frame has been
|
||||
destroyed. This is an inherent limitation of frame pointers.
|
||||
|
||||
b) 100% reliable stack traces for DWARF enabled kernels
|
||||
b) ORC (Oops Rewind Capability) unwind table generation
|
||||
|
||||
(NOTE: This is not yet implemented)
|
||||
An alternative to frame pointers and DWARF, ORC unwind data can be
|
||||
used to walk the stack. Unlike frame pointers, ORC data is out of
|
||||
band. So it doesn't affect runtime performance and it can be
|
||||
reliable even when interrupts or exceptions are involved.
|
||||
|
||||
As an alternative to frame pointers, DWARF Call Frame Information
|
||||
(CFI) metadata can be used to walk the stack. Unlike frame pointers,
|
||||
CFI metadata is out of band. So it doesn't affect runtime
|
||||
performance and it can be reliable even when interrupts or exceptions
|
||||
are involved.
|
||||
|
||||
For C code, gcc automatically generates DWARF CFI metadata. But for
|
||||
asm code, generating CFI is a tedious manual approach which requires
|
||||
manually placed .cfi assembler macros to be scattered throughout the
|
||||
code. It's clumsy and very easy to get wrong, and it makes the real
|
||||
code harder to read.
|
||||
|
||||
Stacktool will improve this situation in several ways. For code
|
||||
which already has CFI annotations, it will validate them. For code
|
||||
which doesn't have CFI annotations, it will generate them. So an
|
||||
architecture can opt to strip out all the manual .cfi annotations
|
||||
from their asm code and have objtool generate them instead.
|
||||
|
||||
We might also add a runtime stack validation debug option where we
|
||||
periodically walk the stack from schedule() and/or an NMI to ensure
|
||||
that the stack metadata is sane and that we reach the bottom of the
|
||||
stack.
|
||||
|
||||
So the benefit of objtool here will be that external tooling should
|
||||
always show perfect stack traces. And the same will be true for
|
||||
kernel warning/oops traces if the architecture has a runtime DWARF
|
||||
unwinder.
|
||||
For more details, see Documentation/x86/orc-unwinder.txt.
|
||||
|
||||
c) Higher live patching compatibility rate
|
||||
|
||||
(NOTE: This is not yet implemented)
|
||||
|
||||
Currently with CONFIG_LIVEPATCH there's a basic live patching
|
||||
framework which is safe for roughly 85-90% of "security" fixes. But
|
||||
patches can't have complex features like function dependency or
|
||||
prototype changes, or data structure changes.
|
||||
|
||||
There's a strong need to support patches which have the more complex
|
||||
features so that the patch compatibility rate for security fixes can
|
||||
eventually approach something resembling 100%. To achieve that, a
|
||||
"consistency model" is needed, which allows tasks to be safely
|
||||
transitioned from an unpatched state to a patched state.
|
||||
|
||||
One of the key requirements of the currently proposed livepatch
|
||||
consistency model [*] is that it needs to walk the stack of each
|
||||
sleeping task to determine if it can be transitioned to the patched
|
||||
state. If objtool can ensure that stack traces are reliable, this
|
||||
consistency model can be used and the live patching compatibility
|
||||
rate can be improved significantly.
|
||||
|
||||
[*] https://lkml.kernel.org/r/cover.1423499826.git.jpoimboe@redhat.com
|
||||
Livepatch has an optional "consistency model", which is needed for
|
||||
more complex patches. In order for the consistency model to work,
|
||||
stack traces need to be reliable (or an unreliable condition needs to
|
||||
be detectable). Objtool makes that possible.
|
||||
|
||||
For more details, see the livepatch documentation in the Linux kernel
|
||||
source tree at Documentation/livepatch/livepatch.txt.
|
||||
|
||||
Rules
|
||||
-----
|
||||
@@ -201,80 +164,84 @@ To achieve the validation, objtool enforces the following rules:
|
||||
return normally.
|
||||
|
||||
|
||||
Errors in .S files
|
||||
------------------
|
||||
Objtool warnings
|
||||
----------------
|
||||
|
||||
If you're getting an error in a compiled .S file which you don't
|
||||
understand, first make sure that the affected code follows the above
|
||||
rules.
|
||||
For asm files, if you're getting an error which doesn't make sense,
|
||||
first make sure that the affected code follows the above rules.
|
||||
|
||||
For C files, the common culprits are inline asm statements and calls to
|
||||
"noreturn" functions. See below for more details.
|
||||
|
||||
Another possible cause for errors in C code is if the Makefile removes
|
||||
-fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.
|
||||
|
||||
Here are some examples of common warnings reported by objtool, what
|
||||
they mean, and suggestions for how to fix them.
|
||||
|
||||
|
||||
1. asm_file.o: warning: objtool: func()+0x128: call without frame pointer save/setup
|
||||
1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup
|
||||
|
||||
The func() function made a function call without first saving and/or
|
||||
updating the frame pointer.
|
||||
updating the frame pointer, and CONFIG_FRAME_POINTER is enabled.
|
||||
|
||||
If func() is indeed a callable function, add proper frame pointer
|
||||
logic using the FRAME_BEGIN and FRAME_END macros. Otherwise, remove
|
||||
its ELF function annotation by changing ENDPROC to END.
|
||||
If the error is for an asm file, and func() is indeed a callable
|
||||
function, add proper frame pointer logic using the FRAME_BEGIN and
|
||||
FRAME_END macros. Otherwise, if it's not a callable function, remove
|
||||
its ELF function annotation by changing ENDPROC to END, and instead
|
||||
use the manual unwind hint macros in asm/unwind_hints.h.
|
||||
|
||||
If you're getting this error in a .c file, see the "Errors in .c
|
||||
files" section.
|
||||
If it's a GCC-compiled .c file, the error may be because the function
|
||||
uses an inline asm() statement which has a "call" instruction. An
|
||||
asm() statement with a call instruction must declare the use of the
|
||||
stack pointer in its output operand. On x86_64, this means adding
|
||||
the ASM_CALL_CONSTRAINT as an output constraint:
|
||||
|
||||
asm volatile("call func" : ASM_CALL_CONSTRAINT);
|
||||
|
||||
Otherwise the stack frame may not get created before the call.
|
||||
|
||||
|
||||
2. asm_file.o: warning: objtool: .text+0x53: return instruction outside of a callable function
|
||||
2. file.o: warning: objtool: .text+0x53: unreachable instruction
|
||||
|
||||
A return instruction was detected, but objtool couldn't find a way
|
||||
for a callable function to reach the instruction.
|
||||
Objtool couldn't find a code path to reach the instruction.
|
||||
|
||||
If the return instruction is inside (or reachable from) a callable
|
||||
function, the function needs to be annotated with the ENTRY/ENDPROC
|
||||
macros.
|
||||
If the error is for an asm file, and the instruction is inside (or
|
||||
reachable from) a callable function, the function should be annotated
|
||||
with the ENTRY/ENDPROC macros (ENDPROC is the important one).
|
||||
Otherwise, the code should probably be annotated with the unwind hint
|
||||
macros in asm/unwind_hints.h so objtool and the unwinder can know the
|
||||
stack state associated with the code.
|
||||
|
||||
If you _really_ need a return instruction outside of a function, and
|
||||
are 100% sure that it won't affect stack traces, you can tell
|
||||
objtool to ignore it. See the "Adding exceptions" section below.
|
||||
|
||||
|
||||
3. asm_file.o: warning: objtool: func()+0x9: function has unreachable instruction
|
||||
|
||||
The instruction lives inside of a callable function, but there's no
|
||||
possible control flow path from the beginning of the function to the
|
||||
instruction.
|
||||
|
||||
If the instruction is actually needed, and it's actually in a
|
||||
callable function, ensure that its function is properly annotated
|
||||
with ENTRY/ENDPROC.
|
||||
If you're 100% sure the code won't affect stack traces, or if you're
|
||||
a just a bad person, you can tell objtool to ignore it. See the
|
||||
"Adding exceptions" section below.
|
||||
|
||||
If it's not actually in a callable function (e.g. kernel entry code),
|
||||
change ENDPROC to END.
|
||||
|
||||
|
||||
4. asm_file.o: warning: objtool: func(): can't find starting instruction
|
||||
4. file.o: warning: objtool: func(): can't find starting instruction
|
||||
or
|
||||
asm_file.o: warning: objtool: func()+0x11dd: can't decode instruction
|
||||
file.o: warning: objtool: func()+0x11dd: can't decode instruction
|
||||
|
||||
Did you put data in a text section? If so, that can confuse
|
||||
Does the file have data in a text section? If so, that can confuse
|
||||
objtool's instruction decoder. Move the data to a more appropriate
|
||||
section like .data or .rodata.
|
||||
|
||||
|
||||
5. asm_file.o: warning: objtool: func()+0x6: kernel entry/exit from callable instruction
|
||||
5. file.o: warning: objtool: func()+0x6: unsupported instruction in callable function
|
||||
|
||||
This is a kernel entry/exit instruction like sysenter or sysret.
|
||||
Such instructions aren't allowed in a callable function, and are most
|
||||
likely part of the kernel entry code.
|
||||
|
||||
If the instruction isn't actually in a callable function, change
|
||||
ENDPROC to END.
|
||||
This is a kernel entry/exit instruction like sysenter or iret. Such
|
||||
instructions aren't allowed in a callable function, and are most
|
||||
likely part of the kernel entry code. They should usually not have
|
||||
the callable function annotation (ENDPROC) and should always be
|
||||
annotated with the unwind hint macros in asm/unwind_hints.h.
|
||||
|
||||
|
||||
6. asm_file.o: warning: objtool: func()+0x26: sibling call from callable instruction with changed frame pointer
|
||||
6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame
|
||||
|
||||
This is a dynamic jump or a jump to an undefined symbol. Stacktool
|
||||
This is a dynamic jump or a jump to an undefined symbol. Objtool
|
||||
assumed it's a sibling call and detected that the frame pointer
|
||||
wasn't first restored to its original state.
|
||||
|
||||
@@ -282,24 +249,28 @@ they mean, and suggestions for how to fix them.
|
||||
destination code to the local file.
|
||||
|
||||
If the instruction is not actually in a callable function (e.g.
|
||||
kernel entry code), change ENDPROC to END.
|
||||
kernel entry code), change ENDPROC to END and annotate manually with
|
||||
the unwind hint macros in asm/unwind_hints.h.
|
||||
|
||||
|
||||
7. asm_file: warning: objtool: func()+0x5c: frame pointer state mismatch
|
||||
7. file: warning: objtool: func()+0x5c: stack state mismatch
|
||||
|
||||
The instruction's frame pointer state is inconsistent, depending on
|
||||
which execution path was taken to reach the instruction.
|
||||
|
||||
Make sure the function pushes and sets up the frame pointer (for
|
||||
x86_64, this means rbp) at the beginning of the function and pops it
|
||||
at the end of the function. Also make sure that no other code in the
|
||||
function touches the frame pointer.
|
||||
Make sure that, when CONFIG_FRAME_POINTER is enabled, the function
|
||||
pushes and sets up the frame pointer (for x86_64, this means rbp) at
|
||||
the beginning of the function and pops it at the end of the function.
|
||||
Also make sure that no other code in the function touches the frame
|
||||
pointer.
|
||||
|
||||
Another possibility is that the code has some asm or inline asm which
|
||||
does some unusual things to the stack or the frame pointer. In such
|
||||
cases it's probably appropriate to use the unwind hint macros in
|
||||
asm/unwind_hints.h.
|
||||
|
||||
|
||||
Errors in .c files
|
||||
------------------
|
||||
|
||||
1. c_file.o: warning: objtool: funcA() falls through to next function funcB()
|
||||
8. file.o: warning: objtool: funcA() falls through to next function funcB()
|
||||
|
||||
This means that funcA() doesn't end with a return instruction or an
|
||||
unconditional jump, and that objtool has determined that the function
|
||||
@@ -318,21 +289,6 @@ Errors in .c files
|
||||
might be corrupt due to a gcc bug. For more details, see:
|
||||
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646
|
||||
|
||||
2. If you're getting any other objtool error in a compiled .c file, it
|
||||
may be because the file uses an asm() statement which has a "call"
|
||||
instruction. An asm() statement with a call instruction must declare
|
||||
the use of the stack pointer in its output operand. On x86_64, this
|
||||
means adding the ASM_CALL_CONSTRAINT as an output constraint:
|
||||
|
||||
asm volatile("call func" : ASM_CALL_CONSTRAINT);
|
||||
|
||||
Otherwise the stack frame may not get created before the call.
|
||||
|
||||
3. Another possible cause for errors in C code is if the Makefile removes
|
||||
-fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.
|
||||
|
||||
Also see the above section for .S file errors for more information what
|
||||
the individual error messages mean.
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
include ../scripts/Makefile.include
|
||||
include ../scripts/Makefile.arch
|
||||
|
||||
@@ -6,17 +7,19 @@ ARCH := x86
|
||||
endif
|
||||
|
||||
# always use the host compiler
|
||||
CC = gcc
|
||||
LD = ld
|
||||
AR = ar
|
||||
HOSTCC ?= gcc
|
||||
HOSTLD ?= ld
|
||||
CC = $(HOSTCC)
|
||||
LD = $(HOSTLD)
|
||||
AR = ar
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
|
||||
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
endif
|
||||
|
||||
SUBCMD_SRCDIR = $(srctree)/tools/lib/subcmd/
|
||||
LIBSUBCMD_OUTPUT = $(if $(OUTPUT),$(OUTPUT),$(PWD)/)
|
||||
LIBSUBCMD_OUTPUT = $(if $(OUTPUT),$(OUTPUT),$(CURDIR)/)
|
||||
LIBSUBCMD = $(LIBSUBCMD_OUTPUT)libsubcmd.a
|
||||
|
||||
OBJTOOL := $(OUTPUT)objtool
|
||||
@@ -24,8 +27,11 @@ OBJTOOL_IN := $(OBJTOOL)-in.o
|
||||
|
||||
all: $(OBJTOOL)
|
||||
|
||||
INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi
|
||||
CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES)
|
||||
INCLUDES := -I$(srctree)/tools/include \
|
||||
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
|
||||
-I$(srctree)/tools/objtool/arch/$(ARCH)/include
|
||||
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed
|
||||
CFLAGS += -Wall -Werror $(WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES)
|
||||
LDFLAGS += -lelf $(LIBSUBCMD)
|
||||
|
||||
# Allow old libelf to be used:
|
||||
@@ -39,19 +45,8 @@ include $(srctree)/tools/build/Makefile.include
|
||||
$(OBJTOOL_IN): fixdep FORCE
|
||||
@$(MAKE) $(build)=objtool
|
||||
|
||||
# Busybox's diff doesn't have -I, avoid warning in that case
|
||||
#
|
||||
$(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
|
||||
@(diff -I 2>&1 | grep -q 'option requires an argument' && \
|
||||
test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \
|
||||
diff -I'^#include' arch/x86/insn/insn.c ../../arch/x86/lib/insn.c >/dev/null && \
|
||||
diff -I'^#include' arch/x86/insn/inat.c ../../arch/x86/lib/inat.c >/dev/null && \
|
||||
diff arch/x86/insn/x86-opcode-map.txt ../../arch/x86/lib/x86-opcode-map.txt >/dev/null && \
|
||||
diff arch/x86/insn/gen-insn-attr-x86.awk ../../arch/x86/tools/gen-insn-attr-x86.awk >/dev/null && \
|
||||
diff -I'^#include' arch/x86/insn/insn.h ../../arch/x86/include/asm/insn.h >/dev/null && \
|
||||
diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \
|
||||
diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \
|
||||
|| echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true
|
||||
@$(CONFIG_SHELL) ./sync-check.sh
|
||||
$(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
|
||||
|
||||
|
||||
@@ -61,7 +56,7 @@ $(LIBSUBCMD): fixdep FORCE
|
||||
clean:
|
||||
$(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL)
|
||||
$(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||
$(Q)$(RM) $(OUTPUT)arch/x86/insn/inat-tables.c $(OUTPUT)fixdep
|
||||
$(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
|
||||
|
||||
FORCE:
|
||||
|
||||
|
||||
@@ -19,26 +19,64 @@
|
||||
#define _ARCH_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <linux/list.h>
|
||||
#include "elf.h"
|
||||
#include "cfi.h"
|
||||
|
||||
#define INSN_FP_SAVE 1
|
||||
#define INSN_FP_SETUP 2
|
||||
#define INSN_FP_RESTORE 3
|
||||
#define INSN_JUMP_CONDITIONAL 4
|
||||
#define INSN_JUMP_UNCONDITIONAL 5
|
||||
#define INSN_JUMP_DYNAMIC 6
|
||||
#define INSN_CALL 7
|
||||
#define INSN_CALL_DYNAMIC 8
|
||||
#define INSN_RETURN 9
|
||||
#define INSN_CONTEXT_SWITCH 10
|
||||
#define INSN_BUG 11
|
||||
#define INSN_NOP 12
|
||||
#define INSN_OTHER 13
|
||||
#define INSN_JUMP_CONDITIONAL 1
|
||||
#define INSN_JUMP_UNCONDITIONAL 2
|
||||
#define INSN_JUMP_DYNAMIC 3
|
||||
#define INSN_CALL 4
|
||||
#define INSN_CALL_DYNAMIC 5
|
||||
#define INSN_RETURN 6
|
||||
#define INSN_CONTEXT_SWITCH 7
|
||||
#define INSN_STACK 8
|
||||
#define INSN_BUG 9
|
||||
#define INSN_NOP 10
|
||||
#define INSN_OTHER 11
|
||||
#define INSN_LAST INSN_OTHER
|
||||
|
||||
enum op_dest_type {
|
||||
OP_DEST_REG,
|
||||
OP_DEST_REG_INDIRECT,
|
||||
OP_DEST_MEM,
|
||||
OP_DEST_PUSH,
|
||||
OP_DEST_LEAVE,
|
||||
};
|
||||
|
||||
struct op_dest {
|
||||
enum op_dest_type type;
|
||||
unsigned char reg;
|
||||
int offset;
|
||||
};
|
||||
|
||||
enum op_src_type {
|
||||
OP_SRC_REG,
|
||||
OP_SRC_REG_INDIRECT,
|
||||
OP_SRC_CONST,
|
||||
OP_SRC_POP,
|
||||
OP_SRC_ADD,
|
||||
OP_SRC_AND,
|
||||
};
|
||||
|
||||
struct op_src {
|
||||
enum op_src_type type;
|
||||
unsigned char reg;
|
||||
int offset;
|
||||
};
|
||||
|
||||
struct stack_op {
|
||||
struct op_dest dest;
|
||||
struct op_src src;
|
||||
};
|
||||
|
||||
void arch_initial_func_cfi_state(struct cfi_state *state);
|
||||
|
||||
int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||
unsigned long offset, unsigned int maxlen,
|
||||
unsigned int *len, unsigned char *type,
|
||||
unsigned long *displacement);
|
||||
unsigned long *immediate, struct stack_op *op);
|
||||
|
||||
bool arch_callee_saved_reg(unsigned char reg);
|
||||
|
||||
#endif /* _ARCH_H */
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
objtool-y += decode.o
|
||||
|
||||
inat_tables_script = arch/x86/insn/gen-insn-attr-x86.awk
|
||||
inat_tables_maps = arch/x86/insn/x86-opcode-map.txt
|
||||
inat_tables_script = arch/x86/tools/gen-insn-attr-x86.awk
|
||||
inat_tables_maps = arch/x86/lib/x86-opcode-map.txt
|
||||
|
||||
$(OUTPUT)arch/x86/insn/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
|
||||
$(OUTPUT)arch/x86/lib/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,gen)$(AWK) -f $(inat_tables_script) $(inat_tables_maps) > $@
|
||||
|
||||
$(OUTPUT)arch/x86/decode.o: $(OUTPUT)arch/x86/insn/inat-tables.c
|
||||
$(OUTPUT)arch/x86/decode.o: $(OUTPUT)arch/x86/lib/inat-tables.c
|
||||
|
||||
CFLAGS_decode.o += -I$(OUTPUT)arch/x86/insn
|
||||
CFLAGS_decode.o += -I$(OUTPUT)arch/x86/lib
|
||||
|
||||
@@ -19,14 +19,25 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#define unlikely(cond) (cond)
|
||||
#include "insn/insn.h"
|
||||
#include "insn/inat.c"
|
||||
#include "insn/insn.c"
|
||||
#include <asm/insn.h>
|
||||
#include "lib/inat.c"
|
||||
#include "lib/insn.c"
|
||||
|
||||
#include "../../elf.h"
|
||||
#include "../../arch.h"
|
||||
#include "../../warn.h"
|
||||
|
||||
static unsigned char op_to_cfi_reg[][2] = {
|
||||
{CFI_AX, CFI_R8},
|
||||
{CFI_CX, CFI_R9},
|
||||
{CFI_DX, CFI_R10},
|
||||
{CFI_BX, CFI_R11},
|
||||
{CFI_SP, CFI_R12},
|
||||
{CFI_BP, CFI_R13},
|
||||
{CFI_SI, CFI_R14},
|
||||
{CFI_DI, CFI_R15},
|
||||
};
|
||||
|
||||
static int is_x86_64(struct elf *elf)
|
||||
{
|
||||
switch (elf->ehdr.e_machine) {
|
||||
@@ -40,24 +51,50 @@ static int is_x86_64(struct elf *elf)
|
||||
}
|
||||
}
|
||||
|
||||
bool arch_callee_saved_reg(unsigned char reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CFI_BP:
|
||||
case CFI_BX:
|
||||
case CFI_R12:
|
||||
case CFI_R13:
|
||||
case CFI_R14:
|
||||
case CFI_R15:
|
||||
return true;
|
||||
|
||||
case CFI_AX:
|
||||
case CFI_CX:
|
||||
case CFI_DX:
|
||||
case CFI_SI:
|
||||
case CFI_DI:
|
||||
case CFI_SP:
|
||||
case CFI_R8:
|
||||
case CFI_R9:
|
||||
case CFI_R10:
|
||||
case CFI_R11:
|
||||
case CFI_RA:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||
unsigned long offset, unsigned int maxlen,
|
||||
unsigned int *len, unsigned char *type,
|
||||
unsigned long *immediate)
|
||||
unsigned long *immediate, struct stack_op *op)
|
||||
{
|
||||
struct insn insn;
|
||||
int x86_64;
|
||||
unsigned char op1, op2, ext;
|
||||
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;
|
||||
|
||||
x86_64 = is_x86_64(elf);
|
||||
if (x86_64 == -1)
|
||||
return -1;
|
||||
|
||||
insn_init(&insn, (void *)(sec->data + offset), maxlen, x86_64);
|
||||
insn_init(&insn, sec->data->d_buf + offset, maxlen, x86_64);
|
||||
insn_get_length(&insn);
|
||||
insn_get_opcode(&insn);
|
||||
insn_get_modrm(&insn);
|
||||
insn_get_immediate(&insn);
|
||||
|
||||
if (!insn_complete(&insn)) {
|
||||
WARN_FUNC("can't decode instruction", sec, offset);
|
||||
@@ -73,70 +110,317 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||
op1 = insn.opcode.bytes[0];
|
||||
op2 = insn.opcode.bytes[1];
|
||||
|
||||
if (insn.rex_prefix.nbytes) {
|
||||
rex = insn.rex_prefix.bytes[0];
|
||||
rex_w = X86_REX_W(rex) >> 3;
|
||||
rex_r = X86_REX_R(rex) >> 2;
|
||||
rex_x = X86_REX_X(rex) >> 1;
|
||||
rex_b = X86_REX_B(rex);
|
||||
}
|
||||
|
||||
if (insn.modrm.nbytes) {
|
||||
modrm = insn.modrm.bytes[0];
|
||||
modrm_mod = X86_MODRM_MOD(modrm);
|
||||
modrm_reg = X86_MODRM_REG(modrm);
|
||||
modrm_rm = X86_MODRM_RM(modrm);
|
||||
}
|
||||
|
||||
if (insn.sib.nbytes)
|
||||
sib = insn.sib.bytes[0];
|
||||
|
||||
switch (op1) {
|
||||
case 0x55:
|
||||
if (!insn.rex_prefix.nbytes)
|
||||
/* push rbp */
|
||||
*type = INSN_FP_SAVE;
|
||||
|
||||
case 0x1:
|
||||
case 0x29:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x5d:
|
||||
if (!insn.rex_prefix.nbytes)
|
||||
/* pop rbp */
|
||||
*type = INSN_FP_RESTORE;
|
||||
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;
|
||||
|
||||
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];
|
||||
|
||||
break;
|
||||
|
||||
case 0x68:
|
||||
case 0x6a:
|
||||
/* push immediate */
|
||||
*type = INSN_STACK;
|
||||
op->src.type = OP_SRC_CONST;
|
||||
op->dest.type = OP_DEST_PUSH;
|
||||
break;
|
||||
|
||||
case 0x70 ... 0x7f:
|
||||
*type = INSN_JUMP_CONDITIONAL;
|
||||
break;
|
||||
|
||||
case 0x81:
|
||||
case 0x83:
|
||||
if (rex != 0x48)
|
||||
break;
|
||||
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
||||
if (modrm == 0xc4)
|
||||
sign = 1;
|
||||
else if (modrm == 0xec)
|
||||
sign = -1;
|
||||
else
|
||||
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;
|
||||
break;
|
||||
|
||||
case 0x89:
|
||||
if (insn.rex_prefix.nbytes == 1 &&
|
||||
insn.rex_prefix.bytes[0] == 0x48 &&
|
||||
insn.modrm.nbytes && insn.modrm.bytes[0] == 0xe5)
|
||||
/* mov rsp, rbp */
|
||||
*type = INSN_FP_SETUP;
|
||||
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];
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
||||
/* fallthrough */
|
||||
case 0x88:
|
||||
if (!rex_b &&
|
||||
(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;
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x8b:
|
||||
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];
|
||||
|
||||
} 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];
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x8d:
|
||||
if (insn.rex_prefix.nbytes &&
|
||||
insn.rex_prefix.bytes[0] == 0x48 &&
|
||||
insn.modrm.nbytes && insn.modrm.bytes[0] == 0x2c &&
|
||||
insn.sib.nbytes && insn.sib.bytes[0] == 0x24)
|
||||
/* lea %(rsp), %rbp */
|
||||
*type = INSN_FP_SETUP;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
|
||||
} else if (rex == 0x49 && modrm == 0x62 &&
|
||||
insn.displacement.value == -8) {
|
||||
|
||||
/*
|
||||
* lea -0x8(%r10), %rsp
|
||||
*
|
||||
* 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;
|
||||
|
||||
} else if (rex == 0x49 && modrm == 0x65 &&
|
||||
insn.displacement.value == -16) {
|
||||
|
||||
/*
|
||||
* lea -0x10(%r13), %rsp
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0x8f:
|
||||
/* pop to mem */
|
||||
*type = INSN_STACK;
|
||||
op->src.type = OP_SRC_POP;
|
||||
op->dest.type = OP_DEST_MEM;
|
||||
break;
|
||||
|
||||
case 0x90:
|
||||
*type = INSN_NOP;
|
||||
break;
|
||||
|
||||
case 0x9c:
|
||||
/* pushf */
|
||||
*type = INSN_STACK;
|
||||
op->src.type = OP_SRC_CONST;
|
||||
op->dest.type = OP_DEST_PUSH;
|
||||
break;
|
||||
|
||||
case 0x9d:
|
||||
/* popf */
|
||||
*type = INSN_STACK;
|
||||
op->src.type = OP_SRC_POP;
|
||||
op->dest.type = OP_DEST_MEM;
|
||||
break;
|
||||
|
||||
case 0x0f:
|
||||
if (op2 >= 0x80 && op2 <= 0x8f)
|
||||
|
||||
if (op2 >= 0x80 && op2 <= 0x8f) {
|
||||
|
||||
*type = INSN_JUMP_CONDITIONAL;
|
||||
else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
|
||||
op2 == 0x35)
|
||||
|
||||
} else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
|
||||
op2 == 0x35) {
|
||||
|
||||
/* sysenter, sysret */
|
||||
*type = INSN_CONTEXT_SWITCH;
|
||||
else if (op2 == 0x0b || op2 == 0xb9)
|
||||
|
||||
} else if (op2 == 0x0b || op2 == 0xb9) {
|
||||
|
||||
/* ud2 */
|
||||
*type = INSN_BUG;
|
||||
else if (op2 == 0x0d || op2 == 0x1f)
|
||||
|
||||
} else if (op2 == 0x0d || op2 == 0x1f) {
|
||||
|
||||
/* nopl/nopw */
|
||||
*type = INSN_NOP;
|
||||
else if (op2 == 0x01 && insn.modrm.nbytes &&
|
||||
(insn.modrm.bytes[0] == 0xc2 ||
|
||||
insn.modrm.bytes[0] == 0xd8))
|
||||
/* vmlaunch, vmrun */
|
||||
*type = INSN_CONTEXT_SWITCH;
|
||||
|
||||
} else if (op2 == 0xa0 || op2 == 0xa8) {
|
||||
|
||||
/* push fs/gs */
|
||||
*type = INSN_STACK;
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 0xc9: /* leave */
|
||||
*type = INSN_FP_RESTORE;
|
||||
case 0xc9:
|
||||
/*
|
||||
* leave
|
||||
*
|
||||
* equivalent to:
|
||||
* mov bp, sp
|
||||
* pop bp
|
||||
*/
|
||||
*type = INSN_STACK;
|
||||
op->dest.type = OP_DEST_LEAVE;
|
||||
|
||||
break;
|
||||
|
||||
case 0xe3: /* jecxz/jrcxz */
|
||||
case 0xe3:
|
||||
/* jecxz/jrcxz */
|
||||
*type = INSN_JUMP_CONDITIONAL;
|
||||
break;
|
||||
|
||||
@@ -161,14 +445,27 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
ext = X86_MODRM_REG(insn.modrm.bytes[0]);
|
||||
if (ext == 2 || ext == 3)
|
||||
if (modrm_reg == 2 || modrm_reg == 3)
|
||||
|
||||
*type = INSN_CALL_DYNAMIC;
|
||||
else if (ext == 4)
|
||||
|
||||
else if (modrm_reg == 4)
|
||||
|
||||
*type = INSN_JUMP_DYNAMIC;
|
||||
else if (ext == 5) /*jmpf */
|
||||
|
||||
else if (modrm_reg == 5)
|
||||
|
||||
/* jmpf */
|
||||
*type = INSN_CONTEXT_SWITCH;
|
||||
|
||||
else if (modrm_reg == 6) {
|
||||
|
||||
/* push from mem */
|
||||
*type = INSN_STACK;
|
||||
op->src.type = OP_SRC_CONST;
|
||||
op->dest.type = OP_DEST_PUSH;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -179,3 +476,21 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void arch_initial_func_cfi_state(struct cfi_state *state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CFI_NUM_REGS; i++) {
|
||||
state->regs[i].base = CFI_UNDEFINED;
|
||||
state->regs[i].offset = 0;
|
||||
}
|
||||
|
||||
/* initial CFA (call frame address) */
|
||||
state->cfa.base = CFI_SP;
|
||||
state->cfa.offset = 8;
|
||||
|
||||
/* initial RA (return address) */
|
||||
state->regs[16].base = CFI_CFA;
|
||||
state->regs[16].offset = -8;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
#include "inat_types.h"
|
||||
#include <asm/inat_types.h>
|
||||
|
||||
/*
|
||||
* Internal bits. Don't use bitmasks directly, because these bits are
|
||||
@@ -21,7 +21,7 @@
|
||||
*/
|
||||
|
||||
/* insn_attr_t is defined in inat.h */
|
||||
#include "inat.h"
|
||||
#include <asm/inat.h>
|
||||
|
||||
struct insn_field {
|
||||
union {
|
||||
107
tools/objtool/arch/x86/include/asm/orc_types.h
Normal file
107
tools/objtool/arch/x86/include/asm/orc_types.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _ORC_TYPES_H
|
||||
#define _ORC_TYPES_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
/*
|
||||
* The ORC_REG_* registers are base registers which are used to find other
|
||||
* registers on the stack.
|
||||
*
|
||||
* ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
|
||||
* address of the previous frame: the caller's SP before it called the current
|
||||
* function.
|
||||
*
|
||||
* ORC_REG_UNDEFINED means the corresponding register's value didn't change in
|
||||
* the current frame.
|
||||
*
|
||||
* The most commonly used base registers are SP and BP -- which the previous SP
|
||||
* is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is
|
||||
* usually based on.
|
||||
*
|
||||
* The rest of the base registers are needed for special cases like entry code
|
||||
* and GCC realigned stacks.
|
||||
*/
|
||||
#define ORC_REG_UNDEFINED 0
|
||||
#define ORC_REG_PREV_SP 1
|
||||
#define ORC_REG_DX 2
|
||||
#define ORC_REG_DI 3
|
||||
#define ORC_REG_BP 4
|
||||
#define ORC_REG_SP 5
|
||||
#define ORC_REG_R10 6
|
||||
#define ORC_REG_R13 7
|
||||
#define ORC_REG_BP_INDIRECT 8
|
||||
#define ORC_REG_SP_INDIRECT 9
|
||||
#define ORC_REG_MAX 15
|
||||
|
||||
/*
|
||||
* ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
|
||||
* caller's SP right before it made the call). Used for all callable
|
||||
* functions, i.e. all C code and all callable asm functions.
|
||||
*
|
||||
* ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
|
||||
* to a fully populated pt_regs from a syscall, interrupt, or exception.
|
||||
*
|
||||
* ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
|
||||
* points to the iret return frame.
|
||||
*
|
||||
* The UNWIND_HINT macros are used only for the unwind_hint struct. They
|
||||
* aren't used in struct orc_entry due to size and complexity constraints.
|
||||
* Objtool converts them to real types when it converts the hints to orc
|
||||
* entries.
|
||||
*/
|
||||
#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
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/*
|
||||
* This struct is more or less a vastly simplified version of the DWARF Call
|
||||
* Frame Information standard. It contains only the necessary parts of DWARF
|
||||
* CFI, simplified for ease of access by the in-kernel unwinder. It tells the
|
||||
* unwinder how to find the previous SP and BP (and sometimes entry regs) on
|
||||
* the stack for a given code address. Each instance of the struct corresponds
|
||||
* to one or more code locations.
|
||||
*/
|
||||
struct orc_entry {
|
||||
s16 sp_offset;
|
||||
s16 bp_offset;
|
||||
unsigned sp_reg:4;
|
||||
unsigned bp_reg:4;
|
||||
unsigned type:2;
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct is used by asm and inline asm code to manually annotate the
|
||||
* location of registers on the stack for the ORC unwinder.
|
||||
*
|
||||
* Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
|
||||
*/
|
||||
struct unwind_hint {
|
||||
u32 ip;
|
||||
s16 sp_offset;
|
||||
u8 sp_reg;
|
||||
u8 type;
|
||||
};
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* _ORC_TYPES_H */
|
||||
@@ -18,7 +18,7 @@
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*/
|
||||
#include "insn.h"
|
||||
#include <asm/insn.h>
|
||||
|
||||
/* Attribute tables are generated from opcode map */
|
||||
#include "inat-tables.c"
|
||||
@@ -23,8 +23,8 @@
|
||||
#else
|
||||
#include <string.h>
|
||||
#endif
|
||||
#include "inat.h"
|
||||
#include "insn.h"
|
||||
#include <asm/inat.h>
|
||||
#include <asm/insn.h>
|
||||
|
||||
/* Verify next sizeof(t) bytes can be on the same instruction */
|
||||
#define validate_next(t, insn, n) \
|
||||
File diff suppressed because it is too large
Load Diff
68
tools/objtool/builtin-orc.c
Normal file
68
tools/objtool/builtin-orc.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* objtool orc:
|
||||
*
|
||||
* This command analyzes a .o file and adds .orc_unwind and .orc_unwind_ip
|
||||
* sections to it, which is used by the in-kernel ORC unwinder.
|
||||
*
|
||||
* This command is a superset of "objtool check".
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "builtin.h"
|
||||
#include "check.h"
|
||||
|
||||
|
||||
static const char *orc_usage[] = {
|
||||
"objtool orc generate [<options>] file.o",
|
||||
"objtool orc dump file.o",
|
||||
NULL,
|
||||
};
|
||||
|
||||
int cmd_orc(int argc, const char **argv)
|
||||
{
|
||||
const char *objname;
|
||||
|
||||
argc--; argv++;
|
||||
if (argc <= 0)
|
||||
usage_with_options(orc_usage, check_options);
|
||||
|
||||
if (!strncmp(argv[0], "gen", 3)) {
|
||||
argc = parse_options(argc, argv, check_options, orc_usage, 0);
|
||||
if (argc != 1)
|
||||
usage_with_options(orc_usage, check_options);
|
||||
|
||||
objname = argv[0];
|
||||
|
||||
return check(objname, true);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0], "dump")) {
|
||||
if (argc != 2)
|
||||
usage_with_options(orc_usage, check_options);
|
||||
|
||||
objname = argv[1];
|
||||
|
||||
return orc_dump(objname);
|
||||
}
|
||||
|
||||
usage_with_options(orc_usage, check_options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -17,6 +17,12 @@
|
||||
#ifndef _BUILTIN_H
|
||||
#define _BUILTIN_H
|
||||
|
||||
#include <subcmd/parse-options.h>
|
||||
|
||||
extern const struct option check_options[];
|
||||
extern bool no_fp, no_unreachable, retpoline, module;
|
||||
|
||||
extern int cmd_check(int argc, const char **argv);
|
||||
extern int cmd_orc(int argc, const char **argv);
|
||||
|
||||
#endif /* _BUILTIN_H */
|
||||
|
||||
55
tools/objtool/cfi.h
Normal file
55
tools/objtool/cfi.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _OBJTOOL_CFI_H
|
||||
#define _OBJTOOL_CFI_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_reg regs[CFI_NUM_REGS];
|
||||
};
|
||||
|
||||
#endif /* _OBJTOOL_CFI_H */
|
||||
2209
tools/objtool/check.c
Normal file
2209
tools/objtool/check.c
Normal file
File diff suppressed because it is too large
Load Diff
82
tools/objtool/check.h
Normal file
82
tools/objtool/check.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _CHECK_H
|
||||
#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;
|
||||
int drap_reg, drap_offset;
|
||||
struct cfi_reg vals[CFI_NUM_REGS];
|
||||
};
|
||||
|
||||
struct instruction {
|
||||
struct list_head list;
|
||||
struct hlist_node hash;
|
||||
struct section *sec;
|
||||
unsigned long offset;
|
||||
unsigned int len;
|
||||
unsigned char type;
|
||||
unsigned long immediate;
|
||||
bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts;
|
||||
bool retpoline_safe;
|
||||
struct symbol *call_dest;
|
||||
struct instruction *jump_dest;
|
||||
struct instruction *first_jump_src;
|
||||
struct list_head alts;
|
||||
struct symbol *func;
|
||||
struct stack_op stack_op;
|
||||
struct insn_state state;
|
||||
struct orc_entry orc;
|
||||
};
|
||||
|
||||
struct objtool_file {
|
||||
struct elf *elf;
|
||||
struct list_head insn_list;
|
||||
DECLARE_HASHTABLE(insn_hash, 16);
|
||||
struct section *rodata, *whitelist;
|
||||
bool ignore_unreachables, c_file, hints;
|
||||
};
|
||||
|
||||
int check(const char *objname, bool orc);
|
||||
|
||||
struct instruction *find_insn(struct objtool_file *file,
|
||||
struct section *sec, unsigned long offset);
|
||||
|
||||
#define for_each_insn(file, insn) \
|
||||
list_for_each_entry(insn, &file->insn_list, list)
|
||||
|
||||
#define sec_for_each_insn(file, sec, insn) \
|
||||
for (insn = find_insn(file, sec, 0); \
|
||||
insn && &insn->list != &file->insn_list && \
|
||||
insn->sec == sec; \
|
||||
insn = list_next_entry(insn, list))
|
||||
|
||||
|
||||
#endif /* _CHECK_H */
|
||||
@@ -31,13 +31,6 @@
|
||||
#include "elf.h"
|
||||
#include "warn.h"
|
||||
|
||||
/*
|
||||
* Fallback for systems without this "read, mmaping if possible" cmd.
|
||||
*/
|
||||
#ifndef ELF_C_READ_MMAP
|
||||
#define ELF_C_READ_MMAP ELF_C_READ
|
||||
#endif
|
||||
|
||||
struct section *find_section_by_name(struct elf *elf, const char *name)
|
||||
{
|
||||
struct section *sec;
|
||||
@@ -86,6 +79,19 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
|
||||
{
|
||||
struct section *sec;
|
||||
struct symbol *sym;
|
||||
|
||||
list_for_each_entry(sec, &elf->sections, list)
|
||||
list_for_each_entry(sym, &sec->symbol_list, list)
|
||||
if (!strcmp(sym->name, name))
|
||||
return sym;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
|
||||
{
|
||||
struct symbol *sym;
|
||||
@@ -140,12 +146,12 @@ static int read_sections(struct elf *elf)
|
||||
int i;
|
||||
|
||||
if (elf_getshdrnum(elf->elf, §ions_nr)) {
|
||||
perror("elf_getshdrnum");
|
||||
WARN_ELF("elf_getshdrnum");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elf_getshdrstrndx(elf->elf, &shstrndx)) {
|
||||
perror("elf_getshdrstrndx");
|
||||
WARN_ELF("elf_getshdrstrndx");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -166,37 +172,37 @@ static int read_sections(struct elf *elf)
|
||||
|
||||
s = elf_getscn(elf->elf, i);
|
||||
if (!s) {
|
||||
perror("elf_getscn");
|
||||
WARN_ELF("elf_getscn");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sec->idx = elf_ndxscn(s);
|
||||
|
||||
if (!gelf_getshdr(s, &sec->sh)) {
|
||||
perror("gelf_getshdr");
|
||||
WARN_ELF("gelf_getshdr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name);
|
||||
if (!sec->name) {
|
||||
perror("elf_strptr");
|
||||
WARN_ELF("elf_strptr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sec->elf_data = elf_getdata(s, NULL);
|
||||
if (!sec->elf_data) {
|
||||
perror("elf_getdata");
|
||||
return -1;
|
||||
if (sec->sh.sh_size != 0) {
|
||||
sec->data = elf_getdata(s, NULL);
|
||||
if (!sec->data) {
|
||||
WARN_ELF("elf_getdata");
|
||||
return -1;
|
||||
}
|
||||
if (sec->data->d_off != 0 ||
|
||||
sec->data->d_size != sec->sh.sh_size) {
|
||||
WARN("unexpected data attributes for %s",
|
||||
sec->name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (sec->elf_data->d_off != 0 ||
|
||||
sec->elf_data->d_size != sec->sh.sh_size) {
|
||||
WARN("unexpected data attributes for %s", sec->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sec->data = (unsigned long)sec->elf_data->d_buf;
|
||||
sec->len = sec->elf_data->d_size;
|
||||
sec->len = sec->sh.sh_size;
|
||||
}
|
||||
|
||||
/* sanity check, one more call to elf_nextscn() should return NULL */
|
||||
@@ -210,10 +216,11 @@ static int read_sections(struct elf *elf)
|
||||
|
||||
static int read_symbols(struct elf *elf)
|
||||
{
|
||||
struct section *symtab;
|
||||
struct symbol *sym;
|
||||
struct section *symtab, *sec;
|
||||
struct symbol *sym, *pfunc;
|
||||
struct list_head *entry, *tmp;
|
||||
int symbols_nr, i;
|
||||
char *coldstr;
|
||||
|
||||
symtab = find_section_by_name(elf, ".symtab");
|
||||
if (!symtab) {
|
||||
@@ -233,15 +240,15 @@ static int read_symbols(struct elf *elf)
|
||||
|
||||
sym->idx = i;
|
||||
|
||||
if (!gelf_getsym(symtab->elf_data, i, &sym->sym)) {
|
||||
perror("gelf_getsym");
|
||||
if (!gelf_getsym(symtab->data, i, &sym->sym)) {
|
||||
WARN_ELF("gelf_getsym");
|
||||
goto err;
|
||||
}
|
||||
|
||||
sym->name = elf_strptr(elf->elf, symtab->sh.sh_link,
|
||||
sym->sym.st_name);
|
||||
if (!sym->name) {
|
||||
perror("elf_strptr");
|
||||
WARN_ELF("elf_strptr");
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -288,6 +295,30 @@ static int read_symbols(struct elf *elf)
|
||||
hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
|
||||
}
|
||||
|
||||
/* Create parent/child links for any cold subfunctions */
|
||||
list_for_each_entry(sec, &elf->sections, list) {
|
||||
list_for_each_entry(sym, &sec->symbol_list, list) {
|
||||
if (sym->type != STT_FUNC)
|
||||
continue;
|
||||
sym->pfunc = sym->cfunc = sym;
|
||||
coldstr = strstr(sym->name, ".cold.");
|
||||
if (coldstr) {
|
||||
coldstr[0] = '\0';
|
||||
pfunc = find_symbol_by_name(elf, sym->name);
|
||||
coldstr[0] = '.';
|
||||
|
||||
if (!pfunc) {
|
||||
WARN("%s(): can't find parent function",
|
||||
sym->name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
sym->pfunc = pfunc;
|
||||
pfunc->cfunc = sym;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@@ -323,8 +354,8 @@ static int read_relas(struct elf *elf)
|
||||
}
|
||||
memset(rela, 0, sizeof(*rela));
|
||||
|
||||
if (!gelf_getrela(sec->elf_data, i, &rela->rela)) {
|
||||
perror("gelf_getrela");
|
||||
if (!gelf_getrela(sec->data, i, &rela->rela)) {
|
||||
WARN_ELF("gelf_getrela");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -348,9 +379,10 @@ static int read_relas(struct elf *elf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct elf *elf_open(const char *name)
|
||||
struct elf *elf_open(const char *name, int flags)
|
||||
{
|
||||
struct elf *elf;
|
||||
Elf_Cmd cmd;
|
||||
|
||||
elf_version(EV_CURRENT);
|
||||
|
||||
@@ -363,27 +395,28 @@ struct elf *elf_open(const char *name)
|
||||
|
||||
INIT_LIST_HEAD(&elf->sections);
|
||||
|
||||
elf->name = strdup(name);
|
||||
if (!elf->name) {
|
||||
perror("strdup");
|
||||
goto err;
|
||||
}
|
||||
|
||||
elf->fd = open(name, O_RDONLY);
|
||||
elf->fd = open(name, flags);
|
||||
if (elf->fd == -1) {
|
||||
fprintf(stderr, "objtool: Can't open '%s': %s\n",
|
||||
name, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
elf->elf = elf_begin(elf->fd, ELF_C_READ_MMAP, NULL);
|
||||
if ((flags & O_ACCMODE) == O_RDONLY)
|
||||
cmd = ELF_C_READ_MMAP;
|
||||
else if ((flags & O_ACCMODE) == O_RDWR)
|
||||
cmd = ELF_C_RDWR;
|
||||
else /* O_WRONLY */
|
||||
cmd = ELF_C_WRITE;
|
||||
|
||||
elf->elf = elf_begin(elf->fd, cmd, NULL);
|
||||
if (!elf->elf) {
|
||||
perror("elf_begin");
|
||||
WARN_ELF("elf_begin");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!gelf_getehdr(elf->elf, &elf->ehdr)) {
|
||||
perror("gelf_getehdr");
|
||||
WARN_ELF("gelf_getehdr");
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -403,12 +436,212 @@ err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct section *elf_create_section(struct elf *elf, const char *name,
|
||||
size_t entsize, int nr)
|
||||
{
|
||||
struct section *sec, *shstrtab;
|
||||
size_t size = entsize * nr;
|
||||
struct Elf_Scn *s;
|
||||
Elf_Data *data;
|
||||
|
||||
sec = malloc(sizeof(*sec));
|
||||
if (!sec) {
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
}
|
||||
memset(sec, 0, sizeof(*sec));
|
||||
|
||||
INIT_LIST_HEAD(&sec->symbol_list);
|
||||
INIT_LIST_HEAD(&sec->rela_list);
|
||||
hash_init(sec->rela_hash);
|
||||
hash_init(sec->symbol_hash);
|
||||
|
||||
list_add_tail(&sec->list, &elf->sections);
|
||||
|
||||
s = elf_newscn(elf->elf);
|
||||
if (!s) {
|
||||
WARN_ELF("elf_newscn");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sec->name = strdup(name);
|
||||
if (!sec->name) {
|
||||
perror("strdup");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sec->idx = elf_ndxscn(s);
|
||||
sec->len = size;
|
||||
sec->changed = true;
|
||||
|
||||
sec->data = elf_newdata(s);
|
||||
if (!sec->data) {
|
||||
WARN_ELF("elf_newdata");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sec->data->d_size = size;
|
||||
sec->data->d_align = 1;
|
||||
|
||||
if (size) {
|
||||
sec->data->d_buf = malloc(size);
|
||||
if (!sec->data->d_buf) {
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
}
|
||||
memset(sec->data->d_buf, 0, size);
|
||||
}
|
||||
|
||||
if (!gelf_getshdr(s, &sec->sh)) {
|
||||
WARN_ELF("gelf_getshdr");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sec->sh.sh_size = size;
|
||||
sec->sh.sh_entsize = entsize;
|
||||
sec->sh.sh_type = SHT_PROGBITS;
|
||||
sec->sh.sh_addralign = 1;
|
||||
sec->sh.sh_flags = SHF_ALLOC;
|
||||
|
||||
|
||||
/* Add section name to .shstrtab */
|
||||
shstrtab = find_section_by_name(elf, ".shstrtab");
|
||||
if (!shstrtab) {
|
||||
WARN("can't find .shstrtab section");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = elf_getscn(elf->elf, shstrtab->idx);
|
||||
if (!s) {
|
||||
WARN_ELF("elf_getscn");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = elf_newdata(s);
|
||||
if (!data) {
|
||||
WARN_ELF("elf_newdata");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->d_buf = sec->name;
|
||||
data->d_size = strlen(name) + 1;
|
||||
data->d_align = 1;
|
||||
|
||||
sec->sh.sh_name = shstrtab->len;
|
||||
|
||||
shstrtab->len += strlen(name) + 1;
|
||||
shstrtab->changed = true;
|
||||
|
||||
return sec;
|
||||
}
|
||||
|
||||
struct section *elf_create_rela_section(struct elf *elf, struct section *base)
|
||||
{
|
||||
char *relaname;
|
||||
struct section *sec;
|
||||
|
||||
relaname = malloc(strlen(base->name) + strlen(".rela") + 1);
|
||||
if (!relaname) {
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
}
|
||||
strcpy(relaname, ".rela");
|
||||
strcat(relaname, base->name);
|
||||
|
||||
sec = elf_create_section(elf, relaname, sizeof(GElf_Rela), 0);
|
||||
free(relaname);
|
||||
if (!sec)
|
||||
return NULL;
|
||||
|
||||
base->rela = sec;
|
||||
sec->base = base;
|
||||
|
||||
sec->sh.sh_type = SHT_RELA;
|
||||
sec->sh.sh_addralign = 8;
|
||||
sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
|
||||
sec->sh.sh_info = base->idx;
|
||||
sec->sh.sh_flags = SHF_INFO_LINK;
|
||||
|
||||
return sec;
|
||||
}
|
||||
|
||||
int elf_rebuild_rela_section(struct section *sec)
|
||||
{
|
||||
struct rela *rela;
|
||||
int nr, idx = 0, size;
|
||||
GElf_Rela *relas;
|
||||
|
||||
nr = 0;
|
||||
list_for_each_entry(rela, &sec->rela_list, list)
|
||||
nr++;
|
||||
|
||||
size = nr * sizeof(*relas);
|
||||
relas = malloc(size);
|
||||
if (!relas) {
|
||||
perror("malloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sec->data->d_buf = relas;
|
||||
sec->data->d_size = size;
|
||||
|
||||
sec->sh.sh_size = size;
|
||||
|
||||
idx = 0;
|
||||
list_for_each_entry(rela, &sec->rela_list, list) {
|
||||
relas[idx].r_offset = rela->offset;
|
||||
relas[idx].r_addend = rela->addend;
|
||||
relas[idx].r_info = GELF_R_INFO(rela->sym->idx, rela->type);
|
||||
idx++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int elf_write(struct elf *elf)
|
||||
{
|
||||
struct section *sec;
|
||||
Elf_Scn *s;
|
||||
|
||||
/* Update section headers for changed sections: */
|
||||
list_for_each_entry(sec, &elf->sections, list) {
|
||||
if (sec->changed) {
|
||||
s = elf_getscn(elf->elf, sec->idx);
|
||||
if (!s) {
|
||||
WARN_ELF("elf_getscn");
|
||||
return -1;
|
||||
}
|
||||
if (!gelf_update_shdr(s, &sec->sh)) {
|
||||
WARN_ELF("gelf_update_shdr");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the new section header entries get updated properly. */
|
||||
elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY);
|
||||
|
||||
/* Write all changes to the file. */
|
||||
if (elf_update(elf->elf, ELF_C_WRITE) < 0) {
|
||||
WARN_ELF("elf_update");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void elf_close(struct elf *elf)
|
||||
{
|
||||
struct section *sec, *tmpsec;
|
||||
struct symbol *sym, *tmpsym;
|
||||
struct rela *rela, *tmprela;
|
||||
|
||||
if (elf->elf)
|
||||
elf_end(elf->elf);
|
||||
|
||||
if (elf->fd > 0)
|
||||
close(elf->fd);
|
||||
|
||||
list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) {
|
||||
list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) {
|
||||
list_del(&sym->list);
|
||||
@@ -423,11 +656,6 @@ void elf_close(struct elf *elf)
|
||||
list_del(&sec->list);
|
||||
free(sec);
|
||||
}
|
||||
if (elf->name)
|
||||
free(elf->name);
|
||||
if (elf->fd > 0)
|
||||
close(elf->fd);
|
||||
if (elf->elf)
|
||||
elf_end(elf->elf);
|
||||
|
||||
free(elf);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,13 @@
|
||||
# define elf_getshdrstrndx elf_getshstrndx
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Fallback for systems without this "read, mmaping if possible" cmd.
|
||||
*/
|
||||
#ifndef ELF_C_READ_MMAP
|
||||
#define ELF_C_READ_MMAP ELF_C_READ
|
||||
#endif
|
||||
|
||||
struct section {
|
||||
struct list_head list;
|
||||
GElf_Shdr sh;
|
||||
@@ -37,11 +44,11 @@ struct section {
|
||||
DECLARE_HASHTABLE(rela_hash, 16);
|
||||
struct section *base, *rela;
|
||||
struct symbol *sym;
|
||||
Elf_Data *elf_data;
|
||||
Elf_Data *data;
|
||||
char *name;
|
||||
int idx;
|
||||
unsigned long data;
|
||||
unsigned int len;
|
||||
bool changed, text;
|
||||
};
|
||||
|
||||
struct symbol {
|
||||
@@ -54,6 +61,7 @@ struct symbol {
|
||||
unsigned char bind, type;
|
||||
unsigned long offset;
|
||||
unsigned int len;
|
||||
struct symbol *pfunc, *cfunc;
|
||||
};
|
||||
|
||||
struct rela {
|
||||
@@ -76,16 +84,23 @@ struct elf {
|
||||
};
|
||||
|
||||
|
||||
struct elf *elf_open(const char *name);
|
||||
struct elf *elf_open(const char *name, int flags);
|
||||
struct section *find_section_by_name(struct elf *elf, const char *name);
|
||||
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
|
||||
struct symbol *find_symbol_by_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 section *sec, unsigned long offset);
|
||||
struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
|
||||
unsigned int len);
|
||||
struct symbol *find_containing_func(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)
|
||||
|
||||
#endif /* _OBJTOOL_ELF_H */
|
||||
|
||||
@@ -31,11 +31,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <subcmd/exec-cmd.h>
|
||||
#include <subcmd/pager.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "builtin.h"
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
struct cmd_struct {
|
||||
const char *name;
|
||||
int (*fn)(int, const char **);
|
||||
@@ -43,10 +42,11 @@ struct cmd_struct {
|
||||
};
|
||||
|
||||
static const char objtool_usage_string[] =
|
||||
"objtool [OPTIONS] COMMAND [ARGS]";
|
||||
"objtool COMMAND [ARGS]";
|
||||
|
||||
static struct cmd_struct objtool_cmds[] = {
|
||||
{"check", cmd_check, "Perform stack metadata validation on an object file" },
|
||||
{"orc", cmd_orc, "Generate in-place ORC unwind tables for an object file" },
|
||||
};
|
||||
|
||||
bool help;
|
||||
@@ -70,7 +70,7 @@ static void cmd_usage(void)
|
||||
|
||||
printf("\n");
|
||||
|
||||
exit(1);
|
||||
exit(129);
|
||||
}
|
||||
|
||||
static void handle_options(int *argc, const char ***argv)
|
||||
@@ -86,9 +86,7 @@ static void handle_options(int *argc, const char ***argv)
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option: %s\n", cmd);
|
||||
fprintf(stderr, "\n Usage: %s\n",
|
||||
objtool_usage_string);
|
||||
exit(1);
|
||||
cmd_usage();
|
||||
}
|
||||
|
||||
(*argv)++;
|
||||
|
||||
30
tools/objtool/orc.h
Normal file
30
tools/objtool/orc.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 */
|
||||
213
tools/objtool/orc_dump.c
Normal file
213
tools/objtool/orc_dump.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include "orc.h"
|
||||
#include "warn.h"
|
||||
|
||||
static const char *reg_name(unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case ORC_REG_PREV_SP:
|
||||
return "prevsp";
|
||||
case ORC_REG_DX:
|
||||
return "dx";
|
||||
case ORC_REG_DI:
|
||||
return "di";
|
||||
case ORC_REG_BP:
|
||||
return "bp";
|
||||
case ORC_REG_SP:
|
||||
return "sp";
|
||||
case ORC_REG_R10:
|
||||
return "r10";
|
||||
case ORC_REG_R13:
|
||||
return "r13";
|
||||
case ORC_REG_BP_INDIRECT:
|
||||
return "bp(ind)";
|
||||
case ORC_REG_SP_INDIRECT:
|
||||
return "sp(ind)";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *orc_type_name(unsigned int type)
|
||||
{
|
||||
switch (type) {
|
||||
case ORC_TYPE_CALL:
|
||||
return "call";
|
||||
case ORC_TYPE_REGS:
|
||||
return "regs";
|
||||
case ORC_TYPE_REGS_IRET:
|
||||
return "iret";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static void print_reg(unsigned int reg, int offset)
|
||||
{
|
||||
if (reg == ORC_REG_BP_INDIRECT)
|
||||
printf("(bp%+d)", offset);
|
||||
else if (reg == ORC_REG_SP_INDIRECT)
|
||||
printf("(sp%+d)", offset);
|
||||
else if (reg == ORC_REG_UNDEFINED)
|
||||
printf("(und)");
|
||||
else
|
||||
printf("%s%+d", reg_name(reg), offset);
|
||||
}
|
||||
|
||||
int orc_dump(const char *_objname)
|
||||
{
|
||||
int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0;
|
||||
struct orc_entry *orc = NULL;
|
||||
char *name;
|
||||
size_t nr_sections;
|
||||
Elf64_Addr orc_ip_addr = 0;
|
||||
size_t shstrtab_idx;
|
||||
Elf *elf;
|
||||
Elf_Scn *scn;
|
||||
GElf_Shdr sh;
|
||||
GElf_Rela rela;
|
||||
GElf_Sym sym;
|
||||
Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL;
|
||||
|
||||
|
||||
objname = _objname;
|
||||
|
||||
elf_version(EV_CURRENT);
|
||||
|
||||
fd = open(objname, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror("open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
||||
if (!elf) {
|
||||
WARN_ELF("elf_begin");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elf_getshdrnum(elf, &nr_sections)) {
|
||||
WARN_ELF("elf_getshdrnum");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (elf_getshdrstrndx(elf, &shstrtab_idx)) {
|
||||
WARN_ELF("elf_getshdrstrndx");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_sections; i++) {
|
||||
scn = elf_getscn(elf, i);
|
||||
if (!scn) {
|
||||
WARN_ELF("elf_getscn");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!gelf_getshdr(scn, &sh)) {
|
||||
WARN_ELF("gelf_getshdr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
|
||||
if (!name) {
|
||||
WARN_ELF("elf_strptr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = elf_getdata(scn, NULL);
|
||||
if (!data) {
|
||||
WARN_ELF("elf_getdata");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(name, ".symtab")) {
|
||||
symtab = data;
|
||||
} else if (!strcmp(name, ".orc_unwind")) {
|
||||
orc = data->d_buf;
|
||||
orc_size = sh.sh_size;
|
||||
} else if (!strcmp(name, ".orc_unwind_ip")) {
|
||||
orc_ip = data->d_buf;
|
||||
orc_ip_addr = sh.sh_addr;
|
||||
} else if (!strcmp(name, ".rela.orc_unwind_ip")) {
|
||||
rela_orc_ip = data;
|
||||
}
|
||||
}
|
||||
|
||||
if (!symtab || !orc || !orc_ip)
|
||||
return 0;
|
||||
|
||||
if (orc_size % sizeof(*orc) != 0) {
|
||||
WARN("bad .orc_unwind section size");
|
||||
return -1;
|
||||
}
|
||||
|
||||
nr_entries = orc_size / sizeof(*orc);
|
||||
for (i = 0; i < nr_entries; i++) {
|
||||
if (rela_orc_ip) {
|
||||
if (!gelf_getrela(rela_orc_ip, i, &rela)) {
|
||||
WARN_ELF("gelf_getrela");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) {
|
||||
WARN_ELF("gelf_getsym");
|
||||
return -1;
|
||||
}
|
||||
|
||||
scn = elf_getscn(elf, sym.st_shndx);
|
||||
if (!scn) {
|
||||
WARN_ELF("elf_getscn");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!gelf_getshdr(scn, &sh)) {
|
||||
WARN_ELF("gelf_getshdr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
name = elf_strptr(elf, shstrtab_idx, sh.sh_name);
|
||||
if (!name || !*name) {
|
||||
WARN_ELF("elf_strptr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s+%llx:", name, (unsigned long long)rela.r_addend);
|
||||
|
||||
} else {
|
||||
printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i]));
|
||||
}
|
||||
|
||||
|
||||
printf(" sp:");
|
||||
|
||||
print_reg(orc[i].sp_reg, orc[i].sp_offset);
|
||||
|
||||
printf(" bp:");
|
||||
|
||||
print_reg(orc[i].bp_reg, orc[i].bp_offset);
|
||||
|
||||
printf(" type:%s\n", orc_type_name(orc[i].type));
|
||||
}
|
||||
|
||||
elf_end(elf);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
221
tools/objtool/orc_gen.c
Normal file
221
tools/objtool/orc_gen.c
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "orc.h"
|
||||
#include "check.h"
|
||||
#include "warn.h"
|
||||
|
||||
int create_orc(struct objtool_file *file)
|
||||
{
|
||||
struct instruction *insn;
|
||||
|
||||
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];
|
||||
|
||||
if (cfa->base == CFI_UNDEFINED) {
|
||||
orc->sp_reg = ORC_REG_UNDEFINED;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (cfa->base) {
|
||||
case CFI_SP:
|
||||
orc->sp_reg = ORC_REG_SP;
|
||||
break;
|
||||
case CFI_SP_INDIRECT:
|
||||
orc->sp_reg = ORC_REG_SP_INDIRECT;
|
||||
break;
|
||||
case CFI_BP:
|
||||
orc->sp_reg = ORC_REG_BP;
|
||||
break;
|
||||
case CFI_BP_INDIRECT:
|
||||
orc->sp_reg = ORC_REG_BP_INDIRECT;
|
||||
break;
|
||||
case CFI_R10:
|
||||
orc->sp_reg = ORC_REG_R10;
|
||||
break;
|
||||
case CFI_R13:
|
||||
orc->sp_reg = ORC_REG_R13;
|
||||
break;
|
||||
case CFI_DI:
|
||||
orc->sp_reg = ORC_REG_DI;
|
||||
break;
|
||||
case CFI_DX:
|
||||
orc->sp_reg = ORC_REG_DX;
|
||||
break;
|
||||
default:
|
||||
WARN_FUNC("unknown CFA base reg %d",
|
||||
insn->sec, insn->offset, cfa->base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch(bp->base) {
|
||||
case CFI_UNDEFINED:
|
||||
orc->bp_reg = ORC_REG_UNDEFINED;
|
||||
break;
|
||||
case CFI_CFA:
|
||||
orc->bp_reg = ORC_REG_PREV_SP;
|
||||
break;
|
||||
case CFI_BP:
|
||||
orc->bp_reg = ORC_REG_BP;
|
||||
break;
|
||||
default:
|
||||
WARN_FUNC("unknown BP base reg %d",
|
||||
insn->sec, insn->offset, bp->base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
orc->sp_offset = cfa->offset;
|
||||
orc->bp_offset = bp->offset;
|
||||
orc->type = insn->state.type;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_orc_entry(struct section *u_sec, struct section *ip_relasec,
|
||||
unsigned int idx, struct section *insn_sec,
|
||||
unsigned long insn_off, struct orc_entry *o)
|
||||
{
|
||||
struct orc_entry *orc;
|
||||
struct rela *rela;
|
||||
|
||||
if (!insn_sec->sym) {
|
||||
WARN("missing symbol for section %s", insn_sec->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* populate ORC data */
|
||||
orc = (struct orc_entry *)u_sec->data->d_buf + idx;
|
||||
memcpy(orc, o, sizeof(*orc));
|
||||
|
||||
/* populate rela for ip */
|
||||
rela = malloc(sizeof(*rela));
|
||||
if (!rela) {
|
||||
perror("malloc");
|
||||
return -1;
|
||||
}
|
||||
memset(rela, 0, sizeof(*rela));
|
||||
|
||||
rela->sym = insn_sec->sym;
|
||||
rela->addend = insn_off;
|
||||
rela->type = R_X86_64_PC32;
|
||||
rela->offset = idx * sizeof(int);
|
||||
|
||||
list_add_tail(&rela->list, &ip_relasec->rela_list);
|
||||
hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int create_orc_sections(struct objtool_file *file)
|
||||
{
|
||||
struct instruction *insn, *prev_insn;
|
||||
struct section *sec, *u_sec, *ip_relasec;
|
||||
unsigned int idx;
|
||||
|
||||
struct orc_entry empty = {
|
||||
.sp_reg = ORC_REG_UNDEFINED,
|
||||
.bp_reg = ORC_REG_UNDEFINED,
|
||||
.type = ORC_TYPE_CALL,
|
||||
};
|
||||
|
||||
sec = find_section_by_name(file->elf, ".orc_unwind");
|
||||
if (sec) {
|
||||
WARN("file already has .orc_unwind section, skipping");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* count the number of needed orcs */
|
||||
idx = 0;
|
||||
for_each_sec(file, sec) {
|
||||
if (!sec->text)
|
||||
continue;
|
||||
|
||||
prev_insn = NULL;
|
||||
sec_for_each_insn(file, sec, insn) {
|
||||
if (!prev_insn ||
|
||||
memcmp(&insn->orc, &prev_insn->orc,
|
||||
sizeof(struct orc_entry))) {
|
||||
idx++;
|
||||
}
|
||||
prev_insn = insn;
|
||||
}
|
||||
|
||||
/* section terminator */
|
||||
if (prev_insn)
|
||||
idx++;
|
||||
}
|
||||
if (!idx)
|
||||
return -1;
|
||||
|
||||
|
||||
/* create .orc_unwind_ip and .rela.orc_unwind_ip sections */
|
||||
sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx);
|
||||
if (!sec)
|
||||
return -1;
|
||||
|
||||
ip_relasec = elf_create_rela_section(file->elf, sec);
|
||||
if (!ip_relasec)
|
||||
return -1;
|
||||
|
||||
/* create .orc_unwind section */
|
||||
u_sec = elf_create_section(file->elf, ".orc_unwind",
|
||||
sizeof(struct orc_entry), idx);
|
||||
|
||||
/* populate sections */
|
||||
idx = 0;
|
||||
for_each_sec(file, sec) {
|
||||
if (!sec->text)
|
||||
continue;
|
||||
|
||||
prev_insn = NULL;
|
||||
sec_for_each_insn(file, sec, insn) {
|
||||
if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc,
|
||||
sizeof(struct orc_entry))) {
|
||||
|
||||
if (create_orc_entry(u_sec, ip_relasec, idx,
|
||||
insn->sec, insn->offset,
|
||||
&insn->orc))
|
||||
return -1;
|
||||
|
||||
idx++;
|
||||
}
|
||||
prev_insn = insn;
|
||||
}
|
||||
|
||||
/* section terminator */
|
||||
if (prev_insn) {
|
||||
if (create_orc_entry(u_sec, ip_relasec, idx,
|
||||
prev_insn->sec,
|
||||
prev_insn->offset + prev_insn->len,
|
||||
&empty))
|
||||
return -1;
|
||||
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
if (elf_rebuild_rela_section(ip_relasec))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -91,16 +91,16 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry,
|
||||
alt->jump_or_nop = entry->jump_or_nop;
|
||||
|
||||
if (alt->group) {
|
||||
alt->orig_len = *(unsigned char *)(sec->data + offset +
|
||||
alt->orig_len = *(unsigned char *)(sec->data->d_buf + offset +
|
||||
entry->orig_len);
|
||||
alt->new_len = *(unsigned char *)(sec->data + offset +
|
||||
alt->new_len = *(unsigned char *)(sec->data->d_buf + offset +
|
||||
entry->new_len);
|
||||
}
|
||||
|
||||
if (entry->feature) {
|
||||
unsigned short feature;
|
||||
|
||||
feature = *(unsigned short *)(sec->data + offset +
|
||||
feature = *(unsigned short *)(sec->data->d_buf + offset +
|
||||
entry->feature);
|
||||
|
||||
/*
|
||||
|
||||
29
tools/objtool/sync-check.sh
Executable file
29
tools/objtool/sync-check.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
FILES='
|
||||
arch/x86/lib/insn.c
|
||||
arch/x86/lib/inat.c
|
||||
arch/x86/lib/x86-opcode-map.txt
|
||||
arch/x86/tools/gen-insn-attr-x86.awk
|
||||
arch/x86/include/asm/insn.h
|
||||
arch/x86/include/asm/inat.h
|
||||
arch/x86/include/asm/inat_types.h
|
||||
arch/x86/include/asm/orc_types.h
|
||||
'
|
||||
|
||||
check()
|
||||
{
|
||||
local file=$1
|
||||
|
||||
diff $file ../../$file > /dev/null ||
|
||||
echo "Warning: synced file at 'tools/objtool/$file' differs from latest kernel version at '$file'"
|
||||
}
|
||||
|
||||
if [ ! -d ../../kernel ] || [ ! -d ../../tools ] || [ ! -d ../objtool ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for i in $FILES; do
|
||||
check $i
|
||||
done
|
||||
@@ -18,6 +18,13 @@
|
||||
#ifndef _WARN_H
|
||||
#define _WARN_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "elf.h"
|
||||
|
||||
extern const char *objname;
|
||||
|
||||
static inline char *offstr(struct section *sec, unsigned long offset)
|
||||
@@ -57,4 +64,7 @@ static inline char *offstr(struct section *sec, unsigned long offset)
|
||||
free(_str); \
|
||||
})
|
||||
|
||||
#define WARN_ELF(format, ...) \
|
||||
WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1))
|
||||
|
||||
#endif /* _WARN_H */
|
||||
|
||||
@@ -51,6 +51,7 @@ tools/include/asm-generic/bitops/arch_hweight.h
|
||||
tools/include/asm-generic/bitops/atomic.h
|
||||
tools/include/asm-generic/bitops/const_hweight.h
|
||||
tools/include/asm-generic/bitops/__ffs.h
|
||||
tools/include/asm-generic/bitops/__ffz.h
|
||||
tools/include/asm-generic/bitops/__fls.h
|
||||
tools/include/asm-generic/bitops/find.h
|
||||
tools/include/asm-generic/bitops/fls64.h
|
||||
@@ -60,7 +61,9 @@ tools/include/asm-generic/bitops.h
|
||||
tools/include/linux/atomic.h
|
||||
tools/include/linux/bitops.h
|
||||
tools/include/linux/compiler.h
|
||||
tools/include/linux/compiler-gcc.h
|
||||
tools/include/linux/coresight-pmu.h
|
||||
tools/include/linux/bug.h
|
||||
tools/include/linux/filter.h
|
||||
tools/include/linux/hash.h
|
||||
tools/include/linux/kernel.h
|
||||
@@ -70,12 +73,15 @@ tools/include/uapi/asm-generic/mman-common.h
|
||||
tools/include/uapi/asm-generic/mman.h
|
||||
tools/include/uapi/linux/bpf.h
|
||||
tools/include/uapi/linux/bpf_common.h
|
||||
tools/include/uapi/linux/fcntl.h
|
||||
tools/include/uapi/linux/hw_breakpoint.h
|
||||
tools/include/uapi/linux/mman.h
|
||||
tools/include/uapi/linux/perf_event.h
|
||||
tools/include/uapi/linux/stat.h
|
||||
tools/include/linux/poison.h
|
||||
tools/include/linux/rbtree.h
|
||||
tools/include/linux/rbtree_augmented.h
|
||||
tools/include/linux/refcount.h
|
||||
tools/include/linux/string.h
|
||||
tools/include/linux/stringify.h
|
||||
tools/include/linux/types.h
|
||||
|
||||
@@ -177,6 +177,36 @@ ifeq ($(filter-out $(NON_CONFIG_TARGETS),$(MAKECMDGOALS)),)
|
||||
endif
|
||||
endif
|
||||
|
||||
# The fixdep build - we force fixdep tool to be built as
|
||||
# the first target in the separate make session not to be
|
||||
# disturbed by any parallel make jobs. Once fixdep is done
|
||||
# we issue the requested build with FIXDEP=1 variable.
|
||||
#
|
||||
# The fixdep build is disabled for $(NON_CONFIG_TARGETS)
|
||||
# targets, because it's not necessary.
|
||||
|
||||
ifdef FIXDEP
|
||||
force_fixdep := 0
|
||||
else
|
||||
force_fixdep := $(config)
|
||||
endif
|
||||
|
||||
export srctree OUTPUT RM CC CXX LD AR CFLAGS CXXFLAGS V BISON FLEX AWK
|
||||
export HOSTCC HOSTLD HOSTAR
|
||||
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
ifeq ($(force_fixdep),1)
|
||||
goals := $(filter-out all sub-make, $(MAKECMDGOALS))
|
||||
|
||||
$(goals) all: sub-make
|
||||
|
||||
sub-make: fixdep
|
||||
@./check-headers.sh
|
||||
$(Q)$(MAKE) FIXDEP=1 -f Makefile.perf $(goals)
|
||||
|
||||
else # force_fixdep
|
||||
|
||||
# Set FEATURE_TESTS to 'all' so all possible feature checkers are executed.
|
||||
# Without this setting the output feature dump file misses some features, for
|
||||
# example, liberty. Select all checkers so we won't get an incomplete feature
|
||||
@@ -348,10 +378,6 @@ strip: $(PROGRAMS) $(OUTPUT)perf
|
||||
|
||||
PERF_IN := $(OUTPUT)perf-in.o
|
||||
|
||||
export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK
|
||||
export HOSTCC HOSTLD HOSTAR
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
JEVENTS := $(OUTPUT)pmu-events/jevents
|
||||
JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o
|
||||
|
||||
@@ -362,99 +388,6 @@ export JEVENTS
|
||||
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
|
||||
|
||||
$(PERF_IN): prepare FORCE
|
||||
@(test -f ../../include/uapi/linux/perf_event.h && ( \
|
||||
(diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/uapi/linux/perf_event.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/linux/hash.h && ( \
|
||||
(diff -B ../include/linux/hash.h ../../include/linux/hash.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/linux/hash.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/uapi/linux/hw_breakpoint.h && ( \
|
||||
(diff -B ../include/uapi/linux/hw_breakpoint.h ../../include/uapi/linux/hw_breakpoint.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/uapi/linux/hw_breakpoint.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/include/asm/disabled-features.h && ( \
|
||||
(diff -B ../arch/x86/include/asm/disabled-features.h ../../arch/x86/include/asm/disabled-features.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/include/asm/disabled-features.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/include/asm/required-features.h && ( \
|
||||
(diff -B ../arch/x86/include/asm/required-features.h ../../arch/x86/include/asm/required-features.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/include/asm/required-features.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/include/asm/cpufeatures.h && ( \
|
||||
(diff -B ../arch/x86/include/asm/cpufeatures.h ../../arch/x86/include/asm/cpufeatures.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/include/asm/cpufeatures.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/lib/memcpy_64.S && ( \
|
||||
(diff -B ../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memcpy_64.S >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/lib/memcpy_64.S differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/lib/memset_64.S && ( \
|
||||
(diff -B ../arch/x86/lib/memset_64.S ../../arch/x86/lib/memset_64.S >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/lib/memset_64.S differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/arm/include/uapi/asm/perf_regs.h && ( \
|
||||
(diff -B ../arch/arm/include/uapi/asm/perf_regs.h ../../arch/arm/include/uapi/asm/perf_regs.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/arm/include/uapi/asm/perf_regs.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/arm64/include/uapi/asm/perf_regs.h && ( \
|
||||
(diff -B ../arch/arm64/include/uapi/asm/perf_regs.h ../../arch/arm64/include/uapi/asm/perf_regs.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/arm64/include/uapi/asm/perf_regs.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/powerpc/include/uapi/asm/perf_regs.h && ( \
|
||||
(diff -B ../arch/powerpc/include/uapi/asm/perf_regs.h ../../arch/powerpc/include/uapi/asm/perf_regs.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/powerpc/include/uapi/asm/perf_regs.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/include/uapi/asm/perf_regs.h && ( \
|
||||
(diff -B ../arch/x86/include/uapi/asm/perf_regs.h ../../arch/x86/include/uapi/asm/perf_regs.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/include/uapi/asm/perf_regs.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/include/uapi/asm/kvm.h && ( \
|
||||
(diff -B ../arch/x86/include/uapi/asm/kvm.h ../../arch/x86/include/uapi/asm/kvm.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/include/uapi/asm/kvm_perf.h && ( \
|
||||
(diff -B ../arch/x86/include/uapi/asm/kvm_perf.h ../../arch/x86/include/uapi/asm/kvm_perf.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/include/uapi/asm/kvm_perf.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/include/uapi/asm/svm.h && ( \
|
||||
(diff -B ../arch/x86/include/uapi/asm/svm.h ../../arch/x86/include/uapi/asm/svm.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/include/uapi/asm/svm.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/x86/include/uapi/asm/vmx.h && ( \
|
||||
(diff -B ../arch/x86/include/uapi/asm/vmx.h ../../arch/x86/include/uapi/asm/vmx.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/x86/include/uapi/asm/vmx.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/powerpc/include/uapi/asm/kvm.h && ( \
|
||||
(diff -B ../arch/powerpc/include/uapi/asm/kvm.h ../../arch/powerpc/include/uapi/asm/kvm.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/powerpc/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/s390/include/uapi/asm/kvm.h && ( \
|
||||
(diff -B ../arch/s390/include/uapi/asm/kvm.h ../../arch/s390/include/uapi/asm/kvm.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/s390/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/s390/include/uapi/asm/kvm_perf.h && ( \
|
||||
(diff -B ../arch/s390/include/uapi/asm/kvm_perf.h ../../arch/s390/include/uapi/asm/kvm_perf.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/s390/include/uapi/asm/kvm_perf.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/s390/include/uapi/asm/sie.h && ( \
|
||||
(diff -B ../arch/s390/include/uapi/asm/sie.h ../../arch/s390/include/uapi/asm/sie.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/s390/include/uapi/asm/sie.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/arm/include/uapi/asm/kvm.h && ( \
|
||||
(diff -B ../arch/arm/include/uapi/asm/kvm.h ../../arch/arm/include/uapi/asm/kvm.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/arm/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../arch/arm64/include/uapi/asm/kvm.h && ( \
|
||||
(diff -B ../arch/arm64/include/uapi/asm/kvm.h ../../arch/arm64/include/uapi/asm/kvm.h >/dev/null) \
|
||||
|| echo "Warning: tools/arch/arm64/include/uapi/asm/kvm.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/asm-generic/bitops/arch_hweight.h && ( \
|
||||
(diff -B ../include/asm-generic/bitops/arch_hweight.h ../../include/asm-generic/bitops/arch_hweight.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/asm-generic/bitops/arch_hweight.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/asm-generic/bitops/const_hweight.h && ( \
|
||||
(diff -B ../include/asm-generic/bitops/const_hweight.h ../../include/asm-generic/bitops/const_hweight.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/asm-generic/bitops/const_hweight.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/asm-generic/bitops/__fls.h && ( \
|
||||
(diff -B ../include/asm-generic/bitops/__fls.h ../../include/asm-generic/bitops/__fls.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/asm-generic/bitops/__fls.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/asm-generic/bitops/fls.h && ( \
|
||||
(diff -B ../include/asm-generic/bitops/fls.h ../../include/asm-generic/bitops/fls.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/asm-generic/bitops/fls.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/asm-generic/bitops/fls64.h && ( \
|
||||
(diff -B ../include/asm-generic/bitops/fls64.h ../../include/asm-generic/bitops/fls64.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/asm-generic/bitops/fls64.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/linux/coresight-pmu.h && ( \
|
||||
(diff -B ../include/linux/coresight-pmu.h ../../include/linux/coresight-pmu.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/linux/coresight-pmu.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/uapi/asm-generic/mman-common.h && ( \
|
||||
(diff -B ../include/uapi/asm-generic/mman-common.h ../../include/uapi/asm-generic/mman-common.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/uapi/asm-generic/mman-common.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/uapi/asm-generic/mman.h && ( \
|
||||
(diff -B -I "^#include <\(uapi/\)*asm-generic/mman-common.h>$$" ../include/uapi/asm-generic/mman.h ../../include/uapi/asm-generic/mman.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/uapi/asm-generic/mman.h differs from kernel" >&2 )) || true
|
||||
@(test -f ../../include/uapi/linux/mman.h && ( \
|
||||
(diff -B -I "^#include <\(uapi/\)*asm/mman.h>$$" ../include/uapi/linux/mman.h ../../include/uapi/linux/mman.h >/dev/null) \
|
||||
|| echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true
|
||||
$(Q)$(MAKE) $(build)=perf
|
||||
|
||||
$(JEVENTS_IN): FORCE
|
||||
@@ -470,7 +403,7 @@ $(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN) $(LIBTRACEEVENT_DYNAMIC_L
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \
|
||||
$(PERF_IN) $(PMU_EVENTS_IN) $(LIBS) -o $@
|
||||
|
||||
$(GTK_IN): fixdep FORCE
|
||||
$(GTK_IN): FORCE
|
||||
$(Q)$(MAKE) $(build)=gtk
|
||||
|
||||
$(OUTPUT)libperf-gtk.so: $(GTK_IN) $(PERFLIBS)
|
||||
@@ -515,7 +448,7 @@ endif
|
||||
__build-dir = $(subst $(OUTPUT),,$(dir $@))
|
||||
build-dir = $(if $(__build-dir),$(__build-dir),.)
|
||||
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h fixdep archheaders
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders
|
||||
|
||||
$(OUTPUT)%.o: %.c prepare FORCE
|
||||
$(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
|
||||
@@ -555,7 +488,7 @@ $(patsubst perf-%,%.o,$(PROGRAMS)): $(wildcard */*.h)
|
||||
|
||||
LIBPERF_IN := $(OUTPUT)libperf-in.o
|
||||
|
||||
$(LIBPERF_IN): prepare fixdep FORCE
|
||||
$(LIBPERF_IN): prepare FORCE
|
||||
$(Q)$(MAKE) $(build)=libperf
|
||||
|
||||
$(LIB_FILE): $(LIBPERF_IN)
|
||||
@@ -563,10 +496,10 @@ $(LIB_FILE): $(LIBPERF_IN)
|
||||
|
||||
LIBTRACEEVENT_FLAGS += plugin_dir=$(plugindir_SQ)
|
||||
|
||||
$(LIBTRACEEVENT): fixdep FORCE
|
||||
$(LIBTRACEEVENT): FORCE
|
||||
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)libtraceevent.a
|
||||
|
||||
libtraceevent_plugins: fixdep FORCE
|
||||
libtraceevent_plugins: FORCE
|
||||
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) plugins
|
||||
|
||||
$(LIBTRACEEVENT_DYNAMIC_LIST): libtraceevent_plugins
|
||||
@@ -579,21 +512,21 @@ $(LIBTRACEEVENT)-clean:
|
||||
install-traceevent-plugins: libtraceevent_plugins
|
||||
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) install_plugins
|
||||
|
||||
$(LIBAPI): fixdep FORCE
|
||||
$(LIBAPI): FORCE
|
||||
$(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) $(OUTPUT)libapi.a
|
||||
|
||||
$(LIBAPI)-clean:
|
||||
$(call QUIET_CLEAN, libapi)
|
||||
$(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null
|
||||
|
||||
$(LIBBPF): fixdep FORCE
|
||||
$(LIBBPF): FORCE
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT)
|
||||
|
||||
$(LIBBPF)-clean:
|
||||
$(call QUIET_CLEAN, libbpf)
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) clean >/dev/null
|
||||
|
||||
$(LIBSUBCMD): fixdep FORCE
|
||||
$(LIBSUBCMD): FORCE
|
||||
$(Q)$(MAKE) -C $(SUBCMD_DIR) O=$(OUTPUT) $(OUTPUT)libsubcmd.a
|
||||
|
||||
$(LIBSUBCMD)-clean:
|
||||
@@ -790,3 +723,4 @@ FORCE:
|
||||
.PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope FORCE prepare
|
||||
.PHONY: libtraceevent_plugins archheaders
|
||||
|
||||
endif # force_fixdep
|
||||
|
||||
@@ -335,6 +335,9 @@
|
||||
326 common copy_file_range sys_copy_file_range
|
||||
327 64 preadv2 sys_preadv2
|
||||
328 64 pwritev2 sys_pwritev2
|
||||
329 common pkey_mprotect sys_pkey_mprotect
|
||||
330 common pkey_alloc sys_pkey_alloc
|
||||
331 common pkey_free sys_pkey_free
|
||||
|
||||
#
|
||||
# x32-specific system call numbers start at 512 to avoid cache impact
|
||||
@@ -374,5 +377,5 @@
|
||||
543 x32 io_setup compat_sys_io_setup
|
||||
544 x32 io_submit compat_sys_io_submit
|
||||
545 x32 execveat compat_sys_execveat/ptregs
|
||||
534 x32 preadv2 compat_sys_preadv2
|
||||
535 x32 pwritev2 compat_sys_pwritev2
|
||||
546 x32 preadv2 compat_sys_preadv64v2
|
||||
547 x32 pwritev2 compat_sys_pwritev64v2
|
||||
|
||||
61
tools/perf/check-headers.sh
Executable file
61
tools/perf/check-headers.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/bin/sh
|
||||
|
||||
HEADERS='
|
||||
include/uapi/linux/fcntl.h
|
||||
include/uapi/linux/perf_event.h
|
||||
include/uapi/linux/stat.h
|
||||
include/linux/hash.h
|
||||
include/uapi/linux/hw_breakpoint.h
|
||||
arch/x86/include/asm/disabled-features.h
|
||||
arch/x86/include/asm/required-features.h
|
||||
arch/x86/include/asm/cpufeatures.h
|
||||
arch/arm/include/uapi/asm/perf_regs.h
|
||||
arch/arm64/include/uapi/asm/perf_regs.h
|
||||
arch/powerpc/include/uapi/asm/perf_regs.h
|
||||
arch/x86/include/uapi/asm/perf_regs.h
|
||||
arch/x86/include/uapi/asm/kvm.h
|
||||
arch/x86/include/uapi/asm/kvm_perf.h
|
||||
arch/x86/include/uapi/asm/svm.h
|
||||
arch/x86/include/uapi/asm/vmx.h
|
||||
arch/powerpc/include/uapi/asm/kvm.h
|
||||
arch/s390/include/uapi/asm/kvm.h
|
||||
arch/s390/include/uapi/asm/kvm_perf.h
|
||||
arch/s390/include/uapi/asm/sie.h
|
||||
arch/arm/include/uapi/asm/kvm.h
|
||||
arch/arm64/include/uapi/asm/kvm.h
|
||||
include/asm-generic/bitops/arch_hweight.h
|
||||
include/asm-generic/bitops/const_hweight.h
|
||||
include/asm-generic/bitops/__fls.h
|
||||
include/asm-generic/bitops/fls.h
|
||||
include/asm-generic/bitops/fls64.h
|
||||
include/linux/coresight-pmu.h
|
||||
include/uapi/asm-generic/mman-common.h
|
||||
'
|
||||
|
||||
check () {
|
||||
file=$1
|
||||
opts=
|
||||
|
||||
shift
|
||||
while [ -n "$*" ]; do
|
||||
opts="$opts \"$1\""
|
||||
shift
|
||||
done
|
||||
|
||||
cmd="diff $opts ../$file ../../$file > /dev/null"
|
||||
|
||||
test -f ../../$file &&
|
||||
eval $cmd || echo "Warning: $file differs from kernel" >&2
|
||||
}
|
||||
|
||||
|
||||
# simple diff check
|
||||
for i in $HEADERS; do
|
||||
check $i -B
|
||||
done
|
||||
|
||||
# diff with extra ignore lines
|
||||
check arch/x86/lib/memcpy_64.S -B -I "^EXPORT_SYMBOL" -I "^#include <asm/export.h>"
|
||||
check arch/x86/lib/memset_64.S -B -I "^EXPORT_SYMBOL" -I "^#include <asm/export.h>"
|
||||
check include/uapi/asm-generic/mman.h -B -I "^#include <\(uapi/\)*asm-generic/mman-common.h>"
|
||||
check include/uapi/linux/mman.h -B -I "^#include <\(uapi/\)*asm/mman.h>"
|
||||
@@ -23,8 +23,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define TYPEOF(x) (__typeof__(x))
|
||||
#else
|
||||
|
||||
Reference in New Issue
Block a user