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 */