From c749e4bfbddc6ea72e111a822c2668c4a48368e5 Mon Sep 17 00:00:00 2001 From: wangjianyu3 Date: Mon, 21 Aug 2023 20:28:56 +0800 Subject: [PATCH] binfmt: ELF support load to LMA Load all sections to LMA not VMA, so the startup code(e.g. start.S) need relocate .data section to the final address(VMA) and zero .bss section by self. For example, SiFli and Actions: Background: Device with small sram, Bootloader run in sram and psram, need boot to Application, with memory overlap and without XIP. VMA of .data is in "psram" and LMA in "rom", if not enable `ELF_LOADTO_LMA`, ELF loader will load the section to VMA (will fill bootloader itself). Signed-off-by: wangjianyu3 --- binfmt/elf.c | 17 ++++++++++ binfmt/libelf/Kconfig | 8 +++++ binfmt/libelf/libelf.h | 14 ++++++++ binfmt/libelf/libelf_load.c | 59 ++++++++++++++++++++++++++++++++- binfmt/libelf/libelf_sections.c | 59 +++++++++++++++++++++++++++++++++ binfmt/libelf/libelf_uninit.c | 6 ++++ include/nuttx/binfmt/elf.h | 1 + 7 files changed, 163 insertions(+), 1 deletion(-) diff --git a/binfmt/elf.c b/binfmt/elf.c index d78abcff987..5e3ed98380b 100644 --- a/binfmt/elf.c +++ b/binfmt/elf.c @@ -142,6 +142,23 @@ static void elf_dumploadinfo(FAR struct elf_loadinfo_s *loadinfo) binfo(" e_shnum: %d\n", loadinfo->ehdr.e_shnum); binfo(" e_shstrndx: %d\n", loadinfo->ehdr.e_shstrndx); + if (loadinfo->phdr && loadinfo->ehdr.e_phnum > 0) + { + for (i = 0; i < loadinfo->ehdr.e_phnum; i++) + { + FAR Elf_Phdr *phdr = &loadinfo->phdr[i]; + binfo("Programs %d:\n", i); + binfo(" p_type: %08jx\n", (uintmax_t)phdr->p_type); + binfo(" p_offset: %08jx\n", (uintmax_t)phdr->p_offset); + binfo(" p_vaddr: %08jx\n", (uintmax_t)phdr->p_vaddr); + binfo(" p_paddr: %08jx\n", (uintmax_t)phdr->p_paddr); + binfo(" p_filesz: %08jx\n", (uintmax_t)phdr->p_filesz); + binfo(" p_memsz: %08jx\n", (uintmax_t)phdr->p_memsz); + binfo(" p_flags: %08jx\n", (uintmax_t)phdr->p_flags); + binfo(" p_align: %08x\n", phdr->p_align); + } + } + if (loadinfo->shdr && loadinfo->ehdr.e_shnum > 0) { for (i = 0; i < loadinfo->ehdr.e_shnum; i++) diff --git a/binfmt/libelf/Kconfig b/binfmt/libelf/Kconfig index 951e56b317e..dbba7921e40 100644 --- a/binfmt/libelf/Kconfig +++ b/binfmt/libelf/Kconfig @@ -73,3 +73,11 @@ config ELF_COREDUMP The memory state embeds a snapshot of all segments mapped in the memory space of the program. The CPU state contains register values when the core dump has been generated. + +config ELF_LOADTO_LMA + bool "ELF load sections to LMA" + default n + ---help--- + Load all section to LMA not VMA, so the startup code(e.g. start.S) need + relocate .data section to the final address(VMA) and zero .bss section + by self. diff --git a/binfmt/libelf/libelf.h b/binfmt/libelf/libelf.h index a13aa74d6e7..fdc02c1ce22 100644 --- a/binfmt/libelf/libelf.h +++ b/binfmt/libelf/libelf.h @@ -65,6 +65,20 @@ int elf_verifyheader(FAR const Elf_Ehdr *header); int elf_read(FAR struct elf_loadinfo_s *loadinfo, FAR uint8_t *buffer, size_t readsize, off_t offset); +/**************************************************************************** + * Name: elf_loadphdrs + * + * Description: + * Loads program headers into memory. + * + * Returned Value: + * 0 (OK) is returned on success and a negated errno is returned on + * failure. + * + ****************************************************************************/ + +int elf_loadphdrs(FAR struct elf_loadinfo_s *loadinfo); + /**************************************************************************** * Name: elf_loadshdrs * diff --git a/binfmt/libelf/libelf_load.c b/binfmt/libelf/libelf_load.c index e7f7ebd06b8..2a6be23bc6b 100644 --- a/binfmt/libelf/libelf_load.c +++ b/binfmt/libelf/libelf_load.c @@ -123,6 +123,41 @@ static void elf_elfsize(FAR struct elf_loadinfo_s *loadinfo) loadinfo->datasize = datasize; } +#ifdef CONFIG_ELF_LOADTO_LMA +/**************************************************************************** + * Name: elf_vma2lma + * + * Description: + * Convert section`s VMA to LMA according to PhysAddr(p_paddr) of + * Program Header. + * + * Returned Value: + * 0 (OK) is returned on success and a negated errno is returned on + * failure. + * + ****************************************************************************/ + +static int elf_vma2lma(FAR struct elf_loadinfo_s *loadinfo, + FAR Elf_Shdr *shdr, FAR Elf_Addr *lma) +{ + int i; + + for (i = 0; i < loadinfo->ehdr.e_phnum; i++) + { + FAR Elf_Phdr *phdr = &loadinfo->phdr[i]; + + if (shdr->sh_addr >= phdr->p_vaddr && + shdr->sh_addr < phdr->p_vaddr + phdr->p_memsz) + { + *lma = phdr->p_paddr + shdr->sh_addr - phdr->p_vaddr; + return 0; + } + } + + return -ENOENT; +} +#endif + /**************************************************************************** * Name: elf_loadfile * @@ -178,9 +213,20 @@ static inline int elf_loadfile(FAR struct elf_loadinfo_s *loadinfo) { if (shdr->sh_type != SHT_NOBITS) { + Elf_Addr addr = shdr->sh_addr; + +#ifdef CONFIG_ELF_LOADTO_LMA + ret = elf_vma2lma(loadinfo, shdr, &addr); + if (ret < 0) + { + berr("ERROR: Failed to convert addr %d: %d\n", i, ret); + return ret; + } +#endif + /* Read the section data from sh_offset to specified region */ - ret = elf_read(loadinfo, (FAR uint8_t *)shdr->sh_addr, + ret = elf_read(loadinfo, (FAR uint8_t *)addr, shdr->sh_size, shdr->sh_offset); if (ret < 0) { @@ -189,6 +235,7 @@ static inline int elf_loadfile(FAR struct elf_loadinfo_s *loadinfo) } } +#ifndef CONFIG_ELF_LOADTO_LMA /* If there is no data in an allocated section, then the * allocated section must be cleared. */ @@ -197,6 +244,7 @@ static inline int elf_loadfile(FAR struct elf_loadinfo_s *loadinfo) { memset((FAR uint8_t *)shdr->sh_addr, 0, shdr->sh_size); } +#endif continue; } @@ -284,6 +332,15 @@ int elf_load(FAR struct elf_loadinfo_s *loadinfo) binfo("loadinfo: %p\n", loadinfo); DEBUGASSERT(loadinfo && loadinfo->file.f_inode); + /* Load program headers into memory */ + + ret = elf_loadphdrs(loadinfo); + if (ret < 0) + { + berr("ERROR: elf_loadphdrs failed: %d\n", ret); + goto errout_with_buffers; + } + /* Load section headers into memory */ ret = elf_loadshdrs(loadinfo); diff --git a/binfmt/libelf/libelf_sections.c b/binfmt/libelf/libelf_sections.c index 7866d23252f..5de75a8855e 100644 --- a/binfmt/libelf/libelf_sections.c +++ b/binfmt/libelf/libelf_sections.c @@ -165,6 +165,65 @@ static inline int elf_sectname(FAR struct elf_loadinfo_s *loadinfo, * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: elf_loadphdrs + * + * Description: + * Loads program headers into memory. + * + * Returned Value: + * 0 (OK) is returned on success and a negated errno is returned on + * failure. + * + ****************************************************************************/ + +int elf_loadphdrs(FAR struct elf_loadinfo_s *loadinfo) +{ + size_t phdrsize; + int ret; + + DEBUGASSERT(loadinfo->phdr == NULL); + + /* Verify that there are programs */ + + if (loadinfo->ehdr.e_phnum < 1) + { + berr("No programs(?)\n"); + return -EINVAL; + } + + /* Get the total size of the program header table */ + + phdrsize = (size_t)loadinfo->ehdr.e_phentsize * + (size_t)loadinfo->ehdr.e_phnum; + if (loadinfo->ehdr.e_phoff + phdrsize > loadinfo->filelen) + { + berr("Insufficient space in file for program header table\n"); + return -ESPIPE; + } + + /* Allocate memory to hold a working copy of the program header table */ + + loadinfo->phdr = (FAR FAR Elf_Phdr *)kmm_malloc(phdrsize); + if (!loadinfo->phdr) + { + berr("Failed to allocate the program header table. Size: %ld\n", + (long)phdrsize); + return -ENOMEM; + } + + /* Read the program header table into memory */ + + ret = elf_read(loadinfo, (FAR uint8_t *)loadinfo->phdr, phdrsize, + loadinfo->ehdr.e_phoff); + if (ret < 0) + { + berr("Failed to read program header table: %d\n", ret); + } + + return ret; +} + /**************************************************************************** * Name: elf_loadshdrs * diff --git a/binfmt/libelf/libelf_uninit.c b/binfmt/libelf/libelf_uninit.c index 5c2754696fe..c94426d4cf8 100644 --- a/binfmt/libelf/libelf_uninit.c +++ b/binfmt/libelf/libelf_uninit.c @@ -94,6 +94,12 @@ int elf_freebuffers(FAR struct elf_loadinfo_s *loadinfo) { /* Release all working allocations */ + if (loadinfo->phdr) + { + kmm_free((FAR void *)loadinfo->phdr); + loadinfo->phdr = NULL; + } + if (loadinfo->shdr) { kmm_free(loadinfo->shdr); diff --git a/include/nuttx/binfmt/elf.h b/include/nuttx/binfmt/elf.h index 80f7749756d..7c32f5ff123 100644 --- a/include/nuttx/binfmt/elf.h +++ b/include/nuttx/binfmt/elf.h @@ -91,6 +91,7 @@ struct elf_loadinfo_s int filemode; /* Mode of the file system */ Elf_Ehdr ehdr; /* Buffered ELF file header */ + FAR Elf_Phdr *phdr; /* Buffered ELF program headers */ FAR Elf_Shdr *shdr; /* Buffered ELF section headers */ uint8_t *iobuffer; /* File I/O buffer */