fs/Kconfig.binfmt | 9 +++++++++ fs/binfmt_elf.c | 26 ++++++++++++++++++++------ include/linux/mm_types.h | 5 +++++ 3 files changed, 34 insertions(+), 6 deletions(-)
Preserve the original ELF e_flags from the executable in the core dump
header instead of relying on compile-time defaults (ELF_CORE_EFLAGS or
value from the regset view). This ensures that ABI-specific flags in
the dump file match the actual binary being executed.
Save the e_flags field during ELF binary loading (in load_elf_binary())
into the mm_struct, and later retrieve it during core dump generation
(in fill_note_info()). Use this saved value to populate the e_flags in
the core dump ELF header.
Add a new Kconfig option, CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS, to guard
this behavior. Although motivated by a RISC-V use case, the mechanism is
generic and can be applied to all architectures.
This change is needed to resolve a debugging issue encountered when
analyzing core dumps with GDB for RISC-V systems. GDB inspects the
e_flags field to determine whether optional register sets such as the
floating-point unit are supported. Without correct flags, GDB may warn
and ignore valid register data:
warning: Unexpected size of section '.reg2/213' in core file.
As a result, floating-point registers are not accessible in the debugger,
even though they were dumped. Preserving the original e_flags enables
GDB and other tools to properly interpret the dump contents.
Signed-off-by: Svetlana Parfenova <svetlana.parfenova@syntacore.com>
---
fs/Kconfig.binfmt | 9 +++++++++
fs/binfmt_elf.c | 26 ++++++++++++++++++++------
include/linux/mm_types.h | 5 +++++
3 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt
index bd2f530e5740..45bed2041542 100644
--- a/fs/Kconfig.binfmt
+++ b/fs/Kconfig.binfmt
@@ -184,4 +184,13 @@ config EXEC_KUNIT_TEST
This builds the exec KUnit tests, which tests boundary conditions
of various aspects of the exec internals.
+config CORE_DUMP_USE_PROCESS_EFLAGS
+ bool "Preserve ELF e_flags from executable in core dumps"
+ depends on BINFMT_ELF && ELF_CORE && RISCV
+ default n
+ help
+ Save the ELF e_flags from the process executable at load time
+ and use it in the core dump header. This ensures the dump reflects
+ the original binary ABI.
+
endmenu
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index caeddccaa1fe..e5e06e11f9fc 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1290,6 +1290,11 @@ static int load_elf_binary(struct linux_binprm *bprm)
mm->end_data = end_data;
mm->start_stack = bprm->p;
+#ifdef CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS
+ /* stash e_flags for use in core dumps */
+ mm->saved_e_flags = elf_ex->e_flags;
+#endif
+
/**
* DOC: "brk" handling
*
@@ -1804,6 +1809,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
struct elf_thread_core_info *t;
struct elf_prpsinfo *psinfo;
struct core_thread *ct;
+ u16 machine;
+ u32 flags;
psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
if (!psinfo)
@@ -1831,17 +1838,24 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
return 0;
}
- /*
- * Initialize the ELF file header.
- */
- fill_elf_header(elf, phdrs,
- view->e_machine, view->e_flags);
+ machine = view->e_machine;
+ flags = view->e_flags;
#else
view = NULL;
info->thread_notes = 2;
- fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS);
+ machine = ELF_ARCH;
+ flags = ELF_CORE_EFLAGS;
#endif
+#ifdef CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS
+ flags = dump_task->mm->saved_e_flags;
+#endif
+
+ /*
+ * Initialize the ELF file header.
+ */
+ fill_elf_header(elf, phdrs, machine, flags);
+
/*
* Allocate a structure for each thread.
*/
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index d6b91e8a66d6..39921b32e4f5 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -1098,6 +1098,11 @@ struct mm_struct {
unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
+#ifdef CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS
+ /* the ABI-related flags from the ELF header. Used for core dump */
+ unsigned long saved_e_flags;
+#endif
+
struct percpu_counter rss_stat[NR_MM_COUNTERS];
struct linux_binfmt *binfmt;
--
2.50.1
On Wed, Aug 06, 2025 at 10:18:14PM +0600, Svetlana Parfenova wrote: > Preserve the original ELF e_flags from the executable in the core dump > header instead of relying on compile-time defaults (ELF_CORE_EFLAGS or > value from the regset view). This ensures that ABI-specific flags in > the dump file match the actual binary being executed. > > Save the e_flags field during ELF binary loading (in load_elf_binary()) > into the mm_struct, and later retrieve it during core dump generation > (in fill_note_info()). Use this saved value to populate the e_flags in > the core dump ELF header. > > Add a new Kconfig option, CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS, to guard > this behavior. Although motivated by a RISC-V use case, the mechanism is > generic and can be applied to all architectures. In the general case, is e_flags mismatched? i.e. why hide this behind a Kconfig? Put another way, if I enabled this Kconfig and dumped core from some regular x86_64 process, will e_flags be different? > This change is needed to resolve a debugging issue encountered when > analyzing core dumps with GDB for RISC-V systems. GDB inspects the > e_flags field to determine whether optional register sets such as the > floating-point unit are supported. Without correct flags, GDB may warn > and ignore valid register data: > > warning: Unexpected size of section '.reg2/213' in core file. > > As a result, floating-point registers are not accessible in the debugger, > even though they were dumped. Preserving the original e_flags enables > GDB and other tools to properly interpret the dump contents. > > Signed-off-by: Svetlana Parfenova <svetlana.parfenova@syntacore.com> > --- > fs/Kconfig.binfmt | 9 +++++++++ > fs/binfmt_elf.c | 26 ++++++++++++++++++++------ > include/linux/mm_types.h | 5 +++++ > 3 files changed, 34 insertions(+), 6 deletions(-) > > diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt > index bd2f530e5740..45bed2041542 100644 > --- a/fs/Kconfig.binfmt > +++ b/fs/Kconfig.binfmt > @@ -184,4 +184,13 @@ config EXEC_KUNIT_TEST > This builds the exec KUnit tests, which tests boundary conditions > of various aspects of the exec internals. > > +config CORE_DUMP_USE_PROCESS_EFLAGS > + bool "Preserve ELF e_flags from executable in core dumps" > + depends on BINFMT_ELF && ELF_CORE && RISCV > + default n > + help > + Save the ELF e_flags from the process executable at load time > + and use it in the core dump header. This ensures the dump reflects > + the original binary ABI. > + > endmenu > diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c > index caeddccaa1fe..e5e06e11f9fc 100644 > --- a/fs/binfmt_elf.c > +++ b/fs/binfmt_elf.c > @@ -1290,6 +1290,11 @@ static int load_elf_binary(struct linux_binprm *bprm) > mm->end_data = end_data; > mm->start_stack = bprm->p; > > +#ifdef CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS > + /* stash e_flags for use in core dumps */ > + mm->saved_e_flags = elf_ex->e_flags; > +#endif Is this structure actually lost during ELF load? I thought we preserved some more of the ELF headers during load... > + > /** > * DOC: "brk" handling > * > @@ -1804,6 +1809,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, > struct elf_thread_core_info *t; > struct elf_prpsinfo *psinfo; > struct core_thread *ct; > + u16 machine; > + u32 flags; > > psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); > if (!psinfo) > @@ -1831,17 +1838,24 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, > return 0; > } > > - /* > - * Initialize the ELF file header. > - */ > - fill_elf_header(elf, phdrs, > - view->e_machine, view->e_flags); > + machine = view->e_machine; > + flags = view->e_flags; > #else > view = NULL; > info->thread_notes = 2; > - fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS); > + machine = ELF_ARCH; > + flags = ELF_CORE_EFLAGS; > #endif > > +#ifdef CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS > + flags = dump_task->mm->saved_e_flags; > +#endif This appears to clobber the value from view->e_flags. Is that right? It feels like this change should only be needed in the default ELF_CORE_EFLAGS case. How is view->e_flags normally set? > + > + /* > + * Initialize the ELF file header. > + */ > + fill_elf_header(elf, phdrs, machine, flags); > + > /* > * Allocate a structure for each thread. > */ > diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h > index d6b91e8a66d6..39921b32e4f5 100644 > --- a/include/linux/mm_types.h > +++ b/include/linux/mm_types.h > @@ -1098,6 +1098,11 @@ struct mm_struct { > > unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ > > +#ifdef CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS > + /* the ABI-related flags from the ELF header. Used for core dump */ > + unsigned long saved_e_flags; > +#endif > + > struct percpu_counter rss_stat[NR_MM_COUNTERS]; > > struct linux_binfmt *binfmt; > -- > 2.50.1 > -Kees -- Kees Cook
On 07/08/2025 00.57, Kees Cook wrote: > On Wed, Aug 06, 2025 at 10:18:14PM +0600, Svetlana Parfenova wrote: >> Preserve the original ELF e_flags from the executable in the core dump >> header instead of relying on compile-time defaults (ELF_CORE_EFLAGS or >> value from the regset view). This ensures that ABI-specific flags in >> the dump file match the actual binary being executed. >> >> Save the e_flags field during ELF binary loading (in load_elf_binary()) >> into the mm_struct, and later retrieve it during core dump generation >> (in fill_note_info()). Use this saved value to populate the e_flags in >> the core dump ELF header. >> >> Add a new Kconfig option, CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS, to guard >> this behavior. Although motivated by a RISC-V use case, the mechanism is >> generic and can be applied to all architectures. > > In the general case, is e_flags mismatched? i.e. why hide this behind a > Kconfig? Put another way, if I enabled this Kconfig and dumped core from > some regular x86_64 process, will e_flags be different? > The Kconfig option is currently restricted to the RISC-V architecture because it's not clear to me whether other architectures need actual e_flags value from ELF header. If this option is disabled, the core dump will always use a compile time value for e_flags, regardless of which method is selected: ELF_CORE_EFLAGS or CORE_DUMP_USE_REGSET. And this constant does not necessarily reflect the actual e_flags of the running process (at least on RISC-V), which can vary depending on how the binary was compiled. Thus, I made a third method to obtain e_flags that reflects the real value. And it is gated behind a Kconfig option, as not all users may need it. >> This change is needed to resolve a debugging issue encountered when >> analyzing core dumps with GDB for RISC-V systems. GDB inspects the >> e_flags field to determine whether optional register sets such as the >> floating-point unit are supported. Without correct flags, GDB may warn >> and ignore valid register data: >> >> warning: Unexpected size of section '.reg2/213' in core file. >> >> As a result, floating-point registers are not accessible in the debugger, >> even though they were dumped. Preserving the original e_flags enables >> GDB and other tools to properly interpret the dump contents. >> >> Signed-off-by: Svetlana Parfenova <svetlana.parfenova@syntacore.com> >> --- >> fs/Kconfig.binfmt | 9 +++++++++ >> fs/binfmt_elf.c | 26 ++++++++++++++++++++------ >> include/linux/mm_types.h | 5 +++++ >> 3 files changed, 34 insertions(+), 6 deletions(-) >> >> diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt >> index bd2f530e5740..45bed2041542 100644 >> --- a/fs/Kconfig.binfmt >> +++ b/fs/Kconfig.binfmt >> @@ -184,4 +184,13 @@ config EXEC_KUNIT_TEST >> This builds the exec KUnit tests, which tests boundary conditions >> of various aspects of the exec internals. >> >> +config CORE_DUMP_USE_PROCESS_EFLAGS >> + bool "Preserve ELF e_flags from executable in core dumps" >> + depends on BINFMT_ELF && ELF_CORE && RISCV >> + default n >> + help >> + Save the ELF e_flags from the process executable at load time >> + and use it in the core dump header. This ensures the dump reflects >> + the original binary ABI. >> + >> endmenu >> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c >> index caeddccaa1fe..e5e06e11f9fc 100644 >> --- a/fs/binfmt_elf.c >> +++ b/fs/binfmt_elf.c >> @@ -1290,6 +1290,11 @@ static int load_elf_binary(struct linux_binprm *bprm) >> mm->end_data = end_data; >> mm->start_stack = bprm->p; >> >> +#ifdef CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS >> + /* stash e_flags for use in core dumps */ >> + mm->saved_e_flags = elf_ex->e_flags; >> +#endif > > Is this structure actually lost during ELF load? I thought we preserved > some more of the ELF headers during load... > As far as I can tell, the ELF header itself is not preserved beyond loading. If there's a mechanism I'm missing that saves it, please let me know. >> + >> /** >> * DOC: "brk" handling >> * >> @@ -1804,6 +1809,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, >> struct elf_thread_core_info *t; >> struct elf_prpsinfo *psinfo; >> struct core_thread *ct; >> + u16 machine; >> + u32 flags; >> >> psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); >> if (!psinfo) >> @@ -1831,17 +1838,24 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, >> return 0; >> } >> >> - /* >> - * Initialize the ELF file header. >> - */ >> - fill_elf_header(elf, phdrs, >> - view->e_machine, view->e_flags); >> + machine = view->e_machine; >> + flags = view->e_flags; >> #else >> view = NULL; >> info->thread_notes = 2; >> - fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS); >> + machine = ELF_ARCH; >> + flags = ELF_CORE_EFLAGS; >> #endif >> >> +#ifdef CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS >> + flags = dump_task->mm->saved_e_flags; >> +#endif > > This appears to clobber the value from view->e_flags. Is that right? It > feels like this change should only be needed in the default > ELF_CORE_EFLAGS case. How is view->e_flags normally set? > view->e_flags is set at compile time, and view is pointing to const struct. The override of e_flags is intentional in both cases (ELF_CORE_EFLAGS and CORE_DUMP_USE_REGSET) to allow access to the process actual e_flags, regardless of the selected method. >> + >> + /* >> + * Initialize the ELF file header. >> + */ >> + fill_elf_header(elf, phdrs, machine, flags); >> + >> /* >> * Allocate a structure for each thread. >> */ >> diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h >> index d6b91e8a66d6..39921b32e4f5 100644 >> --- a/include/linux/mm_types.h >> +++ b/include/linux/mm_types.h >> @@ -1098,6 +1098,11 @@ struct mm_struct { >> >> unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ >> >> +#ifdef CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS >> + /* the ABI-related flags from the ELF header. Used for core dump */ >> + unsigned long saved_e_flags; >> +#endif >> + >> struct percpu_counter rss_stat[NR_MM_COUNTERS]; >> >> struct linux_binfmt *binfmt; >> -- >> 2.50.1 >> > > -Kees > -- Best regards, Svetlana Parfenova
On Thu, Aug 07, 2025 at 07:13:50PM +0600, Svetlana Parfenova wrote: > On 07/08/2025 00.57, Kees Cook wrote: > > On Wed, Aug 06, 2025 at 10:18:14PM +0600, Svetlana Parfenova wrote: > > > Preserve the original ELF e_flags from the executable in the core dump > > > header instead of relying on compile-time defaults (ELF_CORE_EFLAGS or > > > value from the regset view). This ensures that ABI-specific flags in > > > the dump file match the actual binary being executed. > > > > > > Save the e_flags field during ELF binary loading (in load_elf_binary()) > > > into the mm_struct, and later retrieve it during core dump generation > > > (in fill_note_info()). Use this saved value to populate the e_flags in > > > the core dump ELF header. > > > > > > Add a new Kconfig option, CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS, to guard > > > this behavior. Although motivated by a RISC-V use case, the mechanism is > > > generic and can be applied to all architectures. > > > > In the general case, is e_flags mismatched? i.e. why hide this behind a > > Kconfig? Put another way, if I enabled this Kconfig and dumped core from > > some regular x86_64 process, will e_flags be different? > > > > The Kconfig option is currently restricted to the RISC-V architecture > because it's not clear to me whether other architectures need actual e_flags > value from ELF header. If this option is disabled, the core dump will always > use a compile time value for e_flags, regardless of which method is > selected: ELF_CORE_EFLAGS or CORE_DUMP_USE_REGSET. And this constant does > not necessarily reflect the actual e_flags of the running process (at least > on RISC-V), which can vary depending on how the binary was compiled. Thus, I > made a third method to obtain e_flags that reflects the real value. And it > is gated behind a Kconfig option, as not all users may need it. Can you check if the ELF e_flags and the hard-coded e_flags actually differ on other architectures? I'd rather avoid using the Kconfig so we can have a common execution path for all architectures. -- Kees Cook
On 08/08/2025 03.14, Kees Cook wrote: > On Thu, Aug 07, 2025 at 07:13:50PM +0600, Svetlana Parfenova wrote: >> On 07/08/2025 00.57, Kees Cook wrote: >>> On Wed, Aug 06, 2025 at 10:18:14PM +0600, Svetlana Parfenova >>> wrote: >>>> Preserve the original ELF e_flags from the executable in the >>>> core dump header instead of relying on compile-time defaults >>>> (ELF_CORE_EFLAGS or value from the regset view). This ensures >>>> that ABI-specific flags in the dump file match the actual >>>> binary being executed. >>>> >>>> Save the e_flags field during ELF binary loading (in >>>> load_elf_binary()) into the mm_struct, and later retrieve it >>>> during core dump generation (in fill_note_info()). Use this >>>> saved value to populate the e_flags in the core dump ELF >>>> header. >>>> >>>> Add a new Kconfig option, CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS, >>>> to guard this behavior. Although motivated by a RISC-V use >>>> case, the mechanism is generic and can be applied to all >>>> architectures. >>> >>> In the general case, is e_flags mismatched? i.e. why hide this >>> behind a Kconfig? Put another way, if I enabled this Kconfig and >>> dumped core from some regular x86_64 process, will e_flags be >>> different? >>> >> >> The Kconfig option is currently restricted to the RISC-V >> architecture because it's not clear to me whether other >> architectures need actual e_flags value from ELF header. If this >> option is disabled, the core dump will always use a compile time >> value for e_flags, regardless of which method is selected: >> ELF_CORE_EFLAGS or CORE_DUMP_USE_REGSET. And this constant does >> not necessarily reflect the actual e_flags of the running process >> (at least on RISC-V), which can vary depending on how the binary >> was compiled. Thus, I made a third method to obtain e_flags that >> reflects the real value. And it is gated behind a Kconfig option, >> as not all users may need it. > > Can you check if the ELF e_flags and the hard-coded e_flags actually > differ on other architectures? I'd rather avoid using the Kconfig so > we can have a common execution path for all architectures. > I checked various architectures, and most don’t use e_flags in core dumps - just zero value. For x86 this is valid since it doesn’t define values for e_flags. However, architectures like ARM do have meaningful e_flags, yet still they are set to zero in core dumps. I guess the real question isn't about core dump correctness, but whether tools like GDB actually rely on e_flags to provide debug information. Seems like most architectures either don’t use it or can operate without it. RISC-V looks like black sheep here ... GDB relies on e_flags to determine the ABI and interpret the core dump correctly. What if I rework my patch the following way: - remove Kconfig option; - add function/macro that would override e_flags with value taken from process, but it would only be applied if architecture specifies that. Would that be a better approach? -- Best regards, Svetlana Parfenova
On August 8, 2025 8:54:30 AM PDT, Svetlana Parfenova <svetlana.parfenova@syntacore.com> wrote: >On 08/08/2025 03.14, Kees Cook wrote: >> On Thu, Aug 07, 2025 at 07:13:50PM +0600, Svetlana Parfenova wrote: >>> On 07/08/2025 00.57, Kees Cook wrote: >>>> On Wed, Aug 06, 2025 at 10:18:14PM +0600, Svetlana Parfenova >>>> wrote: >>>>> Preserve the original ELF e_flags from the executable in the >>>>> core dump header instead of relying on compile-time defaults >>>>> (ELF_CORE_EFLAGS or value from the regset view). This ensures >>>>> that ABI-specific flags in the dump file match the actual >>>>> binary being executed. >>>>> >>>>> Save the e_flags field during ELF binary loading (in >>>>> load_elf_binary()) into the mm_struct, and later retrieve it >>>>> during core dump generation (in fill_note_info()). Use this >>>>> saved value to populate the e_flags in the core dump ELF >>>>> header. >>>>> >>>>> Add a new Kconfig option, CONFIG_CORE_DUMP_USE_PROCESS_EFLAGS, >>>>> to guard this behavior. Although motivated by a RISC-V use >>>>> case, the mechanism is generic and can be applied to all >>>>> architectures. >>>> >>>> In the general case, is e_flags mismatched? i.e. why hide this >>>> behind a Kconfig? Put another way, if I enabled this Kconfig and >>>> dumped core from some regular x86_64 process, will e_flags be >>>> different? >>>> >>> >>> The Kconfig option is currently restricted to the RISC-V >>> architecture because it's not clear to me whether other >>> architectures need actual e_flags value from ELF header. If this >>> option is disabled, the core dump will always use a compile time >>> value for e_flags, regardless of which method is selected: >>> ELF_CORE_EFLAGS or CORE_DUMP_USE_REGSET. And this constant does not necessarily reflect the actual e_flags of the running process >>> (at least on RISC-V), which can vary depending on how the binary >>> was compiled. Thus, I made a third method to obtain e_flags that >>> reflects the real value. And it is gated behind a Kconfig option, >>> as not all users may need it. >> >> Can you check if the ELF e_flags and the hard-coded e_flags actually differ on other architectures? I'd rather avoid using the Kconfig so >> we can have a common execution path for all architectures. >> > >I checked various architectures, and most don’t use e_flags in core >dumps - just zero value. For x86 this is valid since it doesn’t define >values for e_flags. However, architectures like ARM do have meaningful >e_flags, yet still they are set to zero in core dumps. I guess the real >question isn't about core dump correctness, but whether tools like GDB >actually rely on e_flags to provide debug information. Seems like most >architectures either don’t use it or can operate without it. RISC-V >looks like black sheep here ... GDB relies on e_flags to determine the >ABI and interpret the core dump correctly. > >What if I rework my patch the following way: >- remove Kconfig option; >- add function/macro that would override e_flags with value taken from >process, but it would only be applied if architecture specifies that. > >Would that be a better approach? Yeah! Let's see what that looks like. :) -- Kees Cook
Some architectures, such as RISC-V, use the ELF e_flags field to encode
ABI-specific information (e.g., ISA extensions, fpu support). Debuggers
like GDB rely on these flags in core dumps to correctly interpret
optional register sets. If the flags are missing or incorrect, GDB may
warn and ignore valid data, for example:
warning: Unexpected size of section '.reg2/213' in core file.
This can prevent access to fpu or other architecture-specific registers
even when they were dumped.
Save the e_flags field during ELF binary loading (in load_elf_binary())
into the mm_struct, and later retrieve it during core dump generation
(in fill_note_info()). A new macro ELF_CORE_USE_PROCESS_EFLAGS allows
architectures to enable this behavior - currently just RISC-V.
Signed-off-by: Svetlana Parfenova <svetlana.parfenova@syntacore.com>
---
Changes in v2:
- Remove usage of Kconfig option.
- Add an architecture-optional macro to set process e_flags. Enabled
by defining ELF_CORE_USE_PROCESS_EFLAGS. Defaults to no-op if not
used.
arch/riscv/include/asm/elf.h | 1 +
fs/binfmt_elf.c | 34 ++++++++++++++++++++++++++++------
include/linux/mm_types.h | 3 +++
3 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h
index c7aea7886d22..5d9f0ac851ee 100644
--- a/arch/riscv/include/asm/elf.h
+++ b/arch/riscv/include/asm/elf.h
@@ -20,6 +20,7 @@
* These are used to set parameters in the core dumps.
*/
#define ELF_ARCH EM_RISCV
+#define ELF_CORE_USE_PROCESS_EFLAGS
#ifndef ELF_CLASS
#ifdef CONFIG_64BIT
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index caeddccaa1fe..e52b1e077218 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -66,6 +66,14 @@
#define elf_check_fdpic(ex) false
#endif
+#ifdef ELF_CORE_USE_PROCESS_EFLAGS
+#define elf_coredump_get_process_eflags(dump_task, e_flags) \
+ (*(e_flags) = (dump_task)->mm->saved_e_flags)
+#else
+#define elf_coredump_get_process_eflags(dump_task, e_flags) \
+ do { (void)(dump_task); (void)(e_flags); } while (0)
+#endif
+
static int load_elf_binary(struct linux_binprm *bprm);
/*
@@ -1290,6 +1298,9 @@ static int load_elf_binary(struct linux_binprm *bprm)
mm->end_data = end_data;
mm->start_stack = bprm->p;
+ /* stash e_flags for use in core dumps */
+ mm->saved_e_flags = elf_ex->e_flags;
+
/**
* DOC: "brk" handling
*
@@ -1804,6 +1815,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
struct elf_thread_core_info *t;
struct elf_prpsinfo *psinfo;
struct core_thread *ct;
+ u16 machine;
+ u32 flags;
psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
if (!psinfo)
@@ -1831,17 +1844,26 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
return 0;
}
- /*
- * Initialize the ELF file header.
- */
- fill_elf_header(elf, phdrs,
- view->e_machine, view->e_flags);
+ machine = view->e_machine;
+ flags = view->e_flags;
#else
view = NULL;
info->thread_notes = 2;
- fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS);
+ machine = ELF_ARCH;
+ flags = ELF_CORE_EFLAGS;
#endif
+ /*
+ * Override ELF e_flags with value taken from process,
+ * if arch wants to.
+ */
+ elf_coredump_get_process_eflags(dump_task, &flags);
+
+ /*
+ * Initialize the ELF file header.
+ */
+ fill_elf_header(elf, phdrs, machine, flags);
+
/*
* Allocate a structure for each thread.
*/
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index d6b91e8a66d6..e46f554f8d91 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -1098,6 +1098,9 @@ struct mm_struct {
unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
+ /* the ABI-related flags from the ELF header. Used for core dump */
+ unsigned long saved_e_flags;
+
struct percpu_counter rss_stat[NR_MM_COUNTERS];
struct linux_binfmt *binfmt;
--
2.50.1
On Mon, Aug 11, 2025 at 03:53:28PM +0600, Svetlana Parfenova wrote: > Some architectures, such as RISC-V, use the ELF e_flags field to encode > ABI-specific information (e.g., ISA extensions, fpu support). Debuggers > like GDB rely on these flags in core dumps to correctly interpret > optional register sets. If the flags are missing or incorrect, GDB may > warn and ignore valid data, for example: > > warning: Unexpected size of section '.reg2/213' in core file. > > This can prevent access to fpu or other architecture-specific registers > even when they were dumped. > > Save the e_flags field during ELF binary loading (in load_elf_binary()) > into the mm_struct, and later retrieve it during core dump generation > (in fill_note_info()). A new macro ELF_CORE_USE_PROCESS_EFLAGS allows > architectures to enable this behavior - currently just RISC-V. > > Signed-off-by: Svetlana Parfenova <svetlana.parfenova@syntacore.com> > --- > Changes in v2: > - Remove usage of Kconfig option. > - Add an architecture-optional macro to set process e_flags. Enabled > by defining ELF_CORE_USE_PROCESS_EFLAGS. Defaults to no-op if not > used. > > arch/riscv/include/asm/elf.h | 1 + > fs/binfmt_elf.c | 34 ++++++++++++++++++++++++++++------ > include/linux/mm_types.h | 3 +++ > 3 files changed, 32 insertions(+), 6 deletions(-) > > diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h > index c7aea7886d22..5d9f0ac851ee 100644 > --- a/arch/riscv/include/asm/elf.h > +++ b/arch/riscv/include/asm/elf.h > @@ -20,6 +20,7 @@ > * These are used to set parameters in the core dumps. > */ > #define ELF_ARCH EM_RISCV > +#define ELF_CORE_USE_PROCESS_EFLAGS Let's move this to the per-arch Kconfig instead, that way we can use it in other places. Maybe call in CONFIG_ARCH_HAS_ELF_CORE_EFLAGS? > > #ifndef ELF_CLASS > #ifdef CONFIG_64BIT > diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c > index caeddccaa1fe..e52b1e077218 100644 > --- a/fs/binfmt_elf.c > +++ b/fs/binfmt_elf.c > @@ -66,6 +66,14 @@ > #define elf_check_fdpic(ex) false > #endif > > +#ifdef ELF_CORE_USE_PROCESS_EFLAGS > +#define elf_coredump_get_process_eflags(dump_task, e_flags) \ > + (*(e_flags) = (dump_task)->mm->saved_e_flags) > +#else > +#define elf_coredump_get_process_eflags(dump_task, e_flags) \ > + do { (void)(dump_task); (void)(e_flags); } while (0) > +#endif Let's make specific set/get helpers here, instead. static inline u32 coredump_get_mm_eflags(struct mm_struct *mm, u32 flags) { #ifdef CONFIG_ARCH_HAS_ELF_CORE_EFLAGS flags = mm->saved_e_flags; #else return flags; } static inline void coredump_set_mm_eflags(struct mm_struct *mm, u32 flags) { #ifdef CONFIG_ARCH_HAS_ELF_CORE_EFLAGS mm->saved_e_flags = flags; #endif } > + > static int load_elf_binary(struct linux_binprm *bprm); > > /* > @@ -1290,6 +1298,9 @@ static int load_elf_binary(struct linux_binprm *bprm) > mm->end_data = end_data; > mm->start_stack = bprm->p; > > + /* stash e_flags for use in core dumps */ > + mm->saved_e_flags = elf_ex->e_flags; Then this is: coredump_set_mm_eflags(mm, elf_ex->e_flags); > + > /** > * DOC: "brk" handling > * > @@ -1804,6 +1815,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, > struct elf_thread_core_info *t; > struct elf_prpsinfo *psinfo; > struct core_thread *ct; > + u16 machine; > + u32 flags; > > psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); > if (!psinfo) > @@ -1831,17 +1844,26 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, > return 0; > } > > - /* > - * Initialize the ELF file header. > - */ > - fill_elf_header(elf, phdrs, > - view->e_machine, view->e_flags); > + machine = view->e_machine; > + flags = view->e_flags; > #else > view = NULL; > info->thread_notes = 2; > - fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS); > + machine = ELF_ARCH; > + flags = ELF_CORE_EFLAGS; > #endif > > + /* > + * Override ELF e_flags with value taken from process, > + * if arch wants to. > + */ > + elf_coredump_get_process_eflags(dump_task, &flags); And this is: flags = coredump_get_mm_eflags(dump_task->mm, flags); > + > + /* > + * Initialize the ELF file header. > + */ > + fill_elf_header(elf, phdrs, machine, flags); > + > /* > * Allocate a structure for each thread. > */ > diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h > index d6b91e8a66d6..e46f554f8d91 100644 > --- a/include/linux/mm_types.h > +++ b/include/linux/mm_types.h > @@ -1098,6 +1098,9 @@ struct mm_struct { > > unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ > And then add: #ifdef CONFIG_ARCH_HAS_ELF_CORE_EFLAGS > + /* the ABI-related flags from the ELF header. Used for core dump */ > + unsigned long saved_e_flags; #endif around this part > + > struct percpu_counter rss_stat[NR_MM_COUNTERS]; > > struct linux_binfmt *binfmt; > -- > 2.50.1 > -- Kees Cook
On 26/08/2025 00.17, Kees Cook wrote: > On Mon, Aug 11, 2025 at 03:53:28PM +0600, Svetlana Parfenova wrote: >> Some architectures, such as RISC-V, use the ELF e_flags field to encode >> ABI-specific information (e.g., ISA extensions, fpu support). Debuggers >> like GDB rely on these flags in core dumps to correctly interpret >> optional register sets. If the flags are missing or incorrect, GDB may >> warn and ignore valid data, for example: >> >> warning: Unexpected size of section '.reg2/213' in core file. >> >> This can prevent access to fpu or other architecture-specific registers >> even when they were dumped. >> >> Save the e_flags field during ELF binary loading (in load_elf_binary()) >> into the mm_struct, and later retrieve it during core dump generation >> (in fill_note_info()). A new macro ELF_CORE_USE_PROCESS_EFLAGS allows >> architectures to enable this behavior - currently just RISC-V. >> >> Signed-off-by: Svetlana Parfenova <svetlana.parfenova@syntacore.com> >> --- >> Changes in v2: >> - Remove usage of Kconfig option. >> - Add an architecture-optional macro to set process e_flags. Enabled >> by defining ELF_CORE_USE_PROCESS_EFLAGS. Defaults to no-op if not >> used. >> >> arch/riscv/include/asm/elf.h | 1 + >> fs/binfmt_elf.c | 34 ++++++++++++++++++++++++++++------ >> include/linux/mm_types.h | 3 +++ >> 3 files changed, 32 insertions(+), 6 deletions(-) >> >> diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h >> index c7aea7886d22..5d9f0ac851ee 100644 >> --- a/arch/riscv/include/asm/elf.h >> +++ b/arch/riscv/include/asm/elf.h >> @@ -20,6 +20,7 @@ >> * These are used to set parameters in the core dumps. >> */ >> #define ELF_ARCH EM_RISCV >> +#define ELF_CORE_USE_PROCESS_EFLAGS > > Let's move this to the per-arch Kconfig instead, that way we can use it > in other places. Maybe call in CONFIG_ARCH_HAS_ELF_CORE_EFLAGS? > >> >> #ifndef ELF_CLASS >> #ifdef CONFIG_64BIT >> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c >> index caeddccaa1fe..e52b1e077218 100644 >> --- a/fs/binfmt_elf.c >> +++ b/fs/binfmt_elf.c >> @@ -66,6 +66,14 @@ >> #define elf_check_fdpic(ex) false >> #endif >> >> +#ifdef ELF_CORE_USE_PROCESS_EFLAGS >> +#define elf_coredump_get_process_eflags(dump_task, e_flags) \ >> + (*(e_flags) = (dump_task)->mm->saved_e_flags) >> +#else >> +#define elf_coredump_get_process_eflags(dump_task, e_flags) \ >> + do { (void)(dump_task); (void)(e_flags); } while (0) >> +#endif > > Let's make specific set/get helpers here, instead. > > static inline > u32 coredump_get_mm_eflags(struct mm_struct *mm, u32 flags) > { > #ifdef CONFIG_ARCH_HAS_ELF_CORE_EFLAGS > flags = mm->saved_e_flags; > #else > return flags; > } > > static inline > void coredump_set_mm_eflags(struct mm_struct *mm, u32 flags) > { > #ifdef CONFIG_ARCH_HAS_ELF_CORE_EFLAGS > mm->saved_e_flags = flags; > #endif > } > > >> + >> static int load_elf_binary(struct linux_binprm *bprm); >> >> /* >> @@ -1290,6 +1298,9 @@ static int load_elf_binary(struct linux_binprm *bprm) >> mm->end_data = end_data; >> mm->start_stack = bprm->p; >> >> + /* stash e_flags for use in core dumps */ >> + mm->saved_e_flags = elf_ex->e_flags; > > Then this is: > > coredump_set_mm_eflags(mm, elf_ex->e_flags); > >> + >> /** >> * DOC: "brk" handling >> * >> @@ -1804,6 +1815,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, >> struct elf_thread_core_info *t; >> struct elf_prpsinfo *psinfo; >> struct core_thread *ct; >> + u16 machine; >> + u32 flags; >> >> psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); >> if (!psinfo) >> @@ -1831,17 +1844,26 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, >> return 0; >> } >> >> - /* >> - * Initialize the ELF file header. >> - */ >> - fill_elf_header(elf, phdrs, >> - view->e_machine, view->e_flags); >> + machine = view->e_machine; >> + flags = view->e_flags; >> #else >> view = NULL; >> info->thread_notes = 2; >> - fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS); >> + machine = ELF_ARCH; >> + flags = ELF_CORE_EFLAGS; >> #endif >> >> + /* >> + * Override ELF e_flags with value taken from process, >> + * if arch wants to. >> + */ >> + elf_coredump_get_process_eflags(dump_task, &flags); > > And this is: > > flags = coredump_get_mm_eflags(dump_task->mm, flags); > >> + >> + /* >> + * Initialize the ELF file header. >> + */ >> + fill_elf_header(elf, phdrs, machine, flags); >> + >> /* >> * Allocate a structure for each thread. >> */ >> diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h >> index d6b91e8a66d6..e46f554f8d91 100644 >> --- a/include/linux/mm_types.h >> +++ b/include/linux/mm_types.h >> @@ -1098,6 +1098,9 @@ struct mm_struct { >> >> unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ >> > > And then add: > > #ifdef CONFIG_ARCH_HAS_ELF_CORE_EFLAGS > >> + /* the ABI-related flags from the ELF header. Used for core dump */ >> + unsigned long saved_e_flags; > > #endif > > around this part > >> + >> struct percpu_counter rss_stat[NR_MM_COUNTERS]; >> >> struct linux_binfmt *binfmt; >> -- >> 2.50.1 >> > Thank you for review! I have addressed your comments in v3 of the patch. -- Best regards, Svetlana Parfenova
© 2016 - 2025 Red Hat, Inc.