kexec: derive purgatory entry from symbol
kexec_load_purgatory() derives image->start by locating e_entry inside an
SHF_EXECINSTR section. If the purgatory object contains multiple
executable sections with overlapping sh_addr, the entrypoint check can
match more than once and trigger a WARN.
Derive the entry section from the purgatory_start symbol when present and
compute image->start from its final placement. Keep the existing e_entry
fallback for purgatories that do not expose the symbol.
WARNING: kernel/kexec_file.c:1009 at kexec_load_purgatory+0x395/0x3c0, CPU#10: kexec/1784
Call Trace:
<TASK>
bzImage64_load+0x133/0xa00
__do_sys_kexec_file_load+0x2b3/0x5c0
do_syscall_64+0x81/0x610
entry_SYSCALL_64_after_hwframe+0x76/0x7e
[me@linux.beauty: move helper to avoid forward declaration, per Baoquan]
Link: https://lkml.kernel.org/r/20260128043511.316860-1-me@linux.beauty
Link: https://lkml.kernel.org/r/20260120124005.148381-1-me@linux.beauty
Fixes: 8652d44f46 ("kexec: support purgatories with .text.hot sections")
Signed-off-by: Li Chen <me@linux.beauty>
Acked-by: Baoquan He <bhe@redhat.com>
Cc: Alexander Graf <graf@amazon.com>
Cc: Eric Biggers <ebiggers@kernel.org>
Cc: Li Chen <me@linux.beauty>
Cc: Philipp Rudo <prudo@redhat.com>
Cc: Ricardo Ribalda Delgado <ribalda@chromium.org>
Cc: Ross Zwisler <zwisler@google.com>
Cc: Sourabh Jain <sourabhjain@linux.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
5138c936c2
commit
480e1d5c64
|
|
@ -882,6 +882,60 @@ out_free_sha_regions:
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY
|
#ifdef CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY
|
||||||
|
/*
|
||||||
|
* kexec_purgatory_find_symbol - find a symbol in the purgatory
|
||||||
|
* @pi: Purgatory to search in.
|
||||||
|
* @name: Name of the symbol.
|
||||||
|
*
|
||||||
|
* Return: pointer to symbol in read-only symtab on success, NULL on error.
|
||||||
|
*/
|
||||||
|
static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
const Elf_Shdr *sechdrs;
|
||||||
|
const Elf_Ehdr *ehdr;
|
||||||
|
const Elf_Sym *syms;
|
||||||
|
const char *strtab;
|
||||||
|
int i, k;
|
||||||
|
|
||||||
|
if (!pi->ehdr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ehdr = pi->ehdr;
|
||||||
|
sechdrs = (void *)ehdr + ehdr->e_shoff;
|
||||||
|
|
||||||
|
for (i = 0; i < ehdr->e_shnum; i++) {
|
||||||
|
if (sechdrs[i].sh_type != SHT_SYMTAB)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (sechdrs[i].sh_link >= ehdr->e_shnum)
|
||||||
|
/* Invalid strtab section number */
|
||||||
|
continue;
|
||||||
|
strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset;
|
||||||
|
syms = (void *)ehdr + sechdrs[i].sh_offset;
|
||||||
|
|
||||||
|
/* Go through symbols for a match */
|
||||||
|
for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) {
|
||||||
|
if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strcmp(strtab + syms[k].st_name, name) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (syms[k].st_shndx == SHN_UNDEF ||
|
||||||
|
syms[k].st_shndx >= ehdr->e_shnum) {
|
||||||
|
pr_debug("Symbol: %s has bad section index %d.\n",
|
||||||
|
name, syms[k].st_shndx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Found the symbol we are looking for */
|
||||||
|
return &syms[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* kexec_purgatory_setup_kbuf - prepare buffer to load purgatory.
|
* kexec_purgatory_setup_kbuf - prepare buffer to load purgatory.
|
||||||
* @pi: Purgatory to be loaded.
|
* @pi: Purgatory to be loaded.
|
||||||
|
|
@ -960,6 +1014,10 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
|
||||||
unsigned long offset;
|
unsigned long offset;
|
||||||
size_t sechdrs_size;
|
size_t sechdrs_size;
|
||||||
Elf_Shdr *sechdrs;
|
Elf_Shdr *sechdrs;
|
||||||
|
const Elf_Sym *entry_sym;
|
||||||
|
u16 entry_shndx = 0;
|
||||||
|
unsigned long entry_off = 0;
|
||||||
|
bool start_fixed = false;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -977,6 +1035,12 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
|
||||||
bss_addr = kbuf->mem + kbuf->bufsz;
|
bss_addr = kbuf->mem + kbuf->bufsz;
|
||||||
kbuf->image->start = pi->ehdr->e_entry;
|
kbuf->image->start = pi->ehdr->e_entry;
|
||||||
|
|
||||||
|
entry_sym = kexec_purgatory_find_symbol(pi, "purgatory_start");
|
||||||
|
if (entry_sym) {
|
||||||
|
entry_shndx = entry_sym->st_shndx;
|
||||||
|
entry_off = entry_sym->st_value;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < pi->ehdr->e_shnum; i++) {
|
for (i = 0; i < pi->ehdr->e_shnum; i++) {
|
||||||
unsigned long align;
|
unsigned long align;
|
||||||
void *src, *dst;
|
void *src, *dst;
|
||||||
|
|
@ -994,6 +1058,13 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
|
||||||
|
|
||||||
offset = ALIGN(offset, align);
|
offset = ALIGN(offset, align);
|
||||||
|
|
||||||
|
if (!start_fixed && entry_sym && i == entry_shndx &&
|
||||||
|
(sechdrs[i].sh_flags & SHF_EXECINSTR) &&
|
||||||
|
entry_off < sechdrs[i].sh_size) {
|
||||||
|
kbuf->image->start = kbuf->mem + offset + entry_off;
|
||||||
|
start_fixed = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the segment contains the entry point, if so,
|
* Check if the segment contains the entry point, if so,
|
||||||
* calculate the value of image->start based on it.
|
* calculate the value of image->start based on it.
|
||||||
|
|
@ -1004,13 +1075,14 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
|
||||||
* is not set to the initial value, and warn the user so they
|
* is not set to the initial value, and warn the user so they
|
||||||
* have a chance to fix their purgatory's linker script.
|
* have a chance to fix their purgatory's linker script.
|
||||||
*/
|
*/
|
||||||
if (sechdrs[i].sh_flags & SHF_EXECINSTR &&
|
if (!start_fixed && sechdrs[i].sh_flags & SHF_EXECINSTR &&
|
||||||
pi->ehdr->e_entry >= sechdrs[i].sh_addr &&
|
pi->ehdr->e_entry >= sechdrs[i].sh_addr &&
|
||||||
pi->ehdr->e_entry < (sechdrs[i].sh_addr
|
pi->ehdr->e_entry < (sechdrs[i].sh_addr
|
||||||
+ sechdrs[i].sh_size) &&
|
+ sechdrs[i].sh_size) &&
|
||||||
!WARN_ON(kbuf->image->start != pi->ehdr->e_entry)) {
|
kbuf->image->start == pi->ehdr->e_entry) {
|
||||||
kbuf->image->start -= sechdrs[i].sh_addr;
|
kbuf->image->start -= sechdrs[i].sh_addr;
|
||||||
kbuf->image->start += kbuf->mem + offset;
|
kbuf->image->start += kbuf->mem + offset;
|
||||||
|
start_fixed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
src = (void *)pi->ehdr + sechdrs[i].sh_offset;
|
src = (void *)pi->ehdr + sechdrs[i].sh_offset;
|
||||||
|
|
@ -1128,61 +1200,6 @@ out_free_kbuf:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* kexec_purgatory_find_symbol - find a symbol in the purgatory
|
|
||||||
* @pi: Purgatory to search in.
|
|
||||||
* @name: Name of the symbol.
|
|
||||||
*
|
|
||||||
* Return: pointer to symbol in read-only symtab on success, NULL on error.
|
|
||||||
*/
|
|
||||||
static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
|
|
||||||
const char *name)
|
|
||||||
{
|
|
||||||
const Elf_Shdr *sechdrs;
|
|
||||||
const Elf_Ehdr *ehdr;
|
|
||||||
const Elf_Sym *syms;
|
|
||||||
const char *strtab;
|
|
||||||
int i, k;
|
|
||||||
|
|
||||||
if (!pi->ehdr)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
ehdr = pi->ehdr;
|
|
||||||
sechdrs = (void *)ehdr + ehdr->e_shoff;
|
|
||||||
|
|
||||||
for (i = 0; i < ehdr->e_shnum; i++) {
|
|
||||||
if (sechdrs[i].sh_type != SHT_SYMTAB)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (sechdrs[i].sh_link >= ehdr->e_shnum)
|
|
||||||
/* Invalid strtab section number */
|
|
||||||
continue;
|
|
||||||
strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset;
|
|
||||||
syms = (void *)ehdr + sechdrs[i].sh_offset;
|
|
||||||
|
|
||||||
/* Go through symbols for a match */
|
|
||||||
for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) {
|
|
||||||
if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (strcmp(strtab + syms[k].st_name, name) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (syms[k].st_shndx == SHN_UNDEF ||
|
|
||||||
syms[k].st_shndx >= ehdr->e_shnum) {
|
|
||||||
pr_debug("Symbol: %s has bad section index %d.\n",
|
|
||||||
name, syms[k].st_shndx);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Found the symbol we are looking for */
|
|
||||||
return &syms[k];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name)
|
void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name)
|
||||||
{
|
{
|
||||||
struct purgatory_info *pi = &image->purgatory_info;
|
struct purgatory_info *pi = &image->purgatory_info;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue