[PATCH 07/23] elf-parse: add section flags, symbol binding and a read-only mapping

From: Lorenzo Stoakes (ARM)

Date: Tue Sep 08 2026 - 17:13:04 EST


Extend elf-parse to be able to read the symbol table of vmlinux in kallyms.

This requires the ability to open ELF files in read-only mode, so provide
elf_map_ro() to do so.

It also requires accessors for section flags and symbol bindings, so
provide these via shdr_flags() and sym_bind().

Also, check for the file being an ELF file first in elf_parse(). This is
the logical thing to check for first, but additionally prevents kallsyms
from having to check this it self.

Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <ljs@xxxxxxxxxx>
---
scripts/elf-parse.c | 48 +++++++++++++++++++++++++++++++++++-------------
scripts/elf-parse.h | 19 +++++++++++++++++++
2 files changed, 54 insertions(+), 13 deletions(-)

diff --git a/scripts/elf-parse.c b/scripts/elf-parse.c
index 99869ff91a8c..87aa95b1e5e1 100644
--- a/scripts/elf-parse.c
+++ b/scripts/elf-parse.c
@@ -17,15 +17,21 @@ struct elf_funcs elf_parser;
* Get the whole file as a programming convenience in order to avoid
* malloc+lseek+read+free of many pieces. If successful, then mmap
* avoids copying unused pieces; else just read the whole file.
- * Open for both read and write.
+ * Open for both read and write if writable is true, otherwise open
+ * read-only.
*/
-static void *map_file(char const *fname, size_t *size)
+static void *map_file(char const *fname, size_t *size, bool writable)
{
- int fd;
+ int fd, prot = PROT_READ, flags = MAP_PRIVATE;
struct stat sb;
void *addr = NULL;

- fd = open(fname, O_RDWR);
+ if (writable) {
+ prot |= PROT_WRITE;
+ flags = MAP_SHARED;
+ }
+
+ fd = open(fname, writable ? O_RDWR : O_RDONLY);
if (fd < 0) {
perror(fname);
return NULL;
@@ -39,7 +45,7 @@ static void *map_file(char const *fname, size_t *size)
goto out;
}

- addr = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ addr = mmap(0, sb.st_size, prot, flags, fd, 0);
if (addr == MAP_FAILED) {
fprintf(stderr, "Could not mmap file: %s\n", fname);
goto out;
@@ -57,6 +63,12 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
Elf_Ehdr *ehdr = addr;
uint16_t type;

+ if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
+ ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
+ fprintf(stderr, "unrecognized ELF file %s\n", fname);
+ return -1;
+ }
+
switch (ehdr->e32.e_ident[EI_DATA]) {
case ELFDATA2LSB:
elf_parser.r = rle;
@@ -78,12 +90,6 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
return -1;
}

- if (memcmp(ELFMAG, ehdr->e32.e_ident, SELFMAG) != 0 ||
- ehdr->e32.e_ident[EI_VERSION] != EV_CURRENT) {
- fprintf(stderr, "unrecognized ELF file %s\n", fname);
- return -1;
- }
-
type = elf_parser.r2(&ehdr->e32.e_type);
if (!((1 << type) & types)) {
fprintf(stderr, "Invalid ELF type file %s\n", fname);
@@ -103,7 +109,9 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
elf_parser.shdr_name = shdr32_name;
elf_parser.shdr_type = shdr32_type;
elf_parser.shdr_entsize = shdr32_entsize;
+ elf_parser.shdr_flags = shdr32_flags;
elf_parser.sym_type = sym32_type;
+ elf_parser.sym_bind = sym32_bind;
elf_parser.sym_name = sym32_name;
elf_parser.sym_value = sym32_value;
elf_parser.sym_shndx = sym32_shndx;
@@ -133,7 +141,9 @@ static int elf_parse(const char *fname, void *addr, uint32_t types)
elf_parser.shdr_name = shdr64_name;
elf_parser.shdr_type = shdr64_type;
elf_parser.shdr_entsize = shdr64_entsize;
+ elf_parser.shdr_flags = shdr64_flags;
elf_parser.sym_type = sym64_type;
+ elf_parser.sym_bind = sym64_bind;
elf_parser.sym_name = sym64_name;
elf_parser.sym_value = sym64_value;
elf_parser.sym_shndx = sym64_shndx;
@@ -174,12 +184,13 @@ int elf_map_long_size(void *addr)
return ehdr->e32.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
}

-void *elf_map(char const *fname, size_t *size, uint32_t types)
+static void *__elf_map(char const *fname, size_t *size, uint32_t types,
+ bool writable)
{
void *addr;
int ret;

- addr = map_file(fname, size);
+ addr = map_file(fname, size, writable);
if (!addr)
return NULL;

@@ -192,6 +203,17 @@ void *elf_map(char const *fname, size_t *size, uint32_t types)
return addr;
}

+void *elf_map(char const *fname, size_t *size, uint32_t types)
+{
+ return __elf_map(fname, size, types, true);
+}
+
+/* For tools that only read the file. */
+void *elf_map_ro(char const *fname, size_t *size, uint32_t types)
+{
+ return __elf_map(fname, size, types, false);
+}
+
void elf_unmap(void *addr, size_t size)
{
munmap(addr, size);
diff --git a/scripts/elf-parse.h b/scripts/elf-parse.h
index f4411e03069d..453286e63032 100644
--- a/scripts/elf-parse.h
+++ b/scripts/elf-parse.h
@@ -37,10 +37,12 @@ struct elf_funcs {
uint64_t (*shdr_offset)(Elf_Shdr *shdr);
uint64_t (*shdr_size)(Elf_Shdr *shdr);
uint64_t (*shdr_entsize)(Elf_Shdr *shdr);
+ uint64_t (*shdr_flags)(Elf_Shdr *shdr);
uint32_t (*shdr_link)(Elf_Shdr *shdr);
uint32_t (*shdr_name)(Elf_Shdr *shdr);
uint32_t (*shdr_type)(Elf_Shdr *shdr);
uint8_t (*sym_type)(Elf_Sym *sym);
+ uint8_t (*sym_bind)(Elf_Sym *sym);
uint32_t (*sym_name)(Elf_Sym *sym);
uint64_t (*sym_value)(Elf_Sym *sym);
uint16_t (*sym_shndx)(Elf_Sym *sym);
@@ -143,6 +145,7 @@ SHDR_ADDR(addr)
SHDR_ADDR(offset)
SHDR_ADDR(size)
SHDR_ADDR(entsize)
+SHDR_ADDR(flags)

SHDR_WORD(link)
SHDR_WORD(name)
@@ -211,6 +214,21 @@ static inline uint8_t sym_type(Elf_Sym *sym)
return elf_parser.sym_type(sym);
}

+static inline uint8_t sym64_bind(Elf_Sym *sym)
+{
+ return ELF64_ST_BIND(sym->e64.st_info);
+}
+
+static inline uint8_t sym32_bind(Elf_Sym *sym)
+{
+ return ELF32_ST_BIND(sym->e32.st_info);
+}
+
+static inline uint8_t sym_bind(Elf_Sym *sym)
+{
+ return elf_parser.sym_bind(sym);
+}
+
SYM_ADDR(value)
SYM_WORD(name)
SYM_HALF(shndx)
@@ -298,6 +316,7 @@ static inline void w8le(uint64_t val, uint64_t *x)
}

void *elf_map(char const *fname, size_t *size, uint32_t types);
+void *elf_map_ro(char const *fname, size_t *size, uint32_t types);
void elf_unmap(void *addr, size_t size);
int elf_map_machine(void *addr);
int elf_map_long_size(void *addr);

--
2.55.0