llext: support multiple string and symbol tables

The ELF format allows for multiple string and symbol tables with
complex references between them. This is especially evident when
debugging information is included.

This patch fixes the issues that have been identified with multiple
string tables to allow LLEXT to properly parse those files:

* The symbol table used by LLEXT (LLEXT_MEM_SYMTAB) is now chosen
  depending on the loaded file type, and other tables are ignored.
  This change is also applied to the SLID injection script.

* The LLEXT string table (LLEXT_MEM_SYMTAB) is now correctly identified
  by the symbol table reference, instead of picking the first one.

* VMA range checks only make sense for allocated sections.

Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
This commit is contained in:
Luca Burelli 2025-01-14 19:22:36 +01:00 committed by Benjamin Cabé
parent 61c61a5528
commit 7094eae111
2 changed files with 37 additions and 32 deletions

View File

@ -36,20 +36,22 @@ class LLEXTSymtabPreparator():
self.elf = ELFFile(self.elf_fd)
def _find_symtab(self):
supported_symtab_sections = [
".symtab",
".dynsym",
]
e_type = self.elf.header['e_type']
if e_type == 'ET_DYN':
symtab_name = ".dynsym"
elif e_type == 'ET_REL':
symtab_name = ".symtab"
else:
self.log.error(f"unexpected ELF file type {e_type}")
return None
symtab = None
for section_name in supported_symtab_sections:
symtab = self.elf.get_section_by_name(section_name)
if not isinstance(symtab, SymbolTableSection):
self.log.debug(f"section {section_name} not found.")
else:
self.log.info(f"processing '{section_name}' symbol table...")
self.log.debug(f"(symbol table is at file offset 0x{symtab['sh_offset']:X})")
break
symtab = self.elf.get_section_by_name(symtab_name)
if not isinstance(symtab, SymbolTableSection):
self.log.debug(f"section {symtab_name} not found.")
return None
self.log.info(f"processing symbol table from '{symtab_name}'...")
self.log.debug(f"(symbol table is at file offset 0x{symtab['sh_offset']:X})")
return symtab
def _find_imports_in_symtab(self, symtab):

View File

@ -152,6 +152,8 @@ static int llext_load_elf_data(struct llext_loader *ldr, struct llext *ext)
static int llext_find_tables(struct llext_loader *ldr, struct llext *ext)
{
int table_cnt, i;
int shstrtab_ndx = ldr->hdr.e_shstrndx;
int strtab_ndx = -1;
memset(ldr->sects, 0, sizeof(ldr->sects));
@ -171,28 +173,28 @@ static int llext_find_tables(struct llext_loader *ldr, struct llext *ext)
shdr->sh_link,
shdr->sh_info);
switch (shdr->sh_type) {
case SHT_SYMTAB:
case SHT_DYNSYM:
if (shdr->sh_type == SHT_SYMTAB && ldr->hdr.e_type == ET_REL) {
LOG_DBG("symtab at %d", i);
ldr->sects[LLEXT_MEM_SYMTAB] = *shdr;
ldr->sect_map[i].mem_idx = LLEXT_MEM_SYMTAB;
strtab_ndx = shdr->sh_link;
table_cnt++;
break;
case SHT_STRTAB:
if (ldr->hdr.e_shstrndx == i) {
LOG_DBG("shstrtab at %d", i);
ldr->sects[LLEXT_MEM_SHSTRTAB] = *shdr;
ldr->sect_map[i].mem_idx = LLEXT_MEM_SHSTRTAB;
} else {
LOG_DBG("strtab at %d", i);
ldr->sects[LLEXT_MEM_STRTAB] = *shdr;
ldr->sect_map[i].mem_idx = LLEXT_MEM_STRTAB;
}
} else if (shdr->sh_type == SHT_DYNSYM && ldr->hdr.e_type == ET_DYN) {
LOG_DBG("dynsym at %d", i);
ldr->sects[LLEXT_MEM_SYMTAB] = *shdr;
ldr->sect_map[i].mem_idx = LLEXT_MEM_SYMTAB;
strtab_ndx = shdr->sh_link;
table_cnt++;
} else if (shdr->sh_type == SHT_STRTAB && i == shstrtab_ndx) {
LOG_DBG("shstrtab at %d", i);
ldr->sects[LLEXT_MEM_SHSTRTAB] = *shdr;
ldr->sect_map[i].mem_idx = LLEXT_MEM_SHSTRTAB;
table_cnt++;
} else if (shdr->sh_type == SHT_STRTAB && i == strtab_ndx) {
LOG_DBG("strtab at %d", i);
ldr->sects[LLEXT_MEM_STRTAB] = *shdr;
ldr->sect_map[i].mem_idx = LLEXT_MEM_STRTAB;
table_cnt++;
break;
default:
break;
}
}
@ -395,9 +397,10 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext,
continue;
}
if (ldr->hdr.e_type == ET_DYN) {
if ((ldr->hdr.e_type == ET_DYN) &&
(x->sh_flags & SHF_ALLOC) && (y->sh_flags & SHF_ALLOC)) {
/*
* Test all merged VMA ranges for overlaps
* Test regions that have VMA ranges for overlaps
*/
if ((x->sh_addr <= y->sh_addr &&
x->sh_addr + x->sh_size > y->sh_addr) ||