Commit 583dbad3 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull core fixes from Thomas Gleixner:

 - Unbreak the BPF compilation which got broken by the unconditional
   requirement of asm-goto, which is not supported by clang.

 - Prevent probing on exception masking instructions in uprobes and
   kprobes to avoid the issues of the delayed exceptions instead of
   having an ugly workaround.

 - Prevent a double free_page() in the error path of do_kexec_load()

 - A set of objtool updates addressing various issues mostly related to
   switch tables and the noreturn detection for recursive sibling calls

 - Header sync for tools.

* 'core-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  objtool: Detect RIP-relative switch table references, part 2
  objtool: Detect RIP-relative switch table references
  objtool: Support GCC 8 switch tables
  objtool: Support GCC 8's cold subfunctions
  objtool: Fix "noreturn" detection for recursive sibling calls
  objtool, kprobes/x86: Sync the latest <asm/insn.h> header with tools/objtool/arch/x86/include/asm/insn.h
  x86/cpufeature: Guard asm_volatile_goto usage for BPF compilation
  uprobes/x86: Prohibit probing on MOV SS instruction
  kprobes/x86: Prohibit probing on exception masking instructions
  x86/kexec: Avoid double free_page() upon do_kexec_load() failure
parents 203ec2fe 7dec80cc
...@@ -140,6 +140,20 @@ extern void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int bit); ...@@ -140,6 +140,20 @@ extern void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int bit);
#define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit) #define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit)
#if defined(__clang__) && !defined(CC_HAVE_ASM_GOTO)
/*
* Workaround for the sake of BPF compilation which utilizes kernel
* headers, but clang does not support ASM GOTO and fails the build.
*/
#ifndef __BPF_TRACING__
#warning "Compiler lacks ASM_GOTO support. Add -D __BPF_TRACING__ to your compiler arguments"
#endif
#define static_cpu_has(bit) boot_cpu_has(bit)
#else
/* /*
* Static testing of CPU features. Used the same as boot_cpu_has(). * Static testing of CPU features. Used the same as boot_cpu_has().
* These will statically patch the target code for additional * These will statically patch the target code for additional
...@@ -195,6 +209,7 @@ static __always_inline __pure bool _static_cpu_has(u16 bit) ...@@ -195,6 +209,7 @@ static __always_inline __pure bool _static_cpu_has(u16 bit)
boot_cpu_has(bit) : \ boot_cpu_has(bit) : \
_static_cpu_has(bit) \ _static_cpu_has(bit) \
) )
#endif
#define cpu_has_bug(c, bit) cpu_has(c, (bit)) #define cpu_has_bug(c, bit) cpu_has(c, (bit))
#define set_cpu_bug(c, bit) set_cpu_cap(c, (bit)) #define set_cpu_bug(c, bit) set_cpu_cap(c, (bit))
......
...@@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn) ...@@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn)
return insn_offset_displacement(insn) + insn->displacement.nbytes; return insn_offset_displacement(insn) + insn->displacement.nbytes;
} }
#define POP_SS_OPCODE 0x1f
#define MOV_SREG_OPCODE 0x8e
/*
* Intel SDM Vol.3A 6.8.3 states;
* "Any single-step trap that would be delivered following the MOV to SS
* instruction or POP to SS instruction (because EFLAGS.TF is 1) is
* suppressed."
* This function returns true if @insn is MOV SS or POP SS. On these
* instructions, single stepping is suppressed.
*/
static inline int insn_masking_exception(struct insn *insn)
{
return insn->opcode.bytes[0] == POP_SS_OPCODE ||
(insn->opcode.bytes[0] == MOV_SREG_OPCODE &&
X86_MODRM_REG(insn->modrm.bytes[0]) == 2);
}
#endif /* _ASM_X86_INSN_H */ #endif /* _ASM_X86_INSN_H */
...@@ -370,6 +370,10 @@ int __copy_instruction(u8 *dest, u8 *src, u8 *real, struct insn *insn) ...@@ -370,6 +370,10 @@ int __copy_instruction(u8 *dest, u8 *src, u8 *real, struct insn *insn)
if (insn->opcode.bytes[0] == BREAKPOINT_INSTRUCTION) if (insn->opcode.bytes[0] == BREAKPOINT_INSTRUCTION)
return 0; return 0;
/* We should not singlestep on the exception masking instructions */
if (insn_masking_exception(insn))
return 0;
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
/* Only x86_64 has RIP relative instructions */ /* Only x86_64 has RIP relative instructions */
if (insn_rip_relative(insn)) { if (insn_rip_relative(insn)) {
......
...@@ -57,12 +57,17 @@ static void load_segments(void) ...@@ -57,12 +57,17 @@ static void load_segments(void)
static void machine_kexec_free_page_tables(struct kimage *image) static void machine_kexec_free_page_tables(struct kimage *image)
{ {
free_page((unsigned long)image->arch.pgd); free_page((unsigned long)image->arch.pgd);
image->arch.pgd = NULL;
#ifdef CONFIG_X86_PAE #ifdef CONFIG_X86_PAE
free_page((unsigned long)image->arch.pmd0); free_page((unsigned long)image->arch.pmd0);
image->arch.pmd0 = NULL;
free_page((unsigned long)image->arch.pmd1); free_page((unsigned long)image->arch.pmd1);
image->arch.pmd1 = NULL;
#endif #endif
free_page((unsigned long)image->arch.pte0); free_page((unsigned long)image->arch.pte0);
image->arch.pte0 = NULL;
free_page((unsigned long)image->arch.pte1); free_page((unsigned long)image->arch.pte1);
image->arch.pte1 = NULL;
} }
static int machine_kexec_alloc_page_tables(struct kimage *image) static int machine_kexec_alloc_page_tables(struct kimage *image)
...@@ -79,7 +84,6 @@ static int machine_kexec_alloc_page_tables(struct kimage *image) ...@@ -79,7 +84,6 @@ static int machine_kexec_alloc_page_tables(struct kimage *image)
!image->arch.pmd0 || !image->arch.pmd1 || !image->arch.pmd0 || !image->arch.pmd1 ||
#endif #endif
!image->arch.pte0 || !image->arch.pte1) { !image->arch.pte0 || !image->arch.pte1) {
machine_kexec_free_page_tables(image);
return -ENOMEM; return -ENOMEM;
} }
return 0; return 0;
......
...@@ -39,9 +39,13 @@ const struct kexec_file_ops * const kexec_file_loaders[] = { ...@@ -39,9 +39,13 @@ const struct kexec_file_ops * const kexec_file_loaders[] = {
static void free_transition_pgtable(struct kimage *image) static void free_transition_pgtable(struct kimage *image)
{ {
free_page((unsigned long)image->arch.p4d); free_page((unsigned long)image->arch.p4d);
image->arch.p4d = NULL;
free_page((unsigned long)image->arch.pud); free_page((unsigned long)image->arch.pud);
image->arch.pud = NULL;
free_page((unsigned long)image->arch.pmd); free_page((unsigned long)image->arch.pmd);
image->arch.pmd = NULL;
free_page((unsigned long)image->arch.pte); free_page((unsigned long)image->arch.pte);
image->arch.pte = NULL;
} }
static int init_transition_pgtable(struct kimage *image, pgd_t *pgd) static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
...@@ -91,7 +95,6 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd) ...@@ -91,7 +95,6 @@ static int init_transition_pgtable(struct kimage *image, pgd_t *pgd)
set_pte(pte, pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL_EXEC_NOENC)); set_pte(pte, pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL_EXEC_NOENC));
return 0; return 0;
err: err:
free_transition_pgtable(image);
return result; return result;
} }
......
...@@ -299,6 +299,10 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool ...@@ -299,6 +299,10 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool
if (is_prefix_bad(insn)) if (is_prefix_bad(insn))
return -ENOTSUPP; return -ENOTSUPP;
/* We should not singlestep on the exception masking instructions */
if (insn_masking_exception(insn))
return -ENOTSUPP;
if (x86_64) if (x86_64)
good_insns = good_insns_64; good_insns = good_insns_64;
else else
......
...@@ -255,7 +255,7 @@ $(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h ...@@ -255,7 +255,7 @@ $(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h
$(obj)/%.o: $(src)/%.c $(obj)/%.o: $(src)/%.c
$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) -I$(obj) \ $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) -I$(obj) \
-I$(srctree)/tools/testing/selftests/bpf/ \ -I$(srctree)/tools/testing/selftests/bpf/ \
-D__KERNEL__ -Wno-unused-value -Wno-pointer-sign \ -D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
-D__TARGET_ARCH_$(ARCH) -Wno-compare-distinct-pointer-types \ -D__TARGET_ARCH_$(ARCH) -Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \ -Wno-gnu-variable-sized-type-not-at-end \
-Wno-address-of-packed-member -Wno-tautological-compare \ -Wno-address-of-packed-member -Wno-tautological-compare \
......
...@@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn) ...@@ -208,4 +208,22 @@ static inline int insn_offset_immediate(struct insn *insn)
return insn_offset_displacement(insn) + insn->displacement.nbytes; return insn_offset_displacement(insn) + insn->displacement.nbytes;
} }
#define POP_SS_OPCODE 0x1f
#define MOV_SREG_OPCODE 0x8e
/*
* Intel SDM Vol.3A 6.8.3 states;
* "Any single-step trap that would be delivered following the MOV to SS
* instruction or POP to SS instruction (because EFLAGS.TF is 1) is
* suppressed."
* This function returns true if @insn is MOV SS or POP SS. On these
* instructions, single stepping is suppressed.
*/
static inline int insn_masking_exception(struct insn *insn)
{
return insn->opcode.bytes[0] == POP_SS_OPCODE ||
(insn->opcode.bytes[0] == MOV_SREG_OPCODE &&
X86_MODRM_REG(insn->modrm.bytes[0]) == 2);
}
#endif /* _ASM_X86_INSN_H */ #endif /* _ASM_X86_INSN_H */
...@@ -59,6 +59,31 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file, ...@@ -59,6 +59,31 @@ static struct instruction *next_insn_same_sec(struct objtool_file *file,
return next; return next;
} }
static struct instruction *next_insn_same_func(struct objtool_file *file,
struct instruction *insn)
{
struct instruction *next = list_next_entry(insn, list);
struct symbol *func = insn->func;
if (!func)
return NULL;
if (&next->list != &file->insn_list && next->func == func)
return next;
/* Check if we're already in the subfunction: */
if (func == func->cfunc)
return NULL;
/* Move to the subfunction: */
return find_insn(file, func->cfunc->sec, func->cfunc->offset);
}
#define func_for_each_insn_all(file, func, insn) \
for (insn = find_insn(file, func->sec, func->offset); \
insn; \
insn = next_insn_same_func(file, insn))
#define func_for_each_insn(file, func, insn) \ #define func_for_each_insn(file, func, insn) \
for (insn = find_insn(file, func->sec, func->offset); \ for (insn = find_insn(file, func->sec, func->offset); \
insn && &insn->list != &file->insn_list && \ insn && &insn->list != &file->insn_list && \
...@@ -149,10 +174,14 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, ...@@ -149,10 +174,14 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
if (!strcmp(func->name, global_noreturns[i])) if (!strcmp(func->name, global_noreturns[i]))
return 1; return 1;
if (!func->sec) if (!func->len)
return 0; return 0;
func_for_each_insn(file, func, insn) { insn = find_insn(file, func->sec, func->offset);
if (!insn->func)
return 0;
func_for_each_insn_all(file, func, insn) {
empty = false; empty = false;
if (insn->type == INSN_RETURN) if (insn->type == INSN_RETURN)
...@@ -167,35 +196,28 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, ...@@ -167,35 +196,28 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
* case, the function's dead-end status depends on whether the target * case, the function's dead-end status depends on whether the target
* of the sibling call returns. * of the sibling call returns.
*/ */
func_for_each_insn(file, func, insn) { func_for_each_insn_all(file, func, insn) {
if (insn->sec != func->sec ||
insn->offset >= func->offset + func->len)
break;
if (insn->type == INSN_JUMP_UNCONDITIONAL) { if (insn->type == INSN_JUMP_UNCONDITIONAL) {
struct instruction *dest = insn->jump_dest; struct instruction *dest = insn->jump_dest;
struct symbol *dest_func;
if (!dest) if (!dest)
/* sibling call to another file */ /* sibling call to another file */
return 0; return 0;
if (dest->sec != func->sec || if (dest->func && dest->func->pfunc != insn->func->pfunc) {
dest->offset < func->offset ||
dest->offset >= func->offset + func->len) {
/* local sibling call */
dest_func = find_symbol_by_offset(dest->sec,
dest->offset);
if (!dest_func)
continue;
/* local sibling call */
if (recursion == 5) { if (recursion == 5) {
WARN_FUNC("infinite recursion (objtool bug!)", /*
dest->sec, dest->offset); * Infinite recursion: two functions
return -1; * have sibling calls to each other.
* This is a very rare case. It means
* they aren't dead ends.
*/
return 0;
} }
return __dead_end_function(file, dest_func, return __dead_end_function(file, dest->func,
recursion + 1); recursion + 1);
} }
} }
...@@ -422,7 +444,7 @@ static void add_ignores(struct objtool_file *file) ...@@ -422,7 +444,7 @@ static void add_ignores(struct objtool_file *file)
if (!ignore_func(file, func)) if (!ignore_func(file, func))
continue; continue;
func_for_each_insn(file, func, insn) func_for_each_insn_all(file, func, insn)
insn->ignore = true; insn->ignore = true;
} }
} }
...@@ -782,30 +804,35 @@ static int add_special_section_alts(struct objtool_file *file) ...@@ -782,30 +804,35 @@ static int add_special_section_alts(struct objtool_file *file)
return ret; return ret;
} }
static int add_switch_table(struct objtool_file *file, struct symbol *func, static int add_switch_table(struct objtool_file *file, struct instruction *insn,
struct instruction *insn, struct rela *table, struct rela *table, struct rela *next_table)
struct rela *next_table)
{ {
struct rela *rela = table; struct rela *rela = table;
struct instruction *alt_insn; struct instruction *alt_insn;
struct alternative *alt; struct alternative *alt;
struct symbol *pfunc = insn->func->pfunc;
unsigned int prev_offset = 0;
list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) { list_for_each_entry_from(rela, &file->rodata->rela->rela_list, list) {
if (rela == next_table) if (rela == next_table)
break; break;
if (rela->sym->sec != insn->sec || /* Make sure the switch table entries are consecutive: */
rela->addend <= func->offset || if (prev_offset && rela->offset != prev_offset + 8)
rela->addend >= func->offset + func->len)
break; break;
alt_insn = find_insn(file, insn->sec, rela->addend); /* Detect function pointers from contiguous objects: */
if (!alt_insn) { if (rela->sym->sec == pfunc->sec &&
WARN("%s: can't find instruction at %s+0x%x", rela->addend == pfunc->offset)
file->rodata->rela->name, insn->sec->name, break;
rela->addend);
return -1; alt_insn = find_insn(file, rela->sym->sec, rela->addend);
} if (!alt_insn)
break;
/* Make sure the jmp dest is in the function or subfunction: */
if (alt_insn->func->pfunc != pfunc)
break;
alt = malloc(sizeof(*alt)); alt = malloc(sizeof(*alt));
if (!alt) { if (!alt) {
...@@ -815,6 +842,13 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func, ...@@ -815,6 +842,13 @@ static int add_switch_table(struct objtool_file *file, struct symbol *func,
alt->insn = alt_insn; alt->insn = alt_insn;
list_add_tail(&alt->list, &insn->alts); list_add_tail(&alt->list, &insn->alts);
prev_offset = rela->offset;
}
if (!prev_offset) {
WARN_FUNC("can't find switch jump table",
insn->sec, insn->offset);
return -1;
} }
return 0; return 0;
...@@ -869,40 +903,21 @@ static struct rela *find_switch_table(struct objtool_file *file, ...@@ -869,40 +903,21 @@ static struct rela *find_switch_table(struct objtool_file *file,
{ {
struct rela *text_rela, *rodata_rela; struct rela *text_rela, *rodata_rela;
struct instruction *orig_insn = insn; struct instruction *orig_insn = insn;
unsigned long table_offset;
text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
if (text_rela && text_rela->sym == file->rodata->sym) {
/* case 1 */
rodata_rela = find_rela_by_dest(file->rodata,
text_rela->addend);
if (rodata_rela)
return rodata_rela;
/* case 2 */
rodata_rela = find_rela_by_dest(file->rodata,
text_rela->addend + 4);
if (!rodata_rela)
return NULL;
file->ignore_unreachables = true;
return rodata_rela;
}
/* case 3 */
/* /*
* Backward search using the @first_jump_src links, these help avoid * Backward search using the @first_jump_src links, these help avoid
* much of the 'in between' code. Which avoids us getting confused by * much of the 'in between' code. Which avoids us getting confused by
* it. * it.
*/ */
for (insn = list_prev_entry(insn, list); for (;
&insn->list != &file->insn_list && &insn->list != &file->insn_list &&
insn->sec == func->sec && insn->sec == func->sec &&
insn->offset >= func->offset; insn->offset >= func->offset;
insn = insn->first_jump_src ?: list_prev_entry(insn, list)) { insn = insn->first_jump_src ?: list_prev_entry(insn, list)) {
if (insn->type == INSN_JUMP_DYNAMIC) if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
break; break;
/* allow small jumps within the range */ /* allow small jumps within the range */
...@@ -918,19 +933,30 @@ static struct rela *find_switch_table(struct objtool_file *file, ...@@ -918,19 +933,30 @@ static struct rela *find_switch_table(struct objtool_file *file,
if (!text_rela || text_rela->sym != file->rodata->sym) if (!text_rela || text_rela->sym != file->rodata->sym)
continue; continue;
table_offset = text_rela->addend;
if (text_rela->type == R_X86_64_PC32)
table_offset += 4;
/* /*
* Make sure the .rodata address isn't associated with a * Make sure the .rodata address isn't associated with a
* symbol. gcc jump tables are anonymous data. * symbol. gcc jump tables are anonymous data.
*/ */
if (find_symbol_containing(file->rodata, text_rela->addend)) if (find_symbol_containing(file->rodata, table_offset))
continue; continue;
rodata_rela = find_rela_by_dest(file->rodata, text_rela->addend); rodata_rela = find_rela_by_dest(file->rodata, table_offset);
if (!rodata_rela) if (rodata_rela) {
continue; /*
* Use of RIP-relative switch jumps is quite rare, and
* indicates a rare GCC quirk/bug which can leave dead
* code behind.
*/
if (text_rela->type == R_X86_64_PC32)
file->ignore_unreachables = true;
return rodata_rela; return rodata_rela;
} }
}
return NULL; return NULL;
} }
...@@ -943,7 +969,7 @@ static int add_func_switch_tables(struct objtool_file *file, ...@@ -943,7 +969,7 @@ static int add_func_switch_tables(struct objtool_file *file,
struct rela *rela, *prev_rela = NULL; struct rela *rela, *prev_rela = NULL;
int ret; int ret;
func_for_each_insn(file, func, insn) { func_for_each_insn_all(file, func, insn) {
if (!last) if (!last)
last = insn; last = insn;
...@@ -974,8 +1000,7 @@ static int add_func_switch_tables(struct objtool_file *file, ...@@ -974,8 +1000,7 @@ static int add_func_switch_tables(struct objtool_file *file,
* the beginning of another switch table in the same function. * the beginning of another switch table in the same function.
*/ */
if (prev_jump) { if (prev_jump) {
ret = add_switch_table(file, func, prev_jump, prev_rela, ret = add_switch_table(file, prev_jump, prev_rela, rela);
rela);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -985,7 +1010,7 @@ static int add_func_switch_tables(struct objtool_file *file, ...@@ -985,7 +1010,7 @@ static int add_func_switch_tables(struct objtool_file *file,
} }
if (prev_jump) { if (prev_jump) {
ret = add_switch_table(file, func, prev_jump, prev_rela, NULL); ret = add_switch_table(file, prev_jump, prev_rela, NULL);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -1749,15 +1774,13 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1749,15 +1774,13 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
while (1) { while (1) {
next_insn = next_insn_same_sec(file, insn); next_insn = next_insn_same_sec(file, insn);
if (file->c_file && func && insn->func && func != insn->func->pfunc) {
if (file->c_file && func && insn->func && func != insn->func) {
WARN("%s() falls through to next function %s()", WARN("%s() falls through to next function %s()",
func->name, insn->func->name); func->name, insn->func->name);
return 1; return 1;
} }
if (insn->func) func = insn->func ? insn->func->pfunc : NULL;
func = insn->func;
if (func && insn->ignore) { if (func && insn->ignore) {
WARN_FUNC("BUG: why am I validating an ignored function?", WARN_FUNC("BUG: why am I validating an ignored function?",
...@@ -1778,7 +1801,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1778,7 +1801,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
i = insn; i = insn;
save_insn = NULL; save_insn = NULL;
func_for_each_insn_continue_reverse(file, func, i) { func_for_each_insn_continue_reverse(file, insn->func, i) {
if (i->save) { if (i->save) {
save_insn = i; save_insn = i;
break; break;
...@@ -1865,7 +1888,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, ...@@ -1865,7 +1888,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
case INSN_JUMP_UNCONDITIONAL: case INSN_JUMP_UNCONDITIONAL:
if (insn->jump_dest && if (insn->jump_dest &&
(!func || !insn->jump_dest->func || (!func || !insn->jump_dest->func ||
func == insn->jump_dest->func)) { insn->jump_dest->func->pfunc == func)) {
ret = validate_branch(file, insn->jump_dest, ret = validate_branch(file, insn->jump_dest,
state); state);
if (ret) if (ret)
...@@ -2060,7 +2083,7 @@ static int validate_functions(struct objtool_file *file) ...@@ -2060,7 +2083,7 @@ static int validate_functions(struct objtool_file *file)
for_each_sec(file, sec) { for_each_sec(file, sec) {
list_for_each_entry(func, &sec->symbol_list, list) { list_for_each_entry(func, &sec->symbol_list, list) {
if (func->type != STT_FUNC) if (func->type != STT_FUNC || func->pfunc != func)
continue; continue;
insn = find_insn(file, sec, func->offset); insn = find_insn(file, sec, func->offset);
......
...@@ -79,6 +79,19 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) ...@@ -79,6 +79,19 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
return NULL; return NULL;
} }
struct symbol *find_symbol_by_name(struct elf *elf, const char *name)
{
struct section *sec;
struct symbol *sym;
list_for_each_entry(sec, &elf->sections, list)
list_for_each_entry(sym, &sec->symbol_list, list)
if (!strcmp(sym->name, name))
return sym;
return NULL;
}
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset) struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
{ {
struct symbol *sym; struct symbol *sym;
...@@ -203,10 +216,11 @@ static int read_sections(struct elf *elf) ...@@ -203,10 +216,11 @@ static int read_sections(struct elf *elf)
static int read_symbols(struct elf *elf) static int read_symbols(struct elf *elf)
{ {
struct section *symtab; struct section *symtab, *sec;
struct symbol *sym; struct symbol *sym, *pfunc;
struct list_head *entry, *tmp; struct list_head *entry, *tmp;
int symbols_nr, i; int symbols_nr, i;
char *coldstr;
symtab = find_section_by_name(elf, ".symtab"); symtab = find_section_by_name(elf, ".symtab");
if (!symtab) { if (!symtab) {
...@@ -281,6 +295,30 @@ static int read_symbols(struct elf *elf) ...@@ -281,6 +295,30 @@ static int read_symbols(struct elf *elf)
hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx); hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
} }
/* Create parent/child links for any cold subfunctions */
list_for_each_entry(sec, &elf->sections, list) {
list_for_each_entry(sym, &sec->symbol_list, list) {
if (sym->type != STT_FUNC)
continue;
sym->pfunc = sym->cfunc = sym;
coldstr = strstr(sym->name, ".cold.");
if (coldstr) {
coldstr[0] = '\0';
pfunc = find_symbol_by_name(elf, sym->name);
coldstr[0] = '.';
if (!pfunc) {
WARN("%s(): can't find parent function",
sym->name);
goto err;
}
sym->pfunc = pfunc;
pfunc->cfunc = sym;
}
}
}
return 0; return 0;
err: err:
......
...@@ -61,6 +61,7 @@ struct symbol { ...@@ -61,6 +61,7 @@ struct symbol {
unsigned char bind, type; unsigned char bind, type;
unsigned long offset; unsigned long offset;
unsigned int len; unsigned int len;
struct symbol *pfunc, *cfunc;
}; };
struct rela { struct rela {
...@@ -86,6 +87,7 @@ struct elf { ...@@ -86,6 +87,7 @@ struct elf {
struct elf *elf_open(const char *name, int flags); struct elf *elf_open(const char *name, int flags);
struct section *find_section_by_name(struct elf *elf, const char *name); struct section *find_section_by_name(struct elf *elf, const char *name);
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_name(struct elf *elf, const char *name);
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset); struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
......
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