Commit 6f612579 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'objtool-core-2023-06-27' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull objtool updates from Ingo Molar:
 "Build footprint & performance improvements:

   - Reduce memory usage with CONFIG_DEBUG_INFO=y

     In the worst case of an allyesconfig+CONFIG_DEBUG_INFO=y kernel,
     DWARF creates almost 200 million relocations, ballooning objtool's
     peak heap usage to 53GB. These patches reduce that to 25GB.

     On a distro-type kernel with kernel IBT enabled, they reduce
     objtool's peak heap usage from 4.2GB to 2.8GB.

     These changes also improve the runtime significantly.

  Debuggability improvements:

   - Add the unwind_debug command-line option, for more extend unwinding
     debugging output
   - Limit unreachable warnings to once per function
   - Add verbose option for disassembling affected functions
   - Include backtrace in verbose mode
   - Detect missing __noreturn annotations
   - Ignore exc_double_fault() __noreturn warnings
   - Remove superfluous global_noreturns entries
   - Move noreturn function list to separate file
   - Add __kunit_abort() to noreturns

  Unwinder improvements:

   - Allow stack operations in UNWIND_HINT_UNDEFINED regions
   - drm/vmwgfx: Add unwind hints around RBP clobber

  Cleanups:

   - Move the x86 entry thunk restore code into thunk functions
   - x86/unwind/orc: Use swap() instead of open coding it
   - Remove unnecessary/unused variables

  Fixes for modern stack canary handling"

* tag 'objtool-core-2023-06-27' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (42 commits)
  x86/orc: Make the is_callthunk() definition depend on CONFIG_BPF_JIT=y
  objtool: Skip reading DWARF section data
  objtool: Free insns when done
  objtool: Get rid of reloc->rel[a]
  objtool: Shrink elf hash nodes
  objtool: Shrink reloc->sym_reloc_entry
  objtool: Get rid of reloc->jump_table_start
  objtool: Get rid of reloc->addend
  objtool: Get rid of reloc->type
  objtool: Get rid of reloc->offset
  objtool: Get rid of reloc->idx
  objtool: Get rid of reloc->list
  objtool: Allocate relocs in advance for new rela sections
  objtool: Add for_each_reloc()
  objtool: Don't free memory in elf_close()
  objtool: Keep GElf_Rel[a] structs synced
  objtool: Add elf_create_section_pair()
  objtool: Add mark_sec_changed()
  objtool: Fix reloc_hash size
  objtool: Consolidate rel/rela handling
  ...
parents 4d675181 301cf77e
...@@ -6598,6 +6598,12 @@ ...@@ -6598,6 +6598,12 @@
unknown_nmi_panic unknown_nmi_panic
[X86] Cause panic on unknown NMI. [X86] Cause panic on unknown NMI.
unwind_debug [X86-64]
Enable unwinder debug output. This can be
useful for debugging certain unwinder error
conditions, including corrupt stacks and
bad/missing unwinder metadata.
usbcore.authorized_default= usbcore.authorized_default=
[USB] Default USB device authorization: [USB] Default USB device authorization:
(default -1 = authorized except for wireless USB, (default -1 = authorized except for wireless USB,
......
...@@ -1605,6 +1605,7 @@ static void add_cpu_to_masks(int cpu) ...@@ -1605,6 +1605,7 @@ static void add_cpu_to_masks(int cpu)
} }
/* Activate a secondary processor. */ /* Activate a secondary processor. */
__no_stack_protector
void start_secondary(void *unused) void start_secondary(void *unused)
{ {
unsigned int cpu = raw_smp_processor_id(); unsigned int cpu = raw_smp_processor_id();
......
...@@ -26,17 +26,7 @@ SYM_FUNC_START(\name) ...@@ -26,17 +26,7 @@ SYM_FUNC_START(\name)
pushq %r11 pushq %r11
call \func call \func
jmp __thunk_restore
SYM_FUNC_END(\name)
_ASM_NOKPROBE(\name)
.endm
THUNK preempt_schedule_thunk, preempt_schedule
THUNK preempt_schedule_notrace_thunk, preempt_schedule_notrace
EXPORT_SYMBOL(preempt_schedule_thunk)
EXPORT_SYMBOL(preempt_schedule_notrace_thunk)
SYM_CODE_START_LOCAL(__thunk_restore)
popq %r11 popq %r11
popq %r10 popq %r10
popq %r9 popq %r9
...@@ -48,5 +38,11 @@ SYM_CODE_START_LOCAL(__thunk_restore) ...@@ -48,5 +38,11 @@ SYM_CODE_START_LOCAL(__thunk_restore)
popq %rdi popq %rdi
popq %rbp popq %rbp
RET RET
_ASM_NOKPROBE(__thunk_restore) SYM_FUNC_END(\name)
SYM_CODE_END(__thunk_restore) _ASM_NOKPROBE(\name)
.endm
THUNK preempt_schedule_thunk, preempt_schedule
THUNK preempt_schedule_notrace_thunk, preempt_schedule_notrace
EXPORT_SYMBOL(preempt_schedule_thunk)
EXPORT_SYMBOL(preempt_schedule_notrace_thunk)
...@@ -113,7 +113,6 @@ extern void callthunks_patch_builtin_calls(void); ...@@ -113,7 +113,6 @@ extern void callthunks_patch_builtin_calls(void);
extern void callthunks_patch_module_calls(struct callthunk_sites *sites, extern void callthunks_patch_module_calls(struct callthunk_sites *sites,
struct module *mod); struct module *mod);
extern void *callthunks_translate_call_dest(void *dest); extern void *callthunks_translate_call_dest(void *dest);
extern bool is_callthunk(void *addr);
extern int x86_call_depth_emit_accounting(u8 **pprog, void *func); extern int x86_call_depth_emit_accounting(u8 **pprog, void *func);
#else #else
static __always_inline void callthunks_patch_builtin_calls(void) {} static __always_inline void callthunks_patch_builtin_calls(void) {}
...@@ -124,10 +123,6 @@ static __always_inline void *callthunks_translate_call_dest(void *dest) ...@@ -124,10 +123,6 @@ static __always_inline void *callthunks_translate_call_dest(void *dest)
{ {
return dest; return dest;
} }
static __always_inline bool is_callthunk(void *addr)
{
return false;
}
static __always_inline int x86_call_depth_emit_accounting(u8 **pprog, static __always_inline int x86_call_depth_emit_accounting(u8 **pprog,
void *func) void *func)
{ {
......
...@@ -76,9 +76,18 @@ ...@@ -76,9 +76,18 @@
#else #else
#define UNWIND_HINT_UNDEFINED \
UNWIND_HINT(UNWIND_HINT_TYPE_UNDEFINED, 0, 0, 0)
#define UNWIND_HINT_FUNC \ #define UNWIND_HINT_FUNC \
UNWIND_HINT(UNWIND_HINT_TYPE_FUNC, ORC_REG_SP, 8, 0) UNWIND_HINT(UNWIND_HINT_TYPE_FUNC, ORC_REG_SP, 8, 0)
#define UNWIND_HINT_SAVE \
UNWIND_HINT(UNWIND_HINT_TYPE_SAVE, 0, 0, 0)
#define UNWIND_HINT_RESTORE \
UNWIND_HINT(UNWIND_HINT_TYPE_RESTORE, 0, 0, 0)
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* _ASM_X86_UNWIND_HINTS_H */ #endif /* _ASM_X86_UNWIND_HINTS_H */
...@@ -293,7 +293,8 @@ void *callthunks_translate_call_dest(void *dest) ...@@ -293,7 +293,8 @@ void *callthunks_translate_call_dest(void *dest)
return target ? : dest; return target ? : dest;
} }
bool is_callthunk(void *addr) #ifdef CONFIG_BPF_JIT
static bool is_callthunk(void *addr)
{ {
unsigned int tmpl_size = SKL_TMPL_SIZE; unsigned int tmpl_size = SKL_TMPL_SIZE;
void *tmpl = skl_call_thunk_template; void *tmpl = skl_call_thunk_template;
...@@ -306,7 +307,6 @@ bool is_callthunk(void *addr) ...@@ -306,7 +307,6 @@ bool is_callthunk(void *addr)
return !bcmp((void *)(dest - tmpl_size), tmpl, tmpl_size); return !bcmp((void *)(dest - tmpl_size), tmpl, tmpl_size);
} }
#ifdef CONFIG_BPF_JIT
int x86_call_depth_emit_accounting(u8 **pprog, void *func) int x86_call_depth_emit_accounting(u8 **pprog, void *func)
{ {
unsigned int tmpl_size = SKL_TMPL_SIZE; unsigned int tmpl_size = SKL_TMPL_SIZE;
......
...@@ -16,8 +16,14 @@ ORC_HEADER; ...@@ -16,8 +16,14 @@ ORC_HEADER;
#define orc_warn_current(args...) \ #define orc_warn_current(args...) \
({ \ ({ \
if (state->task == current && !state->error) \ static bool dumped_before; \
if (state->task == current && !state->error) { \
orc_warn(args); \ orc_warn(args); \
if (unwind_debug && !dumped_before) { \
dumped_before = true; \
unwind_dump(state); \
} \
} \
}) })
extern int __start_orc_unwind_ip[]; extern int __start_orc_unwind_ip[];
...@@ -26,8 +32,49 @@ extern struct orc_entry __start_orc_unwind[]; ...@@ -26,8 +32,49 @@ extern struct orc_entry __start_orc_unwind[];
extern struct orc_entry __stop_orc_unwind[]; extern struct orc_entry __stop_orc_unwind[];
static bool orc_init __ro_after_init; static bool orc_init __ro_after_init;
static bool unwind_debug __ro_after_init;
static unsigned int lookup_num_blocks __ro_after_init; static unsigned int lookup_num_blocks __ro_after_init;
static int __init unwind_debug_cmdline(char *str)
{
unwind_debug = true;
return 0;
}
early_param("unwind_debug", unwind_debug_cmdline);
static void unwind_dump(struct unwind_state *state)
{
static bool dumped_before;
unsigned long word, *sp;
struct stack_info stack_info = {0};
unsigned long visit_mask = 0;
if (dumped_before)
return;
dumped_before = true;
printk_deferred("unwind stack type:%d next_sp:%p mask:0x%lx graph_idx:%d\n",
state->stack_info.type, state->stack_info.next_sp,
state->stack_mask, state->graph_idx);
for (sp = __builtin_frame_address(0); sp;
sp = PTR_ALIGN(stack_info.next_sp, sizeof(long))) {
if (get_stack_info(sp, state->task, &stack_info, &visit_mask))
break;
for (; sp < stack_info.end; sp++) {
word = READ_ONCE_NOCHECK(*sp);
printk_deferred("%0*lx: %0*lx (%pB)\n", BITS_PER_LONG/4,
(unsigned long)sp, BITS_PER_LONG/4,
word, (void *)word);
}
}
}
static inline unsigned long orc_ip(const int *ip) static inline unsigned long orc_ip(const int *ip)
{ {
return (unsigned long)ip + *ip; return (unsigned long)ip + *ip;
...@@ -139,21 +186,6 @@ static struct orc_entry null_orc_entry = { ...@@ -139,21 +186,6 @@ static struct orc_entry null_orc_entry = {
.type = ORC_TYPE_CALL .type = ORC_TYPE_CALL
}; };
#ifdef CONFIG_CALL_THUNKS
static struct orc_entry *orc_callthunk_find(unsigned long ip)
{
if (!is_callthunk((void *)ip))
return NULL;
return &null_orc_entry;
}
#else
static struct orc_entry *orc_callthunk_find(unsigned long ip)
{
return NULL;
}
#endif
/* Fake frame pointer entry -- used as a fallback for generated code */ /* Fake frame pointer entry -- used as a fallback for generated code */
static struct orc_entry orc_fp_entry = { static struct orc_entry orc_fp_entry = {
.type = ORC_TYPE_CALL, .type = ORC_TYPE_CALL,
...@@ -206,11 +238,7 @@ static struct orc_entry *orc_find(unsigned long ip) ...@@ -206,11 +238,7 @@ static struct orc_entry *orc_find(unsigned long ip)
if (orc) if (orc)
return orc; return orc;
orc = orc_ftrace_find(ip); return orc_ftrace_find(ip);
if (orc)
return orc;
return orc_callthunk_find(ip);
} }
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
...@@ -222,7 +250,6 @@ static struct orc_entry *cur_orc_table = __start_orc_unwind; ...@@ -222,7 +250,6 @@ static struct orc_entry *cur_orc_table = __start_orc_unwind;
static void orc_sort_swap(void *_a, void *_b, int size) static void orc_sort_swap(void *_a, void *_b, int size)
{ {
struct orc_entry *orc_a, *orc_b; struct orc_entry *orc_a, *orc_b;
struct orc_entry orc_tmp;
int *a = _a, *b = _b, tmp; int *a = _a, *b = _b, tmp;
int delta = _b - _a; int delta = _b - _a;
...@@ -234,9 +261,7 @@ static void orc_sort_swap(void *_a, void *_b, int size) ...@@ -234,9 +261,7 @@ static void orc_sort_swap(void *_a, void *_b, int size)
/* Swap the corresponding .orc_unwind entries: */ /* Swap the corresponding .orc_unwind entries: */
orc_a = cur_orc_table + (a - cur_orc_ip_table); orc_a = cur_orc_table + (a - cur_orc_ip_table);
orc_b = cur_orc_table + (b - cur_orc_ip_table); orc_b = cur_orc_table + (b - cur_orc_ip_table);
orc_tmp = *orc_a; swap(*orc_a, *orc_b);
*orc_a = *orc_b;
*orc_b = orc_tmp;
} }
static int orc_sort_cmp(const void *_a, const void *_b) static int orc_sort_cmp(const void *_a, const void *_b)
......
...@@ -105,10 +105,14 @@ ...@@ -105,10 +105,14 @@
flags, magic, bp, \ flags, magic, bp, \
eax, ebx, ecx, edx, si, di) \ eax, ebx, ecx, edx, si, di) \
({ \ ({ \
asm volatile ("push %%rbp;" \ asm volatile ( \
UNWIND_HINT_SAVE \
"push %%rbp;" \
UNWIND_HINT_UNDEFINED \
"mov %12, %%rbp;" \ "mov %12, %%rbp;" \
VMWARE_HYPERCALL_HB_OUT \ VMWARE_HYPERCALL_HB_OUT \
"pop %%rbp;" : \ "pop %%rbp;" \
UNWIND_HINT_RESTORE : \
"=a"(eax), \ "=a"(eax), \
"=b"(ebx), \ "=b"(ebx), \
"=c"(ecx), \ "=c"(ecx), \
...@@ -130,10 +134,14 @@ ...@@ -130,10 +134,14 @@
flags, magic, bp, \ flags, magic, bp, \
eax, ebx, ecx, edx, si, di) \ eax, ebx, ecx, edx, si, di) \
({ \ ({ \
asm volatile ("push %%rbp;" \ asm volatile ( \
UNWIND_HINT_SAVE \
"push %%rbp;" \
UNWIND_HINT_UNDEFINED \
"mov %12, %%rbp;" \ "mov %12, %%rbp;" \
VMWARE_HYPERCALL_HB_IN \ VMWARE_HYPERCALL_HB_IN \
"pop %%rbp" : \ "pop %%rbp;" \
UNWIND_HINT_RESTORE : \
"=a"(eax), \ "=a"(eax), \
"=b"(ebx), \ "=b"(ebx), \
"=c"(ecx), \ "=c"(ecx), \
......
...@@ -487,6 +487,7 @@ static void lkdtm_UNSET_SMEP(void) ...@@ -487,6 +487,7 @@ static void lkdtm_UNSET_SMEP(void)
* the cr4 writing instruction. * the cr4 writing instruction.
*/ */
insn = (unsigned char *)native_write_cr4; insn = (unsigned char *)native_write_cr4;
OPTIMIZER_HIDE_VAR(insn);
for (i = 0; i < MOV_CR4_DEPTH; i++) { for (i = 0; i < MOV_CR4_DEPTH; i++) {
/* mov %rdi, %cr4 */ /* mov %rdi, %cr4 */
if (insn[i] == 0x0f && insn[i+1] == 0x22 && insn[i+2] == 0xe7) if (insn[i] == 0x0f && insn[i+1] == 0x22 && insn[i+2] == 0xe7)
......
...@@ -255,6 +255,18 @@ ...@@ -255,6 +255,18 @@
*/ */
#define __noreturn __attribute__((__noreturn__)) #define __noreturn __attribute__((__noreturn__))
/*
* Optional: only supported since GCC >= 11.1, clang >= 7.0.
*
* gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-no_005fstack_005fprotector-function-attribute
* clang: https://clang.llvm.org/docs/AttributeReference.html#no-stack-protector-safebuffers
*/
#if __has_attribute(__no_stack_protector__)
# define __no_stack_protector __attribute__((__no_stack_protector__))
#else
# define __no_stack_protector
#endif
/* /*
* Optional: not supported by gcc. * Optional: not supported by gcc.
* *
......
...@@ -873,7 +873,8 @@ static void __init print_unknown_bootoptions(void) ...@@ -873,7 +873,8 @@ static void __init print_unknown_bootoptions(void)
memblock_free(unknown_options, len); memblock_free(unknown_options, len);
} }
asmlinkage __visible void __init __no_sanitize_address __noreturn start_kernel(void) asmlinkage __visible __init __no_sanitize_address __noreturn __no_stack_protector
void start_kernel(void)
{ {
char *command_line; char *command_line;
char *after_dashes; char *after_dashes;
...@@ -1073,7 +1074,13 @@ asmlinkage __visible void __init __no_sanitize_address __noreturn start_kernel(v ...@@ -1073,7 +1074,13 @@ asmlinkage __visible void __init __no_sanitize_address __noreturn start_kernel(v
/* Do the rest non-__init'ed, we're now alive */ /* Do the rest non-__init'ed, we're now alive */
arch_call_rest_init(); arch_call_rest_init();
/*
* Avoid stack canaries in callers of boot_init_stack_canary for gcc-10
* and older.
*/
#if !__has_attribute(__no_stack_protector__)
prevent_tail_call_optimization(); prevent_tail_call_optimization();
#endif
} }
/* Call all constructor functions linked into the kernel. */ /* Call all constructor functions linked into the kernel. */
......
...@@ -6,10 +6,6 @@ ...@@ -6,10 +6,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#ifndef NORETURN
#define NORETURN __attribute__((__noreturn__))
#endif
enum parse_opt_type { enum parse_opt_type {
/* special types */ /* special types */
OPTION_END, OPTION_END,
...@@ -183,9 +179,9 @@ extern int parse_options_subcommand(int argc, const char **argv, ...@@ -183,9 +179,9 @@ extern int parse_options_subcommand(int argc, const char **argv,
const char *const subcommands[], const char *const subcommands[],
const char *usagestr[], int flags); const char *usagestr[], int flags);
extern NORETURN void usage_with_options(const char * const *usagestr, extern __noreturn void usage_with_options(const char * const *usagestr,
const struct option *options); const struct option *options);
extern NORETURN __attribute__((format(printf,3,4))) extern __noreturn __attribute__((format(printf,3,4)))
void usage_with_options_msg(const char * const *usagestr, void usage_with_options_msg(const char * const *usagestr,
const struct option *options, const struct option *options,
const char *fmt, ...); const char *fmt, ...);
......
...@@ -5,8 +5,7 @@ ...@@ -5,8 +5,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <linux/compiler.h>
#define NORETURN __attribute__((__noreturn__))
static inline void report(const char *prefix, const char *err, va_list params) static inline void report(const char *prefix, const char *err, va_list params)
{ {
...@@ -15,7 +14,7 @@ static inline void report(const char *prefix, const char *err, va_list params) ...@@ -15,7 +14,7 @@ static inline void report(const char *prefix, const char *err, va_list params)
fprintf(stderr, " %s%s\n", prefix, msg); fprintf(stderr, " %s%s\n", prefix, msg);
} }
static NORETURN inline void die(const char *err, ...) static __noreturn inline void die(const char *err, ...)
{ {
va_list params; va_list params;
......
...@@ -244,6 +244,11 @@ To achieve the validation, objtool enforces the following rules: ...@@ -244,6 +244,11 @@ To achieve the validation, objtool enforces the following rules:
Objtool warnings Objtool warnings
---------------- ----------------
NOTE: When requesting help with an objtool warning, please recreate with
OBJTOOL_VERBOSE=1 (e.g., "make OBJTOOL_VERBOSE=1") and send the full
output, including any disassembly or backtrace below the warning, to the
objtool maintainers.
For asm files, if you're getting an error which doesn't make sense, 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. first make sure that the affected code follows the above rules.
...@@ -298,6 +303,11 @@ the objtool maintainers. ...@@ -298,6 +303,11 @@ the objtool maintainers.
If it's not actually in a callable function (e.g. kernel entry code), If it's not actually in a callable function (e.g. kernel entry code),
change ENDPROC to END. change ENDPROC to END.
3. file.o: warning: objtool: foo+0x48c: bar() is missing a __noreturn annotation
The call from foo() to bar() doesn't return, but bar() is missing the
__noreturn annotation. NOTE: In addition to annotating the function
with __noreturn, please also add it to tools/objtool/noreturns.h.
4. file.o: warning: objtool: func(): can't find starting instruction 4. file.o: warning: objtool: func(): can't find starting instruction
or or
......
/* SPDX-License-Identifier: GPL-2.0-or-later */ /* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _OBJTOOL_ARCH_ELF #ifndef _OBJTOOL_ARCH_ELF
#define _OBJTOOL_ARCH_ELF #define _OBJTOOL_ARCH_ELF
#define R_NONE R_PPC_NONE #define R_NONE R_PPC_NONE
#define R_ABS64 R_PPC64_ADDR64 #define R_ABS64 R_PPC64_ADDR64
#define R_ABS32 R_PPC_ADDR32 #define R_ABS32 R_PPC_ADDR32
#define R_DATA32 R_PPC_REL32
#define R_DATA64 R_PPC64_REL64
#define R_TEXT32 R_PPC_REL32
#define R_TEXT64 R_PPC64_REL32
#endif /* _OBJTOOL_ARCH_ELF */ #endif /* _OBJTOOL_ARCH_ELF */
...@@ -84,7 +84,7 @@ bool arch_pc_relative_reloc(struct reloc *reloc) ...@@ -84,7 +84,7 @@ bool arch_pc_relative_reloc(struct reloc *reloc)
* All relocation types where P (the address of the target) * All relocation types where P (the address of the target)
* is included in the computation. * is included in the computation.
*/ */
switch (reloc->type) { switch (reloc_type(reloc)) {
case R_X86_64_PC8: case R_X86_64_PC8:
case R_X86_64_PC16: case R_X86_64_PC16:
case R_X86_64_PC32: case R_X86_64_PC32:
...@@ -623,11 +623,11 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec ...@@ -623,11 +623,11 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (!immr || strcmp(immr->sym->name, "pv_ops")) if (!immr || strcmp(immr->sym->name, "pv_ops"))
break; break;
idx = (immr->addend + 8) / sizeof(void *); idx = (reloc_addend(immr) + 8) / sizeof(void *);
func = disp->sym; func = disp->sym;
if (disp->sym->type == STT_SECTION) if (disp->sym->type == STT_SECTION)
func = find_symbol_by_offset(disp->sym->sec, disp->addend); func = find_symbol_by_offset(disp->sym->sec, reloc_addend(disp));
if (!func) { if (!func) {
WARN("no func for pv_ops[]"); WARN("no func for pv_ops[]");
return -1; return -1;
......
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _OBJTOOL_ARCH_ELF #ifndef _OBJTOOL_ARCH_ELF
#define _OBJTOOL_ARCH_ELF #define _OBJTOOL_ARCH_ELF
#define R_NONE R_X86_64_NONE #define R_NONE R_X86_64_NONE
#define R_ABS64 R_X86_64_64
#define R_ABS32 R_X86_64_32 #define R_ABS32 R_X86_64_32
#define R_ABS64 R_X86_64_64
#define R_DATA32 R_X86_64_PC32
#define R_DATA64 R_X86_64_PC32
#define R_TEXT32 R_X86_64_PC32
#define R_TEXT64 R_X86_64_PC32
#endif /* _OBJTOOL_ARCH_ELF */ #endif /* _OBJTOOL_ARCH_ELF */
...@@ -99,10 +99,10 @@ struct reloc *arch_find_switch_table(struct objtool_file *file, ...@@ -99,10 +99,10 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
!text_reloc->sym->sec->rodata) !text_reloc->sym->sec->rodata)
return NULL; return NULL;
table_offset = text_reloc->addend; table_offset = reloc_addend(text_reloc);
table_sec = text_reloc->sym->sec; table_sec = text_reloc->sym->sec;
if (text_reloc->type == R_X86_64_PC32) if (reloc_type(text_reloc) == R_X86_64_PC32)
table_offset += 4; table_offset += 4;
/* /*
...@@ -132,7 +132,7 @@ struct reloc *arch_find_switch_table(struct objtool_file *file, ...@@ -132,7 +132,7 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
* indicates a rare GCC quirk/bug which can leave dead * indicates a rare GCC quirk/bug which can leave dead
* code behind. * code behind.
*/ */
if (text_reloc->type == R_X86_64_PC32) if (reloc_type(text_reloc) == R_X86_64_PC32)
file->ignore_unreachables = true; file->ignore_unreachables = true;
return rodata_reloc; return rodata_reloc;
......
...@@ -93,6 +93,7 @@ static const struct option check_options[] = { ...@@ -93,6 +93,7 @@ static const struct option check_options[] = {
OPT_BOOLEAN(0, "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"), OPT_BOOLEAN(0, "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"),
OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"), OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"),
OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"), OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"),
OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"),
OPT_END(), OPT_END(),
}; };
...@@ -118,6 +119,10 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]) ...@@ -118,6 +119,10 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[])
parse_options(envc, envv, check_options, env_usage, 0); parse_options(envc, envv, check_options, env_usage, 0);
} }
env = getenv("OBJTOOL_VERBOSE");
if (env && !strcmp(env, "1"))
opts.verbose = true;
argc = parse_options(argc, argv, check_options, usage, 0); argc = parse_options(argc, argv, check_options, usage, 0);
if (argc != 1) if (argc != 1)
usage_with_options(usage, check_options); usage_with_options(usage, check_options);
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include <inttypes.h> #include <inttypes.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <arch/elf.h>
#include <objtool/builtin.h> #include <objtool/builtin.h>
#include <objtool/cfi.h> #include <objtool/cfi.h>
#include <objtool/arch.h> #include <objtool/arch.h>
...@@ -33,6 +32,7 @@ static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache; ...@@ -33,6 +32,7 @@ static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache;
static struct cfi_init_state initial_func_cfi; static struct cfi_init_state initial_func_cfi;
static struct cfi_state init_cfi; static struct cfi_state init_cfi;
static struct cfi_state func_cfi; static struct cfi_state func_cfi;
static struct cfi_state force_undefined_cfi;
struct instruction *find_insn(struct objtool_file *file, struct instruction *find_insn(struct objtool_file *file,
struct section *sec, unsigned long offset) struct section *sec, unsigned long offset)
...@@ -192,51 +192,11 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func, ...@@ -192,51 +192,11 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
struct instruction *insn; struct instruction *insn;
bool empty = true; bool empty = true;
/* #define NORETURN(func) __stringify(func),
* Unfortunately these have to be hard coded because the noreturn
* attribute isn't provided in ELF data. Keep 'em sorted.
*/
static const char * const global_noreturns[] = { static const char * const global_noreturns[] = {
"__invalid_creds", #include "noreturns.h"
"__kunit_abort",
"__module_put_and_kthread_exit",
"__reiserfs_panic",
"__stack_chk_fail",
"__ubsan_handle_builtin_unreachable",
"arch_call_rest_init",
"arch_cpu_idle_dead",
"cpu_bringup_and_idle",
"cpu_startup_entry",
"do_exit",
"do_group_exit",
"do_task_dead",
"ex_handler_msr_mce",
"fortify_panic",
"hlt_play_dead",
"hv_ghcb_terminate",
"kthread_complete_and_exit",
"kthread_exit",
"kunit_try_catch_throw",
"lbug_with_loc",
"machine_real_restart",
"make_task_dead",
"mpt_halt_firmware",
"nmi_panic_self_stop",
"panic",
"panic_smp_self_stop",
"rest_init",
"resume_play_dead",
"rewind_stack_and_make_dead",
"sev_es_terminate",
"snp_abort",
"start_kernel",
"stop_this_cpu",
"usercopy_abort",
"x86_64_start_kernel",
"x86_64_start_reservations",
"xen_cpu_bringup_again",
"xen_start_kernel",
}; };
#undef NORETURN
if (!func) if (!func)
return false; return false;
...@@ -533,7 +493,7 @@ static int add_pv_ops(struct objtool_file *file, const char *symname) ...@@ -533,7 +493,7 @@ static int add_pv_ops(struct objtool_file *file, const char *symname)
{ {
struct symbol *sym, *func; struct symbol *sym, *func;
unsigned long off, end; unsigned long off, end;
struct reloc *rel; struct reloc *reloc;
int idx; int idx;
sym = find_symbol_by_name(file->elf, symname); sym = find_symbol_by_name(file->elf, symname);
...@@ -543,19 +503,20 @@ static int add_pv_ops(struct objtool_file *file, const char *symname) ...@@ -543,19 +503,20 @@ static int add_pv_ops(struct objtool_file *file, const char *symname)
off = sym->offset; off = sym->offset;
end = off + sym->len; end = off + sym->len;
for (;;) { for (;;) {
rel = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off); reloc = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off);
if (!rel) if (!reloc)
break; break;
func = rel->sym; func = reloc->sym;
if (func->type == STT_SECTION) if (func->type == STT_SECTION)
func = find_symbol_by_offset(rel->sym->sec, rel->addend); func = find_symbol_by_offset(reloc->sym->sec,
reloc_addend(reloc));
idx = (rel->offset - sym->offset) / sizeof(unsigned long); idx = (reloc_offset(reloc) - sym->offset) / sizeof(unsigned long);
objtool_pv_add(file, idx, func); objtool_pv_add(file, idx, func);
off = rel->offset + 1; off = reloc_offset(reloc) + 1;
if (off > end) if (off > end)
break; break;
} }
...@@ -620,35 +581,40 @@ static struct instruction *find_last_insn(struct objtool_file *file, ...@@ -620,35 +581,40 @@ static struct instruction *find_last_insn(struct objtool_file *file,
*/ */
static int add_dead_ends(struct objtool_file *file) static int add_dead_ends(struct objtool_file *file)
{ {
struct section *sec; struct section *rsec;
struct reloc *reloc; struct reloc *reloc;
struct instruction *insn; struct instruction *insn;
s64 addend;
/* /*
* Check for manually annotated dead ends. * Check for manually annotated dead ends.
*/ */
sec = find_section_by_name(file->elf, ".rela.discard.unreachable"); rsec = find_section_by_name(file->elf, ".rela.discard.unreachable");
if (!sec) if (!rsec)
goto reachable; goto reachable;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) { if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", sec->name); WARN("unexpected relocation symbol type in %s", rsec->name);
return -1; return -1;
} }
insn = find_insn(file, reloc->sym->sec, reloc->addend);
addend = reloc_addend(reloc);
insn = find_insn(file, reloc->sym->sec, addend);
if (insn) if (insn)
insn = prev_insn_same_sec(file, insn); insn = prev_insn_same_sec(file, insn);
else if (reloc->addend == reloc->sym->sec->sh.sh_size) { else if (addend == reloc->sym->sec->sh.sh_size) {
insn = find_last_insn(file, reloc->sym->sec); insn = find_last_insn(file, reloc->sym->sec);
if (!insn) { if (!insn) {
WARN("can't find unreachable insn at %s+0x%" PRIx64, WARN("can't find unreachable insn at %s+0x%" PRIx64,
reloc->sym->sec->name, reloc->addend); reloc->sym->sec->name, addend);
return -1; return -1;
} }
} else { } else {
WARN("can't find unreachable insn at %s+0x%" PRIx64, WARN("can't find unreachable insn at %s+0x%" PRIx64,
reloc->sym->sec->name, reloc->addend); reloc->sym->sec->name, addend);
return -1; return -1;
} }
...@@ -662,28 +628,32 @@ static int add_dead_ends(struct objtool_file *file) ...@@ -662,28 +628,32 @@ static int add_dead_ends(struct objtool_file *file)
* GCC doesn't know the "ud2" is fatal, so it generates code as if it's * GCC doesn't know the "ud2" is fatal, so it generates code as if it's
* not a dead end. * not a dead end.
*/ */
sec = find_section_by_name(file->elf, ".rela.discard.reachable"); rsec = find_section_by_name(file->elf, ".rela.discard.reachable");
if (!sec) if (!rsec)
return 0; return 0;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) { if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", sec->name); WARN("unexpected relocation symbol type in %s", rsec->name);
return -1; return -1;
} }
insn = find_insn(file, reloc->sym->sec, reloc->addend);
addend = reloc_addend(reloc);
insn = find_insn(file, reloc->sym->sec, addend);
if (insn) if (insn)
insn = prev_insn_same_sec(file, insn); insn = prev_insn_same_sec(file, insn);
else if (reloc->addend == reloc->sym->sec->sh.sh_size) { else if (addend == reloc->sym->sec->sh.sh_size) {
insn = find_last_insn(file, reloc->sym->sec); insn = find_last_insn(file, reloc->sym->sec);
if (!insn) { if (!insn) {
WARN("can't find reachable insn at %s+0x%" PRIx64, WARN("can't find reachable insn at %s+0x%" PRIx64,
reloc->sym->sec->name, reloc->addend); reloc->sym->sec->name, addend);
return -1; return -1;
} }
} else { } else {
WARN("can't find reachable insn at %s+0x%" PRIx64, WARN("can't find reachable insn at %s+0x%" PRIx64,
reloc->sym->sec->name, reloc->addend); reloc->sym->sec->name, addend);
return -1; return -1;
} }
...@@ -695,8 +665,8 @@ static int add_dead_ends(struct objtool_file *file) ...@@ -695,8 +665,8 @@ static int add_dead_ends(struct objtool_file *file)
static int create_static_call_sections(struct objtool_file *file) static int create_static_call_sections(struct objtool_file *file)
{ {
struct section *sec;
struct static_call_site *site; struct static_call_site *site;
struct section *sec;
struct instruction *insn; struct instruction *insn;
struct symbol *key_sym; struct symbol *key_sym;
char *key_name, *tmp; char *key_name, *tmp;
...@@ -716,21 +686,20 @@ static int create_static_call_sections(struct objtool_file *file) ...@@ -716,21 +686,20 @@ static int create_static_call_sections(struct objtool_file *file)
list_for_each_entry(insn, &file->static_call_list, call_node) list_for_each_entry(insn, &file->static_call_list, call_node)
idx++; idx++;
sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE, sec = elf_create_section_pair(file->elf, ".static_call_sites",
sizeof(struct static_call_site), idx); sizeof(*site), idx, idx * 2);
if (!sec) if (!sec)
return -1; return -1;
/* Allow modules to modify the low bits of static_call_site::key */
sec->sh.sh_flags |= SHF_WRITE;
idx = 0; idx = 0;
list_for_each_entry(insn, &file->static_call_list, call_node) { list_for_each_entry(insn, &file->static_call_list, call_node) {
site = (struct static_call_site *)sec->data->d_buf + idx;
memset(site, 0, sizeof(struct static_call_site));
/* populate reloc for 'addr' */ /* populate reloc for 'addr' */
if (elf_add_reloc_to_insn(file->elf, sec, if (!elf_init_reloc_text_sym(file->elf, sec,
idx * sizeof(struct static_call_site), idx * sizeof(*site), idx * 2,
R_X86_64_PC32,
insn->sec, insn->offset)) insn->sec, insn->offset))
return -1; return -1;
...@@ -771,9 +740,9 @@ static int create_static_call_sections(struct objtool_file *file) ...@@ -771,9 +740,9 @@ static int create_static_call_sections(struct objtool_file *file)
free(key_name); free(key_name);
/* populate reloc for 'key' */ /* populate reloc for 'key' */
if (elf_add_reloc(file->elf, sec, if (!elf_init_reloc_data_sym(file->elf, sec,
idx * sizeof(struct static_call_site) + 4, idx * sizeof(*site) + 4,
R_X86_64_PC32, key_sym, (idx * 2) + 1, key_sym,
is_sibling_call(insn) * STATIC_CALL_SITE_TAIL)) is_sibling_call(insn) * STATIC_CALL_SITE_TAIL))
return -1; return -1;
...@@ -802,26 +771,18 @@ static int create_retpoline_sites_sections(struct objtool_file *file) ...@@ -802,26 +771,18 @@ static int create_retpoline_sites_sections(struct objtool_file *file)
if (!idx) if (!idx)
return 0; return 0;
sec = elf_create_section(file->elf, ".retpoline_sites", 0, sec = elf_create_section_pair(file->elf, ".retpoline_sites",
sizeof(int), idx); sizeof(int), idx, idx);
if (!sec) { if (!sec)
WARN("elf_create_section: .retpoline_sites");
return -1; return -1;
}
idx = 0; idx = 0;
list_for_each_entry(insn, &file->retpoline_call_list, call_node) { list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
int *site = (int *)sec->data->d_buf + idx; if (!elf_init_reloc_text_sym(file->elf, sec,
*site = 0; idx * sizeof(int), idx,
insn->sec, insn->offset))
if (elf_add_reloc_to_insn(file->elf, sec,
idx * sizeof(int),
R_X86_64_PC32,
insn->sec, insn->offset)) {
WARN("elf_add_reloc_to_insn: .retpoline_sites");
return -1; return -1;
}
idx++; idx++;
} }
...@@ -848,26 +809,18 @@ static int create_return_sites_sections(struct objtool_file *file) ...@@ -848,26 +809,18 @@ static int create_return_sites_sections(struct objtool_file *file)
if (!idx) if (!idx)
return 0; return 0;
sec = elf_create_section(file->elf, ".return_sites", 0, sec = elf_create_section_pair(file->elf, ".return_sites",
sizeof(int), idx); sizeof(int), idx, idx);
if (!sec) { if (!sec)
WARN("elf_create_section: .return_sites");
return -1; return -1;
}
idx = 0; idx = 0;
list_for_each_entry(insn, &file->return_thunk_list, call_node) { list_for_each_entry(insn, &file->return_thunk_list, call_node) {
int *site = (int *)sec->data->d_buf + idx; if (!elf_init_reloc_text_sym(file->elf, sec,
*site = 0; idx * sizeof(int), idx,
insn->sec, insn->offset))
if (elf_add_reloc_to_insn(file->elf, sec,
idx * sizeof(int),
R_X86_64_PC32,
insn->sec, insn->offset)) {
WARN("elf_add_reloc_to_insn: .return_sites");
return -1; return -1;
}
idx++; idx++;
} }
...@@ -900,12 +853,10 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file) ...@@ -900,12 +853,10 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
if (!idx) if (!idx)
return 0; return 0;
sec = elf_create_section(file->elf, ".ibt_endbr_seal", 0, sec = elf_create_section_pair(file->elf, ".ibt_endbr_seal",
sizeof(int), idx); sizeof(int), idx, idx);
if (!sec) { if (!sec)
WARN("elf_create_section: .ibt_endbr_seal");
return -1; return -1;
}
idx = 0; idx = 0;
list_for_each_entry(insn, &file->endbr_list, call_node) { list_for_each_entry(insn, &file->endbr_list, call_node) {
...@@ -920,13 +871,10 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file) ...@@ -920,13 +871,10 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
!strcmp(sym->name, "cleanup_module"))) !strcmp(sym->name, "cleanup_module")))
WARN("%s(): not an indirect call target", sym->name); WARN("%s(): not an indirect call target", sym->name);
if (elf_add_reloc_to_insn(file->elf, sec, if (!elf_init_reloc_text_sym(file->elf, sec,
idx * sizeof(int), idx * sizeof(int), idx,
R_X86_64_PC32, insn->sec, insn->offset))
insn->sec, insn->offset)) {
WARN("elf_add_reloc_to_insn: .ibt_endbr_seal");
return -1; return -1;
}
idx++; idx++;
} }
...@@ -938,7 +886,6 @@ static int create_cfi_sections(struct objtool_file *file) ...@@ -938,7 +886,6 @@ static int create_cfi_sections(struct objtool_file *file)
{ {
struct section *sec; struct section *sec;
struct symbol *sym; struct symbol *sym;
unsigned int *loc;
int idx; int idx;
sec = find_section_by_name(file->elf, ".cfi_sites"); sec = find_section_by_name(file->elf, ".cfi_sites");
...@@ -959,7 +906,8 @@ static int create_cfi_sections(struct objtool_file *file) ...@@ -959,7 +906,8 @@ static int create_cfi_sections(struct objtool_file *file)
idx++; idx++;
} }
sec = elf_create_section(file->elf, ".cfi_sites", 0, sizeof(unsigned int), idx); sec = elf_create_section_pair(file->elf, ".cfi_sites",
sizeof(unsigned int), idx, idx);
if (!sec) if (!sec)
return -1; return -1;
...@@ -971,12 +919,8 @@ static int create_cfi_sections(struct objtool_file *file) ...@@ -971,12 +919,8 @@ static int create_cfi_sections(struct objtool_file *file)
if (strncmp(sym->name, "__cfi_", 6)) if (strncmp(sym->name, "__cfi_", 6))
continue; continue;
loc = (unsigned int *)sec->data->d_buf + idx; if (!elf_init_reloc_text_sym(file->elf, sec,
memset(loc, 0, sizeof(unsigned int)); idx * sizeof(unsigned int), idx,
if (elf_add_reloc_to_insn(file->elf, sec,
idx * sizeof(unsigned int),
R_X86_64_PC32,
sym->sec, sym->offset)) sym->sec, sym->offset))
return -1; return -1;
...@@ -988,7 +932,7 @@ static int create_cfi_sections(struct objtool_file *file) ...@@ -988,7 +932,7 @@ static int create_cfi_sections(struct objtool_file *file)
static int create_mcount_loc_sections(struct objtool_file *file) static int create_mcount_loc_sections(struct objtool_file *file)
{ {
int addrsize = elf_class_addrsize(file->elf); size_t addr_size = elf_addr_size(file->elf);
struct instruction *insn; struct instruction *insn;
struct section *sec; struct section *sec;
int idx; int idx;
...@@ -1007,25 +951,26 @@ static int create_mcount_loc_sections(struct objtool_file *file) ...@@ -1007,25 +951,26 @@ static int create_mcount_loc_sections(struct objtool_file *file)
list_for_each_entry(insn, &file->mcount_loc_list, call_node) list_for_each_entry(insn, &file->mcount_loc_list, call_node)
idx++; idx++;
sec = elf_create_section(file->elf, "__mcount_loc", 0, addrsize, idx); sec = elf_create_section_pair(file->elf, "__mcount_loc", addr_size,
idx, idx);
if (!sec) if (!sec)
return -1; return -1;
sec->sh.sh_addralign = addrsize; sec->sh.sh_addralign = addr_size;
idx = 0; idx = 0;
list_for_each_entry(insn, &file->mcount_loc_list, call_node) { list_for_each_entry(insn, &file->mcount_loc_list, call_node) {
void *loc;
loc = sec->data->d_buf + idx; struct reloc *reloc;
memset(loc, 0, addrsize);
if (elf_add_reloc_to_insn(file->elf, sec, idx, reloc = elf_init_reloc_text_sym(file->elf, sec, idx * addr_size, idx,
addrsize == sizeof(u64) ? R_ABS64 : R_ABS32, insn->sec, insn->offset);
insn->sec, insn->offset)) if (!reloc)
return -1; return -1;
idx += addrsize; set_reloc_type(file->elf, reloc, addr_size == 8 ? R_ABS64 : R_ABS32);
idx++;
} }
return 0; return 0;
...@@ -1035,7 +980,6 @@ static int create_direct_call_sections(struct objtool_file *file) ...@@ -1035,7 +980,6 @@ static int create_direct_call_sections(struct objtool_file *file)
{ {
struct instruction *insn; struct instruction *insn;
struct section *sec; struct section *sec;
unsigned int *loc;
int idx; int idx;
sec = find_section_by_name(file->elf, ".call_sites"); sec = find_section_by_name(file->elf, ".call_sites");
...@@ -1052,19 +996,16 @@ static int create_direct_call_sections(struct objtool_file *file) ...@@ -1052,19 +996,16 @@ static int create_direct_call_sections(struct objtool_file *file)
list_for_each_entry(insn, &file->call_list, call_node) list_for_each_entry(insn, &file->call_list, call_node)
idx++; idx++;
sec = elf_create_section(file->elf, ".call_sites", 0, sizeof(unsigned int), idx); sec = elf_create_section_pair(file->elf, ".call_sites",
sizeof(unsigned int), idx, idx);
if (!sec) if (!sec)
return -1; return -1;
idx = 0; idx = 0;
list_for_each_entry(insn, &file->call_list, call_node) { list_for_each_entry(insn, &file->call_list, call_node) {
loc = (unsigned int *)sec->data->d_buf + idx; if (!elf_init_reloc_text_sym(file->elf, sec,
memset(loc, 0, sizeof(unsigned int)); idx * sizeof(unsigned int), idx,
if (elf_add_reloc_to_insn(file->elf, sec,
idx * sizeof(unsigned int),
R_X86_64_PC32,
insn->sec, insn->offset)) insn->sec, insn->offset))
return -1; return -1;
...@@ -1080,28 +1021,29 @@ static int create_direct_call_sections(struct objtool_file *file) ...@@ -1080,28 +1021,29 @@ static int create_direct_call_sections(struct objtool_file *file)
static void add_ignores(struct objtool_file *file) static void add_ignores(struct objtool_file *file)
{ {
struct instruction *insn; struct instruction *insn;
struct section *sec; struct section *rsec;
struct symbol *func; struct symbol *func;
struct reloc *reloc; struct reloc *reloc;
sec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard"); rsec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
if (!sec) if (!rsec)
return; return;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
switch (reloc->sym->type) { switch (reloc->sym->type) {
case STT_FUNC: case STT_FUNC:
func = reloc->sym; func = reloc->sym;
break; break;
case STT_SECTION: case STT_SECTION:
func = find_func_by_offset(reloc->sym->sec, reloc->addend); func = find_func_by_offset(reloc->sym->sec, reloc_addend(reloc));
if (!func) if (!func)
continue; continue;
break; break;
default: default:
WARN("unexpected relocation symbol type in %s: %d", sec->name, reloc->sym->type); WARN("unexpected relocation symbol type in %s: %d",
rsec->name, reloc->sym->type);
continue; continue;
} }
...@@ -1320,21 +1262,21 @@ static void add_uaccess_safe(struct objtool_file *file) ...@@ -1320,21 +1262,21 @@ static void add_uaccess_safe(struct objtool_file *file)
*/ */
static int add_ignore_alternatives(struct objtool_file *file) static int add_ignore_alternatives(struct objtool_file *file)
{ {
struct section *sec; struct section *rsec;
struct reloc *reloc; struct reloc *reloc;
struct instruction *insn; struct instruction *insn;
sec = find_section_by_name(file->elf, ".rela.discard.ignore_alts"); rsec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
if (!sec) if (!rsec)
return 0; return 0;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) { if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", sec->name); WARN("unexpected relocation symbol type in %s", rsec->name);
return -1; return -1;
} }
insn = find_insn(file, reloc->sym->sec, reloc->addend); insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) { if (!insn) {
WARN("bad .discard.ignore_alts entry"); WARN("bad .discard.ignore_alts entry");
return -1; return -1;
...@@ -1421,10 +1363,8 @@ static void annotate_call_site(struct objtool_file *file, ...@@ -1421,10 +1363,8 @@ static void annotate_call_site(struct objtool_file *file,
* noinstr text. * noinstr text.
*/ */
if (opts.hack_noinstr && insn->sec->noinstr && sym->profiling_func) { if (opts.hack_noinstr && insn->sec->noinstr && sym->profiling_func) {
if (reloc) { if (reloc)
reloc->type = R_NONE; set_reloc_type(file->elf, reloc, R_NONE);
elf_write_reloc(file->elf, reloc);
}
elf_write_insn(file->elf, insn->sec, elf_write_insn(file->elf, insn->sec,
insn->offset, insn->len, insn->offset, insn->len,
...@@ -1450,10 +1390,8 @@ static void annotate_call_site(struct objtool_file *file, ...@@ -1450,10 +1390,8 @@ static void annotate_call_site(struct objtool_file *file,
if (sibling) if (sibling)
WARN_INSN(insn, "tail call to __fentry__ !?!?"); WARN_INSN(insn, "tail call to __fentry__ !?!?");
if (opts.mnop) { if (opts.mnop) {
if (reloc) { if (reloc)
reloc->type = R_NONE; set_reloc_type(file->elf, reloc, R_NONE);
elf_write_reloc(file->elf, reloc);
}
elf_write_insn(file->elf, insn->sec, elf_write_insn(file->elf, insn->sec,
insn->offset, insn->len, insn->offset, insn->len,
...@@ -1610,7 +1548,7 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -1610,7 +1548,7 @@ static int add_jump_destinations(struct objtool_file *file)
dest_off = arch_jump_destination(insn); dest_off = arch_jump_destination(insn);
} else if (reloc->sym->type == STT_SECTION) { } else if (reloc->sym->type == STT_SECTION) {
dest_sec = reloc->sym->sec; dest_sec = reloc->sym->sec;
dest_off = arch_dest_reloc_offset(reloc->addend); dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
} else if (reloc->sym->retpoline_thunk) { } else if (reloc->sym->retpoline_thunk) {
add_retpoline_call(file, insn); add_retpoline_call(file, insn);
continue; continue;
...@@ -1627,7 +1565,7 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -1627,7 +1565,7 @@ static int add_jump_destinations(struct objtool_file *file)
} else if (reloc->sym->sec->idx) { } else if (reloc->sym->sec->idx) {
dest_sec = reloc->sym->sec; dest_sec = reloc->sym->sec;
dest_off = reloc->sym->sym.st_value + dest_off = reloc->sym->sym.st_value +
arch_dest_reloc_offset(reloc->addend); arch_dest_reloc_offset(reloc_addend(reloc));
} else { } else {
/* non-func asm code jumping to another file */ /* non-func asm code jumping to another file */
continue; continue;
...@@ -1744,7 +1682,7 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1744,7 +1682,7 @@ static int add_call_destinations(struct objtool_file *file)
} }
} else if (reloc->sym->type == STT_SECTION) { } else if (reloc->sym->type == STT_SECTION) {
dest_off = arch_dest_reloc_offset(reloc->addend); dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
dest = find_call_destination(reloc->sym->sec, dest_off); dest = find_call_destination(reloc->sym->sec, dest_off);
if (!dest) { if (!dest) {
WARN_INSN(insn, "can't find call dest symbol at %s+0x%lx", WARN_INSN(insn, "can't find call dest symbol at %s+0x%lx",
...@@ -1932,10 +1870,8 @@ static int handle_jump_alt(struct objtool_file *file, ...@@ -1932,10 +1870,8 @@ static int handle_jump_alt(struct objtool_file *file,
if (opts.hack_jump_label && special_alt->key_addend & 2) { if (opts.hack_jump_label && special_alt->key_addend & 2) {
struct reloc *reloc = insn_reloc(file, orig_insn); struct reloc *reloc = insn_reloc(file, orig_insn);
if (reloc) { if (reloc)
reloc->type = R_NONE; set_reloc_type(file->elf, reloc, R_NONE);
elf_write_reloc(file->elf, reloc);
}
elf_write_insn(file->elf, orig_insn->sec, elf_write_insn(file->elf, orig_insn->sec,
orig_insn->offset, orig_insn->len, orig_insn->offset, orig_insn->len,
arch_nop_insn(orig_insn->len)); arch_nop_insn(orig_insn->len));
...@@ -2047,34 +1983,35 @@ static int add_special_section_alts(struct objtool_file *file) ...@@ -2047,34 +1983,35 @@ static int add_special_section_alts(struct objtool_file *file)
} }
static int add_jump_table(struct objtool_file *file, struct instruction *insn, static int add_jump_table(struct objtool_file *file, struct instruction *insn,
struct reloc *table) struct reloc *next_table)
{ {
struct reloc *reloc = table;
struct instruction *dest_insn;
struct alternative *alt;
struct symbol *pfunc = insn_func(insn)->pfunc; struct symbol *pfunc = insn_func(insn)->pfunc;
struct reloc *table = insn_jump_table(insn);
struct instruction *dest_insn;
unsigned int prev_offset = 0; unsigned int prev_offset = 0;
struct reloc *reloc = table;
struct alternative *alt;
/* /*
* Each @reloc is a switch table relocation which points to the target * Each @reloc is a switch table relocation which points to the target
* instruction. * instruction.
*/ */
list_for_each_entry_from(reloc, &table->sec->reloc_list, list) { for_each_reloc_from(table->sec, reloc) {
/* Check for the end of the table: */ /* Check for the end of the table: */
if (reloc != table && reloc->jump_table_start) if (reloc != table && reloc == next_table)
break; break;
/* Make sure the table entries are consecutive: */ /* Make sure the table entries are consecutive: */
if (prev_offset && reloc->offset != prev_offset + 8) if (prev_offset && reloc_offset(reloc) != prev_offset + 8)
break; break;
/* Detect function pointers from contiguous objects: */ /* Detect function pointers from contiguous objects: */
if (reloc->sym->sec == pfunc->sec && if (reloc->sym->sec == pfunc->sec &&
reloc->addend == pfunc->offset) reloc_addend(reloc) == pfunc->offset)
break; break;
dest_insn = find_insn(file, reloc->sym->sec, reloc->addend); dest_insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!dest_insn) if (!dest_insn)
break; break;
...@@ -2091,7 +2028,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, ...@@ -2091,7 +2028,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
alt->insn = dest_insn; alt->insn = dest_insn;
alt->next = insn->alts; alt->next = insn->alts;
insn->alts = alt; insn->alts = alt;
prev_offset = reloc->offset; prev_offset = reloc_offset(reloc);
} }
if (!prev_offset) { if (!prev_offset) {
...@@ -2135,7 +2072,7 @@ static struct reloc *find_jump_table(struct objtool_file *file, ...@@ -2135,7 +2072,7 @@ static struct reloc *find_jump_table(struct objtool_file *file,
table_reloc = arch_find_switch_table(file, insn); table_reloc = arch_find_switch_table(file, insn);
if (!table_reloc) if (!table_reloc)
continue; continue;
dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend); dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc));
if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func) if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func)
continue; continue;
...@@ -2177,29 +2114,39 @@ static void mark_func_jump_tables(struct objtool_file *file, ...@@ -2177,29 +2114,39 @@ static void mark_func_jump_tables(struct objtool_file *file,
continue; continue;
reloc = find_jump_table(file, func, insn); reloc = find_jump_table(file, func, insn);
if (reloc) { if (reloc)
reloc->jump_table_start = true;
insn->_jump_table = reloc; insn->_jump_table = reloc;
} }
}
} }
static int add_func_jump_tables(struct objtool_file *file, static int add_func_jump_tables(struct objtool_file *file,
struct symbol *func) struct symbol *func)
{ {
struct instruction *insn; struct instruction *insn, *insn_t1 = NULL, *insn_t2;
int ret; int ret = 0;
func_for_each_insn(file, func, insn) { func_for_each_insn(file, func, insn) {
if (!insn_jump_table(insn)) if (!insn_jump_table(insn))
continue; continue;
ret = add_jump_table(file, insn, insn_jump_table(insn)); if (!insn_t1) {
insn_t1 = insn;
continue;
}
insn_t2 = insn;
ret = add_jump_table(file, insn_t1, insn_jump_table(insn_t2));
if (ret) if (ret)
return ret; return ret;
insn_t1 = insn_t2;
} }
return 0; if (insn_t1)
ret = add_jump_table(file, insn_t1, NULL);
return ret;
} }
/* /*
...@@ -2240,7 +2187,7 @@ static void set_func_state(struct cfi_state *state) ...@@ -2240,7 +2187,7 @@ static void set_func_state(struct cfi_state *state)
static int read_unwind_hints(struct objtool_file *file) static int read_unwind_hints(struct objtool_file *file)
{ {
struct cfi_state cfi = init_cfi; struct cfi_state cfi = init_cfi;
struct section *sec, *relocsec; struct section *sec;
struct unwind_hint *hint; struct unwind_hint *hint;
struct instruction *insn; struct instruction *insn;
struct reloc *reloc; struct reloc *reloc;
...@@ -2250,8 +2197,7 @@ static int read_unwind_hints(struct objtool_file *file) ...@@ -2250,8 +2197,7 @@ static int read_unwind_hints(struct objtool_file *file)
if (!sec) if (!sec)
return 0; return 0;
relocsec = sec->reloc; if (!sec->rsec) {
if (!relocsec) {
WARN("missing .rela.discard.unwind_hints section"); WARN("missing .rela.discard.unwind_hints section");
return -1; return -1;
} }
...@@ -2272,7 +2218,7 @@ static int read_unwind_hints(struct objtool_file *file) ...@@ -2272,7 +2218,7 @@ static int read_unwind_hints(struct objtool_file *file)
return -1; return -1;
} }
insn = find_insn(file, reloc->sym->sec, reloc->addend); insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) { if (!insn) {
WARN("can't find insn for unwind_hints[%d]", i); WARN("can't find insn for unwind_hints[%d]", i);
return -1; return -1;
...@@ -2280,6 +2226,11 @@ static int read_unwind_hints(struct objtool_file *file) ...@@ -2280,6 +2226,11 @@ static int read_unwind_hints(struct objtool_file *file)
insn->hint = true; insn->hint = true;
if (hint->type == UNWIND_HINT_TYPE_UNDEFINED) {
insn->cfi = &force_undefined_cfi;
continue;
}
if (hint->type == UNWIND_HINT_TYPE_SAVE) { if (hint->type == UNWIND_HINT_TYPE_SAVE) {
insn->hint = false; insn->hint = false;
insn->save = true; insn->save = true;
...@@ -2326,16 +2277,17 @@ static int read_unwind_hints(struct objtool_file *file) ...@@ -2326,16 +2277,17 @@ static int read_unwind_hints(struct objtool_file *file)
static int read_noendbr_hints(struct objtool_file *file) static int read_noendbr_hints(struct objtool_file *file)
{ {
struct section *sec;
struct instruction *insn; struct instruction *insn;
struct section *rsec;
struct reloc *reloc; struct reloc *reloc;
sec = find_section_by_name(file->elf, ".rela.discard.noendbr"); rsec = find_section_by_name(file->elf, ".rela.discard.noendbr");
if (!sec) if (!rsec)
return 0; return 0;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
insn = find_insn(file, reloc->sym->sec, reloc->sym->offset + reloc->addend); insn = find_insn(file, reloc->sym->sec,
reloc->sym->offset + reloc_addend(reloc));
if (!insn) { if (!insn) {
WARN("bad .discard.noendbr entry"); WARN("bad .discard.noendbr entry");
return -1; return -1;
...@@ -2349,21 +2301,21 @@ static int read_noendbr_hints(struct objtool_file *file) ...@@ -2349,21 +2301,21 @@ static int read_noendbr_hints(struct objtool_file *file)
static int read_retpoline_hints(struct objtool_file *file) static int read_retpoline_hints(struct objtool_file *file)
{ {
struct section *sec; struct section *rsec;
struct instruction *insn; struct instruction *insn;
struct reloc *reloc; struct reloc *reloc;
sec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe"); rsec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe");
if (!sec) if (!rsec)
return 0; return 0;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) { if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", sec->name); WARN("unexpected relocation symbol type in %s", rsec->name);
return -1; return -1;
} }
insn = find_insn(file, reloc->sym->sec, reloc->addend); insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) { if (!insn) {
WARN("bad .discard.retpoline_safe entry"); WARN("bad .discard.retpoline_safe entry");
return -1; return -1;
...@@ -2385,21 +2337,21 @@ static int read_retpoline_hints(struct objtool_file *file) ...@@ -2385,21 +2337,21 @@ static int read_retpoline_hints(struct objtool_file *file)
static int read_instr_hints(struct objtool_file *file) static int read_instr_hints(struct objtool_file *file)
{ {
struct section *sec; struct section *rsec;
struct instruction *insn; struct instruction *insn;
struct reloc *reloc; struct reloc *reloc;
sec = find_section_by_name(file->elf, ".rela.discard.instr_end"); rsec = find_section_by_name(file->elf, ".rela.discard.instr_end");
if (!sec) if (!rsec)
return 0; return 0;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) { if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", sec->name); WARN("unexpected relocation symbol type in %s", rsec->name);
return -1; return -1;
} }
insn = find_insn(file, reloc->sym->sec, reloc->addend); insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) { if (!insn) {
WARN("bad .discard.instr_end entry"); WARN("bad .discard.instr_end entry");
return -1; return -1;
...@@ -2408,17 +2360,17 @@ static int read_instr_hints(struct objtool_file *file) ...@@ -2408,17 +2360,17 @@ static int read_instr_hints(struct objtool_file *file)
insn->instr--; insn->instr--;
} }
sec = find_section_by_name(file->elf, ".rela.discard.instr_begin"); rsec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
if (!sec) if (!rsec)
return 0; return 0;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) { if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", sec->name); WARN("unexpected relocation symbol type in %s", rsec->name);
return -1; return -1;
} }
insn = find_insn(file, reloc->sym->sec, reloc->addend); insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) { if (!insn) {
WARN("bad .discard.instr_begin entry"); WARN("bad .discard.instr_begin entry");
return -1; return -1;
...@@ -2432,21 +2384,21 @@ static int read_instr_hints(struct objtool_file *file) ...@@ -2432,21 +2384,21 @@ static int read_instr_hints(struct objtool_file *file)
static int read_validate_unret_hints(struct objtool_file *file) static int read_validate_unret_hints(struct objtool_file *file)
{ {
struct section *sec; struct section *rsec;
struct instruction *insn; struct instruction *insn;
struct reloc *reloc; struct reloc *reloc;
sec = find_section_by_name(file->elf, ".rela.discard.validate_unret"); rsec = find_section_by_name(file->elf, ".rela.discard.validate_unret");
if (!sec) if (!rsec)
return 0; return 0;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) { if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", sec->name); WARN("unexpected relocation symbol type in %s", rsec->name);
return -1; return -1;
} }
insn = find_insn(file, reloc->sym->sec, reloc->addend); insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) { if (!insn) {
WARN("bad .discard.instr_end entry"); WARN("bad .discard.instr_end entry");
return -1; return -1;
...@@ -2461,23 +2413,23 @@ static int read_validate_unret_hints(struct objtool_file *file) ...@@ -2461,23 +2413,23 @@ static int read_validate_unret_hints(struct objtool_file *file)
static int read_intra_function_calls(struct objtool_file *file) static int read_intra_function_calls(struct objtool_file *file)
{ {
struct instruction *insn; struct instruction *insn;
struct section *sec; struct section *rsec;
struct reloc *reloc; struct reloc *reloc;
sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls"); rsec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls");
if (!sec) if (!rsec)
return 0; return 0;
list_for_each_entry(reloc, &sec->reloc_list, list) { for_each_reloc(rsec, reloc) {
unsigned long dest_off; unsigned long dest_off;
if (reloc->sym->type != STT_SECTION) { if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", WARN("unexpected relocation symbol type in %s",
sec->name); rsec->name);
return -1; return -1;
} }
insn = find_insn(file, reloc->sym->sec, reloc->addend); insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) { if (!insn) {
WARN("bad .discard.intra_function_call entry"); WARN("bad .discard.intra_function_call entry");
return -1; return -1;
...@@ -2833,6 +2785,10 @@ static int update_cfi_state(struct instruction *insn, ...@@ -2833,6 +2785,10 @@ static int update_cfi_state(struct instruction *insn,
struct cfi_reg *cfa = &cfi->cfa; struct cfi_reg *cfa = &cfi->cfa;
struct cfi_reg *regs = cfi->regs; struct cfi_reg *regs = cfi->regs;
/* ignore UNWIND_HINT_UNDEFINED regions */
if (cfi->force_undefined)
return 0;
/* stack operations don't make sense with an undefined CFA */ /* stack operations don't make sense with an undefined CFA */
if (cfa->base == CFI_UNDEFINED) { if (cfa->base == CFI_UNDEFINED) {
if (insn_func(insn)) { if (insn_func(insn)) {
...@@ -3369,15 +3325,15 @@ static inline bool func_uaccess_safe(struct symbol *func) ...@@ -3369,15 +3325,15 @@ static inline bool func_uaccess_safe(struct symbol *func)
static inline const char *call_dest_name(struct instruction *insn) static inline const char *call_dest_name(struct instruction *insn)
{ {
static char pvname[19]; static char pvname[19];
struct reloc *rel; struct reloc *reloc;
int idx; int idx;
if (insn_call_dest(insn)) if (insn_call_dest(insn))
return insn_call_dest(insn)->name; return insn_call_dest(insn)->name;
rel = insn_reloc(NULL, insn); reloc = insn_reloc(NULL, insn);
if (rel && !strcmp(rel->sym->name, "pv_ops")) { if (reloc && !strcmp(reloc->sym->name, "pv_ops")) {
idx = (rel->addend / sizeof(void *)); idx = (reloc_addend(reloc) / sizeof(void *));
snprintf(pvname, sizeof(pvname), "pv_ops[%d]", idx); snprintf(pvname, sizeof(pvname), "pv_ops[%d]", idx);
return pvname; return pvname;
} }
...@@ -3388,14 +3344,14 @@ static inline const char *call_dest_name(struct instruction *insn) ...@@ -3388,14 +3344,14 @@ static inline const char *call_dest_name(struct instruction *insn)
static bool pv_call_dest(struct objtool_file *file, struct instruction *insn) static bool pv_call_dest(struct objtool_file *file, struct instruction *insn)
{ {
struct symbol *target; struct symbol *target;
struct reloc *rel; struct reloc *reloc;
int idx; int idx;
rel = insn_reloc(file, insn); reloc = insn_reloc(file, insn);
if (!rel || strcmp(rel->sym->name, "pv_ops")) if (!reloc || strcmp(reloc->sym->name, "pv_ops"))
return false; return false;
idx = (arch_dest_reloc_offset(rel->addend) / sizeof(void *)); idx = (arch_dest_reloc_offset(reloc_addend(reloc)) / sizeof(void *));
if (file->pv_ops[idx].clean) if (file->pv_ops[idx].clean)
return true; return true;
...@@ -3657,8 +3613,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, ...@@ -3657,8 +3613,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
ret = validate_branch(file, func, alt->insn, state); ret = validate_branch(file, func, alt->insn, state);
if (ret) { if (ret) {
if (opts.backtrace) BT_INSN(insn, "(alt)");
BT_FUNC("(alt)", insn);
return ret; return ret;
} }
} }
...@@ -3703,8 +3658,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, ...@@ -3703,8 +3658,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
ret = validate_branch(file, func, ret = validate_branch(file, func,
insn->jump_dest, state); insn->jump_dest, state);
if (ret) { if (ret) {
if (opts.backtrace) BT_INSN(insn, "(branch)");
BT_FUNC("(branch)", insn);
return ret; return ret;
} }
} }
...@@ -3802,8 +3756,8 @@ static int validate_unwind_hint(struct objtool_file *file, ...@@ -3802,8 +3756,8 @@ static int validate_unwind_hint(struct objtool_file *file,
{ {
if (insn->hint && !insn->visited && !insn->ignore) { if (insn->hint && !insn->visited && !insn->ignore) {
int ret = validate_branch(file, insn_func(insn), insn, *state); int ret = validate_branch(file, insn_func(insn), insn, *state);
if (ret && opts.backtrace) if (ret)
BT_FUNC("<=== (hint)", insn); BT_INSN(insn, "<=== (hint)");
return ret; return ret;
} }
...@@ -3841,7 +3795,7 @@ static int validate_unwind_hints(struct objtool_file *file, struct section *sec) ...@@ -3841,7 +3795,7 @@ static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
static int validate_unret(struct objtool_file *file, struct instruction *insn) static int validate_unret(struct objtool_file *file, struct instruction *insn)
{ {
struct instruction *next, *dest; struct instruction *next, *dest;
int ret, warnings = 0; int ret;
for (;;) { for (;;) {
next = next_insn_to_validate(file, insn); next = next_insn_to_validate(file, insn);
...@@ -3861,8 +3815,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) ...@@ -3861,8 +3815,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn)
ret = validate_unret(file, alt->insn); ret = validate_unret(file, alt->insn);
if (ret) { if (ret) {
if (opts.backtrace) BT_INSN(insn, "(alt)");
BT_FUNC("(alt)", insn);
return ret; return ret;
} }
} }
...@@ -3888,10 +3841,8 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) ...@@ -3888,10 +3841,8 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn)
} }
ret = validate_unret(file, insn->jump_dest); ret = validate_unret(file, insn->jump_dest);
if (ret) { if (ret) {
if (opts.backtrace) { BT_INSN(insn, "(branch%s)",
BT_FUNC("(branch%s)", insn,
insn->type == INSN_JUMP_CONDITIONAL ? "-cond" : ""); insn->type == INSN_JUMP_CONDITIONAL ? "-cond" : "");
}
return ret; return ret;
} }
...@@ -3913,8 +3864,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) ...@@ -3913,8 +3864,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn)
ret = validate_unret(file, dest); ret = validate_unret(file, dest);
if (ret) { if (ret) {
if (opts.backtrace) BT_INSN(insn, "(call)");
BT_FUNC("(call)", insn);
return ret; return ret;
} }
/* /*
...@@ -3943,7 +3893,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) ...@@ -3943,7 +3893,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn)
insn = next; insn = next;
} }
return warnings; return 0;
} }
/* /*
...@@ -4178,7 +4128,6 @@ static int add_prefix_symbols(struct objtool_file *file) ...@@ -4178,7 +4128,6 @@ static int add_prefix_symbols(struct objtool_file *file)
{ {
struct section *sec; struct section *sec;
struct symbol *func; struct symbol *func;
int warnings = 0;
for_each_sec(file, sec) { for_each_sec(file, sec) {
if (!(sec->sh.sh_flags & SHF_EXECINSTR)) if (!(sec->sh.sh_flags & SHF_EXECINSTR))
...@@ -4192,7 +4141,7 @@ static int add_prefix_symbols(struct objtool_file *file) ...@@ -4192,7 +4141,7 @@ static int add_prefix_symbols(struct objtool_file *file)
} }
} }
return warnings; return 0;
} }
static int validate_symbol(struct objtool_file *file, struct section *sec, static int validate_symbol(struct objtool_file *file, struct section *sec,
...@@ -4216,8 +4165,8 @@ static int validate_symbol(struct objtool_file *file, struct section *sec, ...@@ -4216,8 +4165,8 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
state->uaccess = sym->uaccess_safe; state->uaccess = sym->uaccess_safe;
ret = validate_branch(file, insn_func(insn), insn, *state); ret = validate_branch(file, insn_func(insn), insn, *state);
if (ret && opts.backtrace) if (ret)
BT_FUNC("<=== (sym)", insn); BT_INSN(insn, "<=== (sym)");
return ret; return ret;
} }
...@@ -4333,8 +4282,8 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn ...@@ -4333,8 +4282,8 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn
for (reloc = insn_reloc(file, insn); for (reloc = insn_reloc(file, insn);
reloc; reloc;
reloc = find_reloc_by_dest_range(file->elf, insn->sec, reloc = find_reloc_by_dest_range(file->elf, insn->sec,
reloc->offset + 1, reloc_offset(reloc) + 1,
(insn->offset + insn->len) - (reloc->offset + 1))) { (insn->offset + insn->len) - (reloc_offset(reloc) + 1))) {
/* /*
* static_call_update() references the trampoline, which * static_call_update() references the trampoline, which
...@@ -4344,10 +4293,11 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn ...@@ -4344,10 +4293,11 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn
continue; continue;
off = reloc->sym->offset; off = reloc->sym->offset;
if (reloc->type == R_X86_64_PC32 || reloc->type == R_X86_64_PLT32) if (reloc_type(reloc) == R_X86_64_PC32 ||
off += arch_dest_reloc_offset(reloc->addend); reloc_type(reloc) == R_X86_64_PLT32)
off += arch_dest_reloc_offset(reloc_addend(reloc));
else else
off += reloc->addend; off += reloc_addend(reloc);
dest = find_insn(file, reloc->sym->sec, off); dest = find_insn(file, reloc->sym->sec, off);
if (!dest) if (!dest)
...@@ -4404,7 +4354,7 @@ static int validate_ibt_data_reloc(struct objtool_file *file, ...@@ -4404,7 +4354,7 @@ static int validate_ibt_data_reloc(struct objtool_file *file,
struct instruction *dest; struct instruction *dest;
dest = find_insn(file, reloc->sym->sec, dest = find_insn(file, reloc->sym->sec,
reloc->sym->offset + reloc->addend); reloc->sym->offset + reloc_addend(reloc));
if (!dest) if (!dest)
return 0; return 0;
...@@ -4417,7 +4367,7 @@ static int validate_ibt_data_reloc(struct objtool_file *file, ...@@ -4417,7 +4367,7 @@ static int validate_ibt_data_reloc(struct objtool_file *file,
return 0; return 0;
WARN_FUNC("data relocation to !ENDBR: %s", WARN_FUNC("data relocation to !ENDBR: %s",
reloc->sec->base, reloc->offset, reloc->sec->base, reloc_offset(reloc),
offstr(dest->sec, dest->offset)); offstr(dest->sec, dest->offset));
return 1; return 1;
...@@ -4444,7 +4394,7 @@ static int validate_ibt(struct objtool_file *file) ...@@ -4444,7 +4394,7 @@ static int validate_ibt(struct objtool_file *file)
if (sec->sh.sh_flags & SHF_EXECINSTR) if (sec->sh.sh_flags & SHF_EXECINSTR)
continue; continue;
if (!sec->reloc) if (!sec->rsec)
continue; continue;
/* /*
...@@ -4471,7 +4421,7 @@ static int validate_ibt(struct objtool_file *file) ...@@ -4471,7 +4421,7 @@ static int validate_ibt(struct objtool_file *file)
strstr(sec->name, "__patchable_function_entries")) strstr(sec->name, "__patchable_function_entries"))
continue; continue;
list_for_each_entry(reloc, &sec->reloc->reloc_list, list) for_each_reloc(sec->rsec, reloc)
warnings += validate_ibt_data_reloc(file, reloc); warnings += validate_ibt_data_reloc(file, reloc);
} }
...@@ -4511,9 +4461,40 @@ static int validate_sls(struct objtool_file *file) ...@@ -4511,9 +4461,40 @@ static int validate_sls(struct objtool_file *file)
return warnings; return warnings;
} }
static bool ignore_noreturn_call(struct instruction *insn)
{
struct symbol *call_dest = insn_call_dest(insn);
/*
* FIXME: hack, we need a real noreturn solution
*
* Problem is, exc_double_fault() may or may not return, depending on
* whether CONFIG_X86_ESPFIX64 is set. But objtool has no visibility
* to the kernel config.
*
* Other potential ways to fix it:
*
* - have compiler communicate __noreturn functions somehow
* - remove CONFIG_X86_ESPFIX64
* - read the .config file
* - add a cmdline option
* - create a generic objtool annotation format (vs a bunch of custom
* formats) and annotate it
*/
if (!strcmp(call_dest->name, "exc_double_fault")) {
/* prevent further unreachable warnings for the caller */
insn->sym->warned = 1;
return true;
}
return false;
}
static int validate_reachable_instructions(struct objtool_file *file) static int validate_reachable_instructions(struct objtool_file *file)
{ {
struct instruction *insn; struct instruction *insn, *prev_insn;
struct symbol *call_dest;
int warnings = 0;
if (file->ignore_unreachables) if (file->ignore_unreachables)
return 0; return 0;
...@@ -4522,13 +4503,127 @@ static int validate_reachable_instructions(struct objtool_file *file) ...@@ -4522,13 +4503,127 @@ static int validate_reachable_instructions(struct objtool_file *file)
if (insn->visited || ignore_unreachable_insn(file, insn)) if (insn->visited || ignore_unreachable_insn(file, insn))
continue; continue;
prev_insn = prev_insn_same_sec(file, insn);
if (prev_insn && prev_insn->dead_end) {
call_dest = insn_call_dest(prev_insn);
if (call_dest && !ignore_noreturn_call(prev_insn)) {
WARN_INSN(insn, "%s() is missing a __noreturn annotation",
call_dest->name);
warnings++;
continue;
}
}
WARN_INSN(insn, "unreachable instruction"); WARN_INSN(insn, "unreachable instruction");
return 1; warnings++;
}
return warnings;
}
/* 'funcs' is a space-separated list of function names */
static int disas_funcs(const char *funcs)
{
const char *objdump_str, *cross_compile;
int size, ret;
char *cmd;
cross_compile = getenv("CROSS_COMPILE");
objdump_str = "%sobjdump -wdr %s | gawk -M -v _funcs='%s' '"
"BEGIN { split(_funcs, funcs); }"
"/^$/ { func_match = 0; }"
"/<.*>:/ { "
"f = gensub(/.*<(.*)>:/, \"\\\\1\", 1);"
"for (i in funcs) {"
"if (funcs[i] == f) {"
"func_match = 1;"
"base = strtonum(\"0x\" $1);"
"break;"
"}"
"}"
"}"
"{"
"if (func_match) {"
"addr = strtonum(\"0x\" $1);"
"printf(\"%%04x \", addr - base);"
"print;"
"}"
"}' 1>&2";
/* fake snprintf() to calculate the size */
size = snprintf(NULL, 0, objdump_str, cross_compile, objname, funcs) + 1;
if (size <= 0) {
WARN("objdump string size calculation failed");
return -1;
}
cmd = malloc(size);
/* real snprintf() */
snprintf(cmd, size, objdump_str, cross_compile, objname, funcs);
ret = system(cmd);
if (ret) {
WARN("disassembly failed: %d", ret);
return -1;
} }
return 0; return 0;
} }
static int disas_warned_funcs(struct objtool_file *file)
{
struct symbol *sym;
char *funcs = NULL, *tmp;
for_each_sym(file, sym) {
if (sym->warned) {
if (!funcs) {
funcs = malloc(strlen(sym->name) + 1);
strcpy(funcs, sym->name);
} else {
tmp = malloc(strlen(funcs) + strlen(sym->name) + 2);
sprintf(tmp, "%s %s", funcs, sym->name);
free(funcs);
funcs = tmp;
}
}
}
if (funcs)
disas_funcs(funcs);
return 0;
}
struct insn_chunk {
void *addr;
struct insn_chunk *next;
};
/*
* Reduce peak RSS usage by freeing insns memory before writing the ELF file,
* which can trigger more allocations for .debug_* sections whose data hasn't
* been read yet.
*/
static void free_insns(struct objtool_file *file)
{
struct instruction *insn;
struct insn_chunk *chunks = NULL, *chunk;
for_each_insn(file, insn) {
if (!insn->idx) {
chunk = malloc(sizeof(*chunk));
chunk->addr = insn;
chunk->next = chunks;
chunks = chunk;
}
}
for (chunk = chunks; chunk; chunk = chunk->next)
free(chunk->addr);
}
int check(struct objtool_file *file) int check(struct objtool_file *file)
{ {
int ret, warnings = 0; int ret, warnings = 0;
...@@ -4537,6 +4632,8 @@ int check(struct objtool_file *file) ...@@ -4537,6 +4632,8 @@ int check(struct objtool_file *file)
init_cfi_state(&init_cfi); init_cfi_state(&init_cfi);
init_cfi_state(&func_cfi); init_cfi_state(&func_cfi);
set_func_state(&func_cfi); set_func_state(&func_cfi);
init_cfi_state(&force_undefined_cfi);
force_undefined_cfi.force_undefined = true;
if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3))) if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3)))
goto out; goto out;
...@@ -4673,6 +4770,10 @@ int check(struct objtool_file *file) ...@@ -4673,6 +4770,10 @@ int check(struct objtool_file *file)
warnings += ret; warnings += ret;
} }
free_insns(file);
if (opts.verbose)
disas_warned_funcs(file);
if (opts.stats) { if (opts.stats) {
printf("nr_insns_visited: %ld\n", nr_insns_visited); printf("nr_insns_visited: %ld\n", nr_insns_visited);
......
...@@ -32,16 +32,52 @@ static inline u32 str_hash(const char *str) ...@@ -32,16 +32,52 @@ static inline u32 str_hash(const char *str)
#define __elf_table(name) (elf->name##_hash) #define __elf_table(name) (elf->name##_hash)
#define __elf_bits(name) (elf->name##_bits) #define __elf_bits(name) (elf->name##_bits)
#define __elf_table_entry(name, key) \
__elf_table(name)[hash_min(key, __elf_bits(name))]
#define elf_hash_add(name, node, key) \ #define elf_hash_add(name, node, key) \
hlist_add_head(node, &__elf_table(name)[hash_min(key, __elf_bits(name))]) ({ \
struct elf_hash_node *__node = node; \
__node->next = __elf_table_entry(name, key); \
__elf_table_entry(name, key) = __node; \
})
static inline void __elf_hash_del(struct elf_hash_node *node,
struct elf_hash_node **head)
{
struct elf_hash_node *cur, *prev;
if (node == *head) {
*head = node->next;
return;
}
for (prev = NULL, cur = *head; cur; prev = cur, cur = cur->next) {
if (cur == node) {
prev->next = cur->next;
break;
}
}
}
#define elf_hash_del(name, node, key) \
__elf_hash_del(node, &__elf_table_entry(name, key))
#define elf_list_entry(ptr, type, member) \
({ \
typeof(ptr) __ptr = (ptr); \
__ptr ? container_of(__ptr, type, member) : NULL; \
})
#define elf_hash_for_each_possible(name, obj, member, key) \ #define elf_hash_for_each_possible(name, obj, member, key) \
hlist_for_each_entry(obj, &__elf_table(name)[hash_min(key, __elf_bits(name))], member) for (obj = elf_list_entry(__elf_table_entry(name, key), typeof(*obj), member); \
obj; \
obj = elf_list_entry(obj->member.next, typeof(*(obj)), member))
#define elf_alloc_hash(name, size) \ #define elf_alloc_hash(name, size) \
({ \ ({ \
__elf_bits(name) = max(10, ilog2(size)); \ __elf_bits(name) = max(10, ilog2(size)); \
__elf_table(name) = mmap(NULL, sizeof(struct hlist_head) << __elf_bits(name), \ __elf_table(name) = mmap(NULL, sizeof(struct elf_hash_node *) << __elf_bits(name), \
PROT_READ|PROT_WRITE, \ PROT_READ|PROT_WRITE, \
MAP_PRIVATE|MAP_ANON, -1, 0); \ MAP_PRIVATE|MAP_ANON, -1, 0); \
if (__elf_table(name) == (void *)-1L) { \ if (__elf_table(name) == (void *)-1L) { \
...@@ -233,21 +269,22 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se ...@@ -233,21 +269,22 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se
unsigned long offset, unsigned int len) unsigned long offset, unsigned int len)
{ {
struct reloc *reloc, *r = NULL; struct reloc *reloc, *r = NULL;
struct section *rsec;
unsigned long o; unsigned long o;
if (!sec->reloc) rsec = sec->rsec;
if (!rsec)
return NULL; return NULL;
sec = sec->reloc;
for_offset_range(o, offset, offset + len) { for_offset_range(o, offset, offset + len) {
elf_hash_for_each_possible(reloc, reloc, hash, elf_hash_for_each_possible(reloc, reloc, hash,
sec_offset_hash(sec, o)) { sec_offset_hash(rsec, o)) {
if (reloc->sec != sec) if (reloc->sec != rsec)
continue; continue;
if (reloc->offset >= offset && reloc->offset < offset + len) { if (reloc_offset(reloc) >= offset &&
if (!r || reloc->offset < r->offset) reloc_offset(reloc) < offset + len) {
if (!r || reloc_offset(reloc) < reloc_offset(r))
r = reloc; r = reloc;
} }
} }
...@@ -263,6 +300,11 @@ struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, uns ...@@ -263,6 +300,11 @@ struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, uns
return find_reloc_by_dest_range(elf, sec, offset, 1); return find_reloc_by_dest_range(elf, sec, offset, 1);
} }
static bool is_dwarf_section(struct section *sec)
{
return !strncmp(sec->name, ".debug_", 7);
}
static int read_sections(struct elf *elf) static int read_sections(struct elf *elf)
{ {
Elf_Scn *s = NULL; Elf_Scn *s = NULL;
...@@ -293,7 +335,6 @@ static int read_sections(struct elf *elf) ...@@ -293,7 +335,6 @@ static int read_sections(struct elf *elf)
sec = &elf->section_data[i]; sec = &elf->section_data[i];
INIT_LIST_HEAD(&sec->symbol_list); INIT_LIST_HEAD(&sec->symbol_list);
INIT_LIST_HEAD(&sec->reloc_list);
s = elf_getscn(elf->elf, i); s = elf_getscn(elf->elf, i);
if (!s) { if (!s) {
...@@ -314,7 +355,7 @@ static int read_sections(struct elf *elf) ...@@ -314,7 +355,7 @@ static int read_sections(struct elf *elf)
return -1; return -1;
} }
if (sec->sh.sh_size != 0) { if (sec->sh.sh_size != 0 && !is_dwarf_section(sec)) {
sec->data = elf_getdata(s, NULL); sec->data = elf_getdata(s, NULL);
if (!sec->data) { if (!sec->data) {
WARN_ELF("elf_getdata"); WARN_ELF("elf_getdata");
...@@ -328,12 +369,12 @@ static int read_sections(struct elf *elf) ...@@ -328,12 +369,12 @@ static int read_sections(struct elf *elf)
} }
} }
if (sec->sh.sh_flags & SHF_EXECINSTR)
elf->text_size += sec->sh.sh_size;
list_add_tail(&sec->list, &elf->sections); list_add_tail(&sec->list, &elf->sections);
elf_hash_add(section, &sec->hash, sec->idx); elf_hash_add(section, &sec->hash, sec->idx);
elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name));
if (is_reloc_sec(sec))
elf->num_relocs += sec_num_entries(sec);
} }
if (opts.stats) { if (opts.stats) {
...@@ -356,7 +397,6 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym) ...@@ -356,7 +397,6 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)
struct rb_node *pnode; struct rb_node *pnode;
struct symbol *iter; struct symbol *iter;
INIT_LIST_HEAD(&sym->reloc_list);
INIT_LIST_HEAD(&sym->pv_target); INIT_LIST_HEAD(&sym->pv_target);
sym->alias = sym; sym->alias = sym;
...@@ -407,7 +447,7 @@ static int read_symbols(struct elf *elf) ...@@ -407,7 +447,7 @@ static int read_symbols(struct elf *elf)
if (symtab_shndx) if (symtab_shndx)
shndx_data = symtab_shndx->data; shndx_data = symtab_shndx->data;
symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; symbols_nr = sec_num_entries(symtab);
} else { } else {
/* /*
* A missing symbol table is actually possible if it's an empty * A missing symbol table is actually possible if it's an empty
...@@ -533,52 +573,17 @@ static int read_symbols(struct elf *elf) ...@@ -533,52 +573,17 @@ static int read_symbols(struct elf *elf)
return -1; return -1;
} }
static struct section *elf_create_reloc_section(struct elf *elf,
struct section *base,
int reltype);
int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
unsigned int type, struct symbol *sym, s64 addend)
{
struct reloc *reloc;
if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA))
return -1;
reloc = malloc(sizeof(*reloc));
if (!reloc) {
perror("malloc");
return -1;
}
memset(reloc, 0, sizeof(*reloc));
reloc->sec = sec->reloc;
reloc->offset = offset;
reloc->type = type;
reloc->sym = sym;
reloc->addend = addend;
list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list);
list_add_tail(&reloc->list, &sec->reloc->reloc_list);
elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
sec->reloc->sh.sh_size += sec->reloc->sh.sh_entsize;
sec->reloc->changed = true;
return 0;
}
/* /*
* Ensure that any reloc section containing references to @sym is marked * @sym's idx has changed. Update the relocs which reference it.
* changed such that it will get re-generated in elf_rebuild_reloc_sections()
* with the new symbol index.
*/ */
static void elf_dirty_reloc_sym(struct elf *elf, struct symbol *sym) static int elf_update_sym_relocs(struct elf *elf, struct symbol *sym)
{ {
struct reloc *reloc; struct reloc *reloc;
list_for_each_entry(reloc, &sym->reloc_list, sym_reloc_entry) for (reloc = sym->relocs; reloc; reloc = reloc->sym_next_reloc)
reloc->sec->changed = true; set_reloc_sym(elf, reloc, reloc->sym->idx);
return 0;
} }
/* /*
...@@ -655,7 +660,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, ...@@ -655,7 +660,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
symtab_data->d_align = 1; symtab_data->d_align = 1;
symtab_data->d_type = ELF_T_SYM; symtab_data->d_type = ELF_T_SYM;
symtab->changed = true; mark_sec_changed(elf, symtab, true);
symtab->truncate = true; symtab->truncate = true;
if (t) { if (t) {
...@@ -670,7 +675,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, ...@@ -670,7 +675,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,
shndx_data->d_align = sizeof(Elf32_Word); shndx_data->d_align = sizeof(Elf32_Word);
shndx_data->d_type = ELF_T_WORD; shndx_data->d_type = ELF_T_WORD;
symtab_shndx->changed = true; mark_sec_changed(elf, symtab_shndx, true);
symtab_shndx->truncate = true; symtab_shndx->truncate = true;
} }
...@@ -734,7 +739,7 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym) ...@@ -734,7 +739,7 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
return NULL; return NULL;
} }
new_idx = symtab->sh.sh_size / symtab->sh.sh_entsize; new_idx = sec_num_entries(symtab);
if (GELF_ST_BIND(sym->sym.st_info) != STB_LOCAL) if (GELF_ST_BIND(sym->sym.st_info) != STB_LOCAL)
goto non_local; goto non_local;
...@@ -746,18 +751,19 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym) ...@@ -746,18 +751,19 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
first_non_local = symtab->sh.sh_info; first_non_local = symtab->sh.sh_info;
old = find_symbol_by_index(elf, first_non_local); old = find_symbol_by_index(elf, first_non_local);
if (old) { if (old) {
old->idx = new_idx;
hlist_del(&old->hash);
elf_hash_add(symbol, &old->hash, old->idx);
elf_dirty_reloc_sym(elf, old); elf_hash_del(symbol, &old->hash, old->idx);
elf_hash_add(symbol, &old->hash, new_idx);
old->idx = new_idx;
if (elf_update_symbol(elf, symtab, symtab_shndx, old)) { if (elf_update_symbol(elf, symtab, symtab_shndx, old)) {
WARN("elf_update_symbol move"); WARN("elf_update_symbol move");
return NULL; return NULL;
} }
if (elf_update_sym_relocs(elf, old))
return NULL;
new_idx = first_non_local; new_idx = first_non_local;
} }
...@@ -774,11 +780,11 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym) ...@@ -774,11 +780,11 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)
} }
symtab->sh.sh_size += symtab->sh.sh_entsize; symtab->sh.sh_size += symtab->sh.sh_entsize;
symtab->changed = true; mark_sec_changed(elf, symtab, true);
if (symtab_shndx) { if (symtab_shndx) {
symtab_shndx->sh.sh_size += sizeof(Elf32_Word); symtab_shndx->sh.sh_size += sizeof(Elf32_Word);
symtab_shndx->changed = true; mark_sec_changed(elf, symtab_shndx, true);
} }
return sym; return sym;
...@@ -841,13 +847,57 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size) ...@@ -841,13 +847,57 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size)
return sym; return sym;
} }
int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
unsigned long offset, unsigned int type, unsigned int reloc_idx,
struct section *insn_sec, unsigned long insn_off) unsigned long offset, struct symbol *sym,
s64 addend, unsigned int type)
{
struct reloc *reloc, empty = { 0 };
if (reloc_idx >= sec_num_entries(rsec)) {
WARN("%s: bad reloc_idx %u for %s with %d relocs",
__func__, reloc_idx, rsec->name, sec_num_entries(rsec));
return NULL;
}
reloc = &rsec->relocs[reloc_idx];
if (memcmp(reloc, &empty, sizeof(empty))) {
WARN("%s: %s: reloc %d already initialized!",
__func__, rsec->name, reloc_idx);
return NULL;
}
reloc->sec = rsec;
reloc->sym = sym;
set_reloc_offset(elf, reloc, offset);
set_reloc_sym(elf, reloc, sym->idx);
set_reloc_type(elf, reloc, type);
set_reloc_addend(elf, reloc, addend);
elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
reloc->sym_next_reloc = sym->relocs;
sym->relocs = reloc;
return reloc;
}
struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
unsigned long offset,
unsigned int reloc_idx,
struct section *insn_sec,
unsigned long insn_off)
{ {
struct symbol *sym = insn_sec->sym; struct symbol *sym = insn_sec->sym;
int addend = insn_off; int addend = insn_off;
if (!(insn_sec->sh.sh_flags & SHF_EXECINSTR)) {
WARN("bad call to %s() for data symbol %s",
__func__, sym->name);
return NULL;
}
if (!sym) { if (!sym) {
/* /*
* Due to how weak functions work, we must use section based * Due to how weak functions work, we must use section based
...@@ -857,108 +907,86 @@ int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, ...@@ -857,108 +907,86 @@ int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
*/ */
sym = elf_create_section_symbol(elf, insn_sec); sym = elf_create_section_symbol(elf, insn_sec);
if (!sym) if (!sym)
return -1; return NULL;
insn_sec->sym = sym; insn_sec->sym = sym;
} }
return elf_add_reloc(elf, sec, offset, type, sym, addend); return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend,
elf_text_rela_type(elf));
} }
static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
unsigned long offset,
unsigned int reloc_idx,
struct symbol *sym,
s64 addend)
{ {
if (!gelf_getrel(sec->data, i, &reloc->rel)) { if (sym->sec && (sec->sh.sh_flags & SHF_EXECINSTR)) {
WARN_ELF("gelf_getrel"); WARN("bad call to %s() for text symbol %s",
return -1; __func__, sym->name);
return NULL;
} }
reloc->type = GELF_R_TYPE(reloc->rel.r_info);
reloc->addend = 0;
reloc->offset = reloc->rel.r_offset;
*symndx = GELF_R_SYM(reloc->rel.r_info);
return 0;
}
static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend,
{ elf_data_rela_type(elf));
if (!gelf_getrela(sec->data, i, &reloc->rela)) {
WARN_ELF("gelf_getrela");
return -1;
}
reloc->type = GELF_R_TYPE(reloc->rela.r_info);
reloc->addend = reloc->rela.r_addend;
reloc->offset = reloc->rela.r_offset;
*symndx = GELF_R_SYM(reloc->rela.r_info);
return 0;
} }
static int read_relocs(struct elf *elf) static int read_relocs(struct elf *elf)
{ {
unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; unsigned long nr_reloc, max_reloc = 0;
struct section *sec; struct section *rsec;
struct reloc *reloc; struct reloc *reloc;
unsigned int symndx; unsigned int symndx;
struct symbol *sym; struct symbol *sym;
int i; int i;
if (!elf_alloc_hash(reloc, elf->text_size / 16)) if (!elf_alloc_hash(reloc, elf->num_relocs))
return -1; return -1;
list_for_each_entry(sec, &elf->sections, list) { list_for_each_entry(rsec, &elf->sections, list) {
if ((sec->sh.sh_type != SHT_RELA) && if (!is_reloc_sec(rsec))
(sec->sh.sh_type != SHT_REL))
continue; continue;
sec->base = find_section_by_index(elf, sec->sh.sh_info); rsec->base = find_section_by_index(elf, rsec->sh.sh_info);
if (!sec->base) { if (!rsec->base) {
WARN("can't find base section for reloc section %s", WARN("can't find base section for reloc section %s",
sec->name); rsec->name);
return -1; return -1;
} }
sec->base->reloc = sec; rsec->base->rsec = rsec;
nr_reloc = 0; nr_reloc = 0;
sec->reloc_data = calloc(sec->sh.sh_size / sec->sh.sh_entsize, sizeof(*reloc)); rsec->relocs = calloc(sec_num_entries(rsec), sizeof(*reloc));
if (!sec->reloc_data) { if (!rsec->relocs) {
perror("calloc"); perror("calloc");
return -1; return -1;
} }
for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { for (i = 0; i < sec_num_entries(rsec); i++) {
reloc = &sec->reloc_data[i]; reloc = &rsec->relocs[i];
switch (sec->sh.sh_type) {
case SHT_REL:
if (read_rel_reloc(sec, i, reloc, &symndx))
return -1;
break;
case SHT_RELA:
if (read_rela_reloc(sec, i, reloc, &symndx))
return -1;
break;
default: return -1;
}
reloc->sec = sec; reloc->sec = rsec;
reloc->idx = i; symndx = reloc_sym(reloc);
reloc->sym = sym = find_symbol_by_index(elf, symndx); reloc->sym = sym = find_symbol_by_index(elf, symndx);
if (!reloc->sym) { if (!reloc->sym) {
WARN("can't find reloc entry symbol %d for %s", WARN("can't find reloc entry symbol %d for %s",
symndx, sec->name); symndx, rsec->name);
return -1; return -1;
} }
list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list);
list_add_tail(&reloc->list, &sec->reloc_list);
elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
reloc->sym_next_reloc = sym->relocs;
sym->relocs = reloc;
nr_reloc++; nr_reloc++;
} }
max_reloc = max(max_reloc, nr_reloc); max_reloc = max(max_reloc, nr_reloc);
tot_reloc += nr_reloc;
} }
if (opts.stats) { if (opts.stats) {
printf("max_reloc: %lu\n", max_reloc); printf("max_reloc: %lu\n", max_reloc);
printf("tot_reloc: %lu\n", tot_reloc); printf("num_relocs: %lu\n", elf->num_relocs);
printf("reloc_bits: %d\n", elf->reloc_bits); printf("reloc_bits: %d\n", elf->reloc_bits);
} }
...@@ -1053,13 +1081,14 @@ static int elf_add_string(struct elf *elf, struct section *strtab, char *str) ...@@ -1053,13 +1081,14 @@ static int elf_add_string(struct elf *elf, struct section *strtab, char *str)
len = strtab->sh.sh_size; len = strtab->sh.sh_size;
strtab->sh.sh_size += data->d_size; strtab->sh.sh_size += data->d_size;
strtab->changed = true;
mark_sec_changed(elf, strtab, true);
return len; return len;
} }
struct section *elf_create_section(struct elf *elf, const char *name, struct section *elf_create_section(struct elf *elf, const char *name,
unsigned int sh_flags, size_t entsize, int nr) size_t entsize, unsigned int nr)
{ {
struct section *sec, *shstrtab; struct section *sec, *shstrtab;
size_t size = entsize * nr; size_t size = entsize * nr;
...@@ -1073,7 +1102,6 @@ struct section *elf_create_section(struct elf *elf, const char *name, ...@@ -1073,7 +1102,6 @@ struct section *elf_create_section(struct elf *elf, const char *name,
memset(sec, 0, sizeof(*sec)); memset(sec, 0, sizeof(*sec));
INIT_LIST_HEAD(&sec->symbol_list); INIT_LIST_HEAD(&sec->symbol_list);
INIT_LIST_HEAD(&sec->reloc_list);
s = elf_newscn(elf->elf); s = elf_newscn(elf->elf);
if (!s) { if (!s) {
...@@ -1088,7 +1116,6 @@ struct section *elf_create_section(struct elf *elf, const char *name, ...@@ -1088,7 +1116,6 @@ struct section *elf_create_section(struct elf *elf, const char *name,
} }
sec->idx = elf_ndxscn(s); sec->idx = elf_ndxscn(s);
sec->changed = true;
sec->data = elf_newdata(s); sec->data = elf_newdata(s);
if (!sec->data) { if (!sec->data) {
...@@ -1117,7 +1144,7 @@ struct section *elf_create_section(struct elf *elf, const char *name, ...@@ -1117,7 +1144,7 @@ struct section *elf_create_section(struct elf *elf, const char *name,
sec->sh.sh_entsize = entsize; sec->sh.sh_entsize = entsize;
sec->sh.sh_type = SHT_PROGBITS; sec->sh.sh_type = SHT_PROGBITS;
sec->sh.sh_addralign = 1; sec->sh.sh_addralign = 1;
sec->sh.sh_flags = SHF_ALLOC | sh_flags; sec->sh.sh_flags = SHF_ALLOC;
/* Add section name to .shstrtab (or .strtab for Clang) */ /* Add section name to .shstrtab (or .strtab for Clang) */
shstrtab = find_section_by_name(elf, ".shstrtab"); shstrtab = find_section_by_name(elf, ".shstrtab");
...@@ -1135,158 +1162,66 @@ struct section *elf_create_section(struct elf *elf, const char *name, ...@@ -1135,158 +1162,66 @@ struct section *elf_create_section(struct elf *elf, const char *name,
elf_hash_add(section, &sec->hash, sec->idx); elf_hash_add(section, &sec->hash, sec->idx);
elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name));
elf->changed = true; mark_sec_changed(elf, sec, true);
return sec; return sec;
} }
static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) static struct section *elf_create_rela_section(struct elf *elf,
struct section *sec,
unsigned int reloc_nr)
{ {
char *relocname; struct section *rsec;
struct section *sec; char *rsec_name;
relocname = malloc(strlen(base->name) + strlen(".rel") + 1); rsec_name = malloc(strlen(sec->name) + strlen(".rela") + 1);
if (!relocname) { if (!rsec_name) {
perror("malloc"); perror("malloc");
return NULL; return NULL;
} }
strcpy(relocname, ".rel"); strcpy(rsec_name, ".rela");
strcat(relocname, base->name); strcat(rsec_name, sec->name);
sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0); rsec = elf_create_section(elf, rsec_name, elf_rela_size(elf), reloc_nr);
free(relocname); free(rsec_name);
if (!sec) if (!rsec)
return NULL; return NULL;
base->reloc = sec; rsec->data->d_type = ELF_T_RELA;
sec->base = base; rsec->sh.sh_type = SHT_RELA;
rsec->sh.sh_addralign = elf_addr_size(elf);
rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
rsec->sh.sh_info = sec->idx;
rsec->sh.sh_flags = SHF_INFO_LINK;
sec->sh.sh_type = SHT_REL; rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc));
sec->sh.sh_addralign = 8; if (!rsec->relocs) {
sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; perror("calloc");
sec->sh.sh_info = base->idx; return NULL;
sec->sh.sh_flags = SHF_INFO_LINK; }
return sec; sec->rsec = rsec;
rsec->base = sec;
return rsec;
} }
static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) struct section *elf_create_section_pair(struct elf *elf, const char *name,
size_t entsize, unsigned int nr,
unsigned int reloc_nr)
{ {
char *relocname;
struct section *sec; struct section *sec;
int addrsize = elf_class_addrsize(elf);
relocname = malloc(strlen(base->name) + strlen(".rela") + 1); sec = elf_create_section(elf, name, entsize, nr);
if (!relocname) {
perror("malloc");
return NULL;
}
strcpy(relocname, ".rela");
strcat(relocname, base->name);
if (addrsize == sizeof(u32))
sec = elf_create_section(elf, relocname, 0, sizeof(Elf32_Rela), 0);
else
sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0);
free(relocname);
if (!sec) if (!sec)
return NULL; return NULL;
base->reloc = sec; if (!elf_create_rela_section(elf, sec, reloc_nr))
sec->base = base; return NULL;
sec->sh.sh_type = SHT_RELA;
sec->sh.sh_addralign = addrsize;
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; return sec;
} }
static struct section *elf_create_reloc_section(struct elf *elf,
struct section *base,
int reltype)
{
switch (reltype) {
case SHT_REL: return elf_create_rel_reloc_section(elf, base);
case SHT_RELA: return elf_create_rela_reloc_section(elf, base);
default: return NULL;
}
}
static int elf_rebuild_rel_reloc_section(struct section *sec)
{
struct reloc *reloc;
int idx = 0;
void *buf;
/* Allocate a buffer for relocations */
buf = malloc(sec->sh.sh_size);
if (!buf) {
perror("malloc");
return -1;
}
sec->data->d_buf = buf;
sec->data->d_size = sec->sh.sh_size;
sec->data->d_type = ELF_T_REL;
idx = 0;
list_for_each_entry(reloc, &sec->reloc_list, list) {
reloc->rel.r_offset = reloc->offset;
reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
if (!gelf_update_rel(sec->data, idx, &reloc->rel)) {
WARN_ELF("gelf_update_rel");
return -1;
}
idx++;
}
return 0;
}
static int elf_rebuild_rela_reloc_section(struct section *sec)
{
struct reloc *reloc;
int idx = 0;
void *buf;
/* Allocate a buffer for relocations with addends */
buf = malloc(sec->sh.sh_size);
if (!buf) {
perror("malloc");
return -1;
}
sec->data->d_buf = buf;
sec->data->d_size = sec->sh.sh_size;
sec->data->d_type = ELF_T_RELA;
idx = 0;
list_for_each_entry(reloc, &sec->reloc_list, list) {
reloc->rela.r_offset = reloc->offset;
reloc->rela.r_addend = reloc->addend;
reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
if (!gelf_update_rela(sec->data, idx, &reloc->rela)) {
WARN_ELF("gelf_update_rela");
return -1;
}
idx++;
}
return 0;
}
static int elf_rebuild_reloc_section(struct elf *elf, struct section *sec)
{
switch (sec->sh.sh_type) {
case SHT_REL: return elf_rebuild_rel_reloc_section(sec);
case SHT_RELA: return elf_rebuild_rela_reloc_section(sec);
default: return -1;
}
}
int elf_write_insn(struct elf *elf, struct section *sec, int elf_write_insn(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int len, unsigned long offset, unsigned int len,
const char *insn) const char *insn)
...@@ -1299,37 +1234,8 @@ int elf_write_insn(struct elf *elf, struct section *sec, ...@@ -1299,37 +1234,8 @@ int elf_write_insn(struct elf *elf, struct section *sec,
} }
memcpy(data->d_buf + offset, insn, len); memcpy(data->d_buf + offset, insn, len);
elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY);
elf->changed = true; mark_sec_changed(elf, sec, true);
return 0;
}
int elf_write_reloc(struct elf *elf, struct reloc *reloc)
{
struct section *sec = reloc->sec;
if (sec->sh.sh_type == SHT_REL) {
reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
reloc->rel.r_offset = reloc->offset;
if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) {
WARN_ELF("gelf_update_rel");
return -1;
}
} else {
reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
reloc->rela.r_addend = reloc->addend;
reloc->rela.r_offset = reloc->offset;
if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) {
WARN_ELF("gelf_update_rela");
return -1;
}
}
elf->changed = true;
return 0; return 0;
} }
...@@ -1401,25 +1307,20 @@ int elf_write(struct elf *elf) ...@@ -1401,25 +1307,20 @@ int elf_write(struct elf *elf)
if (sec->truncate) if (sec->truncate)
elf_truncate_section(elf, sec); elf_truncate_section(elf, sec);
if (sec->changed) { if (sec_changed(sec)) {
s = elf_getscn(elf->elf, sec->idx); s = elf_getscn(elf->elf, sec->idx);
if (!s) { if (!s) {
WARN_ELF("elf_getscn"); WARN_ELF("elf_getscn");
return -1; return -1;
} }
/* Note this also flags the section dirty */
if (!gelf_update_shdr(s, &sec->sh)) { if (!gelf_update_shdr(s, &sec->sh)) {
WARN_ELF("gelf_update_shdr"); WARN_ELF("gelf_update_shdr");
return -1; return -1;
} }
if (sec->base && mark_sec_changed(elf, sec, false);
elf_rebuild_reloc_section(elf, sec)) {
WARN("elf_rebuild_reloc_section");
return -1;
}
sec->changed = false;
elf->changed = true;
} }
} }
...@@ -1439,30 +1340,14 @@ int elf_write(struct elf *elf) ...@@ -1439,30 +1340,14 @@ int elf_write(struct elf *elf)
void elf_close(struct elf *elf) void elf_close(struct elf *elf)
{ {
struct section *sec, *tmpsec;
struct symbol *sym, *tmpsym;
struct reloc *reloc, *tmpreloc;
if (elf->elf) if (elf->elf)
elf_end(elf->elf); elf_end(elf->elf);
if (elf->fd > 0) if (elf->fd > 0)
close(elf->fd); close(elf->fd);
list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) { /*
list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { * NOTE: All remaining allocations are leaked on purpose. Objtool is
list_del(&sym->list); * about to exit anyway.
hash_del(&sym->hash); */
}
list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) {
list_del(&reloc->list);
hash_del(&reloc->hash);
}
list_del(&sec->list);
free(sec->reloc_data);
}
free(elf->symbol_data);
free(elf->section_data);
free(elf);
} }
...@@ -37,6 +37,7 @@ struct opts { ...@@ -37,6 +37,7 @@ struct opts {
bool no_unreachable; bool no_unreachable;
bool sec_address; bool sec_address;
bool stats; bool stats;
bool verbose;
}; };
extern struct opts opts; extern struct opts opts;
......
...@@ -36,6 +36,7 @@ struct cfi_state { ...@@ -36,6 +36,7 @@ struct cfi_state {
bool drap; bool drap;
bool signal; bool signal;
bool end; bool end;
bool force_undefined;
}; };
#endif /* _OBJTOOL_CFI_H */ #endif /* _OBJTOOL_CFI_H */
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/hashtable.h> #include <linux/hashtable.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/jhash.h> #include <linux/jhash.h>
#include <arch/elf.h>
#ifdef LIBELF_USE_DEPRECATED #ifdef LIBELF_USE_DEPRECATED
# define elf_getshdrnum elf_getshnum # define elf_getshdrnum elf_getshnum
...@@ -25,28 +26,31 @@ ...@@ -25,28 +26,31 @@
#define ELF_C_READ_MMAP ELF_C_READ #define ELF_C_READ_MMAP ELF_C_READ
#endif #endif
struct elf_hash_node {
struct elf_hash_node *next;
};
struct section { struct section {
struct list_head list; struct list_head list;
struct hlist_node hash; struct elf_hash_node hash;
struct hlist_node name_hash; struct elf_hash_node name_hash;
GElf_Shdr sh; GElf_Shdr sh;
struct rb_root_cached symbol_tree; struct rb_root_cached symbol_tree;
struct list_head symbol_list; struct list_head symbol_list;
struct list_head reloc_list; struct section *base, *rsec;
struct section *base, *reloc;
struct symbol *sym; struct symbol *sym;
Elf_Data *data; Elf_Data *data;
char *name; char *name;
int idx; int idx;
bool changed, text, rodata, noinstr, init, truncate; bool _changed, text, rodata, noinstr, init, truncate;
struct reloc *reloc_data; struct reloc *relocs;
}; };
struct symbol { struct symbol {
struct list_head list; struct list_head list;
struct rb_node node; struct rb_node node;
struct hlist_node hash; struct elf_hash_node hash;
struct hlist_node name_hash; struct elf_hash_node name_hash;
GElf_Sym sym; GElf_Sym sym;
struct section *sec; struct section *sec;
char *name; char *name;
...@@ -61,37 +65,27 @@ struct symbol { ...@@ -61,37 +65,27 @@ struct symbol {
u8 return_thunk : 1; u8 return_thunk : 1;
u8 fentry : 1; u8 fentry : 1;
u8 profiling_func : 1; u8 profiling_func : 1;
u8 warned : 1;
struct list_head pv_target; struct list_head pv_target;
struct list_head reloc_list; struct reloc *relocs;
}; };
struct reloc { struct reloc {
struct list_head list; struct elf_hash_node hash;
struct hlist_node hash;
union {
GElf_Rela rela;
GElf_Rel rel;
};
struct section *sec; struct section *sec;
struct symbol *sym; struct symbol *sym;
struct list_head sym_reloc_entry; struct reloc *sym_next_reloc;
unsigned long offset;
unsigned int type;
s64 addend;
int idx;
bool jump_table_start;
}; };
#define ELF_HASH_BITS 20
struct elf { struct elf {
Elf *elf; Elf *elf;
GElf_Ehdr ehdr; GElf_Ehdr ehdr;
int fd; int fd;
bool changed; bool changed;
char *name; char *name;
unsigned int text_size, num_files; unsigned int num_files;
struct list_head sections; struct list_head sections;
unsigned long num_relocs;
int symbol_bits; int symbol_bits;
int symbol_name_bits; int symbol_name_bits;
...@@ -99,44 +93,54 @@ struct elf { ...@@ -99,44 +93,54 @@ struct elf {
int section_name_bits; int section_name_bits;
int reloc_bits; int reloc_bits;
struct hlist_head *symbol_hash; struct elf_hash_node **symbol_hash;
struct hlist_head *symbol_name_hash; struct elf_hash_node **symbol_name_hash;
struct hlist_head *section_hash; struct elf_hash_node **section_hash;
struct hlist_head *section_name_hash; struct elf_hash_node **section_name_hash;
struct hlist_head *reloc_hash; struct elf_hash_node **reloc_hash;
struct section *section_data; struct section *section_data;
struct symbol *symbol_data; struct symbol *symbol_data;
}; };
#define OFFSET_STRIDE_BITS 4 struct elf *elf_open_read(const char *name, int flags);
#define OFFSET_STRIDE (1UL << OFFSET_STRIDE_BITS)
#define OFFSET_STRIDE_MASK (~(OFFSET_STRIDE - 1))
#define for_offset_range(_offset, _start, _end) \
for (_offset = ((_start) & OFFSET_STRIDE_MASK); \
_offset >= ((_start) & OFFSET_STRIDE_MASK) && \
_offset <= ((_end) & OFFSET_STRIDE_MASK); \
_offset += OFFSET_STRIDE)
static inline u32 sec_offset_hash(struct section *sec, unsigned long offset) struct section *elf_create_section(struct elf *elf, const char *name,
{ size_t entsize, unsigned int nr);
u32 ol, oh, idx = sec->idx; struct section *elf_create_section_pair(struct elf *elf, const char *name,
size_t entsize, unsigned int nr,
unsigned int reloc_nr);
offset &= OFFSET_STRIDE_MASK; struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size);
ol = offset; struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
oh = (offset >> 16) >> 16; unsigned long offset,
unsigned int reloc_idx,
struct section *insn_sec,
unsigned long insn_off);
__jhash_mix(ol, oh, idx); struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
unsigned long offset,
unsigned int reloc_idx,
struct symbol *sym,
s64 addend);
return ol; int elf_write_insn(struct elf *elf, struct section *sec,
} unsigned long offset, unsigned int len,
const char *insn);
int elf_write(struct elf *elf);
void elf_close(struct elf *elf);
static inline u32 reloc_hash(struct reloc *reloc) struct section *find_section_by_name(const struct elf *elf, const char *name);
{ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
return sec_offset_hash(reloc->sec, reloc->offset); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
} struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
int find_symbol_hole_containing(const struct section *sec, unsigned long offset);
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
unsigned long offset, unsigned int len);
struct symbol *find_func_containing(struct section *sec, unsigned long offset);
/* /*
* Try to see if it's a whole archive (vmlinux.o or module). * Try to see if it's a whole archive (vmlinux.o or module).
...@@ -148,42 +152,147 @@ static inline bool has_multiple_files(struct elf *elf) ...@@ -148,42 +152,147 @@ static inline bool has_multiple_files(struct elf *elf)
return elf->num_files > 1; return elf->num_files > 1;
} }
static inline int elf_class_addrsize(struct elf *elf) static inline size_t elf_addr_size(struct elf *elf)
{ {
if (elf->ehdr.e_ident[EI_CLASS] == ELFCLASS32) return elf->ehdr.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
return sizeof(u32);
else
return sizeof(u64);
} }
struct elf *elf_open_read(const char *name, int flags); static inline size_t elf_rela_size(struct elf *elf)
struct section *elf_create_section(struct elf *elf, const char *name, unsigned int sh_flags, size_t entsize, int nr); {
return elf_addr_size(elf) == 4 ? sizeof(Elf32_Rela) : sizeof(Elf64_Rela);
}
struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size); static inline unsigned int elf_data_rela_type(struct elf *elf)
{
return elf_addr_size(elf) == 4 ? R_DATA32 : R_DATA64;
}
int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, static inline unsigned int elf_text_rela_type(struct elf *elf)
unsigned int type, struct symbol *sym, s64 addend); {
int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, return elf_addr_size(elf) == 4 ? R_TEXT32 : R_TEXT64;
unsigned long offset, unsigned int type, }
struct section *insn_sec, unsigned long insn_off);
int elf_write_insn(struct elf *elf, struct section *sec, static inline bool is_reloc_sec(struct section *sec)
unsigned long offset, unsigned int len, {
const char *insn); return sec->sh.sh_type == SHT_RELA || sec->sh.sh_type == SHT_REL;
int elf_write_reloc(struct elf *elf, struct reloc *reloc); }
int elf_write(struct elf *elf);
void elf_close(struct elf *elf);
struct section *find_section_by_name(const struct elf *elf, const char *name); static inline bool sec_changed(struct section *sec)
struct symbol *find_func_by_offset(struct section *sec, unsigned long offset); {
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); return sec->_changed;
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name); }
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
int find_symbol_hole_containing(const struct section *sec, unsigned long offset); static inline void mark_sec_changed(struct elf *elf, struct section *sec,
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset); bool changed)
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, {
unsigned long offset, unsigned int len); sec->_changed = changed;
struct symbol *find_func_containing(struct section *sec, unsigned long offset); elf->changed |= changed;
}
static inline unsigned int sec_num_entries(struct section *sec)
{
return sec->sh.sh_size / sec->sh.sh_entsize;
}
static inline unsigned int reloc_idx(struct reloc *reloc)
{
return reloc - reloc->sec->relocs;
}
static inline void *reloc_rel(struct reloc *reloc)
{
struct section *rsec = reloc->sec;
return rsec->data->d_buf + (reloc_idx(reloc) * rsec->sh.sh_entsize);
}
static inline bool is_32bit_reloc(struct reloc *reloc)
{
/*
* Elf32_Rel: 8 bytes
* Elf32_Rela: 12 bytes
* Elf64_Rel: 16 bytes
* Elf64_Rela: 24 bytes
*/
return reloc->sec->sh.sh_entsize < 16;
}
#define __get_reloc_field(reloc, field) \
({ \
is_32bit_reloc(reloc) ? \
((Elf32_Rela *)reloc_rel(reloc))->field : \
((Elf64_Rela *)reloc_rel(reloc))->field; \
})
#define __set_reloc_field(reloc, field, val) \
({ \
if (is_32bit_reloc(reloc)) \
((Elf32_Rela *)reloc_rel(reloc))->field = val; \
else \
((Elf64_Rela *)reloc_rel(reloc))->field = val; \
})
static inline u64 reloc_offset(struct reloc *reloc)
{
return __get_reloc_field(reloc, r_offset);
}
static inline void set_reloc_offset(struct elf *elf, struct reloc *reloc, u64 offset)
{
__set_reloc_field(reloc, r_offset, offset);
mark_sec_changed(elf, reloc->sec, true);
}
static inline s64 reloc_addend(struct reloc *reloc)
{
return __get_reloc_field(reloc, r_addend);
}
static inline void set_reloc_addend(struct elf *elf, struct reloc *reloc, s64 addend)
{
__set_reloc_field(reloc, r_addend, addend);
mark_sec_changed(elf, reloc->sec, true);
}
static inline unsigned int reloc_sym(struct reloc *reloc)
{
u64 info = __get_reloc_field(reloc, r_info);
return is_32bit_reloc(reloc) ?
ELF32_R_SYM(info) :
ELF64_R_SYM(info);
}
static inline unsigned int reloc_type(struct reloc *reloc)
{
u64 info = __get_reloc_field(reloc, r_info);
return is_32bit_reloc(reloc) ?
ELF32_R_TYPE(info) :
ELF64_R_TYPE(info);
}
static inline void set_reloc_sym(struct elf *elf, struct reloc *reloc, unsigned int sym)
{
u64 info = is_32bit_reloc(reloc) ?
ELF32_R_INFO(sym, reloc_type(reloc)) :
ELF64_R_INFO(sym, reloc_type(reloc));
__set_reloc_field(reloc, r_info, info);
mark_sec_changed(elf, reloc->sec, true);
}
static inline void set_reloc_type(struct elf *elf, struct reloc *reloc, unsigned int type)
{
u64 info = is_32bit_reloc(reloc) ?
ELF32_R_INFO(reloc_sym(reloc), type) :
ELF64_R_INFO(reloc_sym(reloc), type);
__set_reloc_field(reloc, r_info, info);
mark_sec_changed(elf, reloc->sec, true);
}
#define for_each_sec(file, sec) \ #define for_each_sec(file, sec) \
list_for_each_entry(sec, &file->elf->sections, list) list_for_each_entry(sec, &file->elf->sections, list)
...@@ -197,4 +306,44 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset); ...@@ -197,4 +306,44 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset);
for_each_sec(file, __sec) \ for_each_sec(file, __sec) \
sec_for_each_sym(__sec, sym) sec_for_each_sym(__sec, sym)
#define for_each_reloc(rsec, reloc) \
for (int __i = 0, __fake = 1; __fake; __fake = 0) \
for (reloc = rsec->relocs; \
__i < sec_num_entries(rsec); \
__i++, reloc++)
#define for_each_reloc_from(rsec, reloc) \
for (int __i = reloc_idx(reloc); \
__i < sec_num_entries(rsec); \
__i++, reloc++)
#define OFFSET_STRIDE_BITS 4
#define OFFSET_STRIDE (1UL << OFFSET_STRIDE_BITS)
#define OFFSET_STRIDE_MASK (~(OFFSET_STRIDE - 1))
#define for_offset_range(_offset, _start, _end) \
for (_offset = ((_start) & OFFSET_STRIDE_MASK); \
_offset >= ((_start) & OFFSET_STRIDE_MASK) && \
_offset <= ((_end) & OFFSET_STRIDE_MASK); \
_offset += OFFSET_STRIDE)
static inline u32 sec_offset_hash(struct section *sec, unsigned long offset)
{
u32 ol, oh, idx = sec->idx;
offset &= OFFSET_STRIDE_MASK;
ol = offset;
oh = (offset >> 16) >> 16;
__jhash_mix(ol, oh, idx);
return ol;
}
static inline u32 reloc_hash(struct reloc *reloc)
{
return sec_offset_hash(reloc->sec, reloc_offset(reloc));
}
#endif /* _OBJTOOL_ELF_H */ #endif /* _OBJTOOL_ELF_H */
...@@ -55,15 +55,22 @@ static inline char *offstr(struct section *sec, unsigned long offset) ...@@ -55,15 +55,22 @@ static inline char *offstr(struct section *sec, unsigned long offset)
#define WARN_INSN(insn, format, ...) \ #define WARN_INSN(insn, format, ...) \
({ \ ({ \
WARN_FUNC(format, insn->sec, insn->offset, ##__VA_ARGS__); \ struct instruction *_insn = (insn); \
if (!_insn->sym || !_insn->sym->warned) \
WARN_FUNC(format, _insn->sec, _insn->offset, \
##__VA_ARGS__); \
if (_insn->sym) \
_insn->sym->warned = 1; \
}) })
#define BT_FUNC(format, insn, ...) \ #define BT_INSN(insn, format, ...) \
({ \ ({ \
if (opts.verbose || opts.backtrace) { \
struct instruction *_insn = (insn); \ struct instruction *_insn = (insn); \
char *_str = offstr(_insn->sec, _insn->offset); \ char *_str = offstr(_insn->sec, _insn->offset); \
WARN(" %s: " format, _str, ##__VA_ARGS__); \ WARN(" %s: " format, _str, ##__VA_ARGS__); \
free(_str); \ free(_str); \
} \
}) })
#define WARN_ELF(format, ...) \ #define WARN_ELF(format, ...) \
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This is a (sorted!) list of all known __noreturn functions in the kernel.
* It's needed for objtool to properly reverse-engineer the control flow graph.
*
* Yes, this is unfortunate. A better solution is in the works.
*/
NORETURN(__invalid_creds)
NORETURN(__kunit_abort)
NORETURN(__module_put_and_kthread_exit)
NORETURN(__reiserfs_panic)
NORETURN(__stack_chk_fail)
NORETURN(__ubsan_handle_builtin_unreachable)
NORETURN(arch_call_rest_init)
NORETURN(arch_cpu_idle_dead)
NORETURN(btrfs_assertfail)
NORETURN(cpu_bringup_and_idle)
NORETURN(cpu_startup_entry)
NORETURN(do_exit)
NORETURN(do_group_exit)
NORETURN(do_task_dead)
NORETURN(ex_handler_msr_mce)
NORETURN(fortify_panic)
NORETURN(hlt_play_dead)
NORETURN(hv_ghcb_terminate)
NORETURN(kthread_complete_and_exit)
NORETURN(kthread_exit)
NORETURN(kunit_try_catch_throw)
NORETURN(machine_real_restart)
NORETURN(make_task_dead)
NORETURN(mpt_halt_firmware)
NORETURN(nmi_panic_self_stop)
NORETURN(panic)
NORETURN(panic_smp_self_stop)
NORETURN(rest_init)
NORETURN(rewind_stack_and_make_dead)
NORETURN(sev_es_terminate)
NORETURN(snp_abort)
NORETURN(start_kernel)
NORETURN(stop_this_cpu)
NORETURN(usercopy_abort)
NORETURN(x86_64_start_kernel)
NORETURN(x86_64_start_reservations)
NORETURN(xen_cpu_bringup_again)
NORETURN(xen_start_kernel)
...@@ -118,7 +118,7 @@ static int write_orc_entry(struct elf *elf, struct section *orc_sec, ...@@ -118,7 +118,7 @@ static int write_orc_entry(struct elf *elf, struct section *orc_sec,
orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); orc->bp_offset = bswap_if_needed(elf, orc->bp_offset);
/* populate reloc for ip */ /* populate reloc for ip */
if (elf_add_reloc_to_insn(elf, ip_sec, idx * sizeof(int), R_X86_64_PC32, if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx,
insn_sec, insn_off)) insn_sec, insn_off))
return -1; return -1;
...@@ -237,12 +237,12 @@ int orc_create(struct objtool_file *file) ...@@ -237,12 +237,12 @@ int orc_create(struct objtool_file *file)
WARN("file already has .orc_unwind section, skipping"); WARN("file already has .orc_unwind section, skipping");
return -1; return -1;
} }
orc_sec = elf_create_section(file->elf, ".orc_unwind", 0, orc_sec = elf_create_section(file->elf, ".orc_unwind",
sizeof(struct orc_entry), nr); sizeof(struct orc_entry), nr);
if (!orc_sec) if (!orc_sec)
return -1; return -1;
sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr); sec = elf_create_section_pair(file->elf, ".orc_unwind_ip", sizeof(int), nr, nr);
if (!sec) if (!sec)
return -1; return -1;
......
...@@ -62,7 +62,7 @@ static void reloc_to_sec_off(struct reloc *reloc, struct section **sec, ...@@ -62,7 +62,7 @@ static void reloc_to_sec_off(struct reloc *reloc, struct section **sec,
unsigned long *off) unsigned long *off)
{ {
*sec = reloc->sym->sec; *sec = reloc->sym->sec;
*off = reloc->sym->offset + reloc->addend; *off = reloc->sym->offset + reloc_addend(reloc);
} }
static int get_alt_entry(struct elf *elf, const struct special_entry *entry, static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
...@@ -126,7 +126,7 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry, ...@@ -126,7 +126,7 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
sec, offset + entry->key); sec, offset + entry->key);
return -1; return -1;
} }
alt->key_addend = key_reloc->addend; alt->key_addend = reloc_addend(key_reloc);
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment