llext: refactor: use cached section headers
The section headers are now available in the loader structure, so we can use those directly instead of reading them from the ELF file every time. This commit contains no logic changes; it removes all copies of the header loading code and replaces them with direct access to the cached section headers. Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
This commit is contained in:
parent
817bbda5cc
commit
08eb314c35
2 changed files with 53 additions and 116 deletions
|
|
@ -163,35 +163,23 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext,
|
||||||
int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local)
|
int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local)
|
||||||
{
|
{
|
||||||
uintptr_t loc = 0;
|
uintptr_t loc = 0;
|
||||||
elf_shdr_t shdr;
|
|
||||||
elf_rela_t rel;
|
elf_rela_t rel;
|
||||||
elf_sym_t sym;
|
elf_sym_t sym;
|
||||||
elf_word rel_cnt = 0;
|
elf_word rel_cnt = 0;
|
||||||
const char *name;
|
const char *name;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
for (i = 0, pos = ldr->hdr.e_shoff;
|
for (i = 0; i < ldr->sect_cnt; ++i) {
|
||||||
i < ldr->hdr.e_shnum - 1;
|
elf_shdr_t *shdr = ldr->sect_hdrs + i;
|
||||||
i++, pos += ldr->hdr.e_shentsize) {
|
|
||||||
ret = llext_seek(ldr, pos);
|
|
||||||
if (ret != 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = llext_read(ldr, &shdr, sizeof(elf_shdr_t));
|
|
||||||
if (ret != 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* find relocation sections */
|
/* find relocation sections */
|
||||||
if (shdr.sh_type != SHT_REL && shdr.sh_type != SHT_RELA) {
|
if (shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rel_cnt = shdr.sh_size / shdr.sh_entsize;
|
rel_cnt = shdr->sh_size / shdr->sh_entsize;
|
||||||
|
|
||||||
name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr.sh_name);
|
name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr->sh_name);
|
||||||
|
|
||||||
if (strcmp(name, ".rel.text") == 0) {
|
if (strcmp(name, ".rel.text") == 0) {
|
||||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_TEXT];
|
loc = (uintptr_t)ext->mem[LLEXT_MEM_TEXT];
|
||||||
|
|
@ -207,13 +195,13 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local)
|
||||||
loc = (uintptr_t)ext->mem[LLEXT_MEM_EXPORT];
|
loc = (uintptr_t)ext->mem[LLEXT_MEM_EXPORT];
|
||||||
} else if (strcmp(name, ".rela.plt") == 0 ||
|
} else if (strcmp(name, ".rela.plt") == 0 ||
|
||||||
strcmp(name, ".rela.dyn") == 0) {
|
strcmp(name, ".rela.dyn") == 0) {
|
||||||
llext_link_plt(ldr, ext, &shdr, do_local, NULL);
|
llext_link_plt(ldr, ext, shdr, do_local, NULL);
|
||||||
continue;
|
continue;
|
||||||
} else if (strncmp(name, ".rela", 5) == 0 && strlen(name) > 5) {
|
} else if (strncmp(name, ".rela", 5) == 0 && strlen(name) > 5) {
|
||||||
elf_shdr_t *tgt = llext_section_by_name(ldr, name + 5);
|
elf_shdr_t *tgt = llext_section_by_name(ldr, name + 5);
|
||||||
|
|
||||||
if (tgt)
|
if (tgt)
|
||||||
llext_link_plt(ldr, ext, &shdr, do_local, tgt);
|
llext_link_plt(ldr, ext, shdr, do_local, tgt);
|
||||||
continue;
|
continue;
|
||||||
} else if (strcmp(name, ".rel.dyn") == 0) {
|
} else if (strcmp(name, ".rel.dyn") == 0) {
|
||||||
/* we assume that first load segment starts at MEM_TEXT */
|
/* we assume that first load segment starts at MEM_TEXT */
|
||||||
|
|
@ -221,16 +209,16 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local)
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DBG("relocation section %s (%d) linked to section %d has %zd relocations",
|
LOG_DBG("relocation section %s (%d) linked to section %d has %zd relocations",
|
||||||
name, i, shdr.sh_link, (size_t)rel_cnt);
|
name, i, shdr->sh_link, (size_t)rel_cnt);
|
||||||
|
|
||||||
for (int j = 0; j < rel_cnt; j++) {
|
for (int j = 0; j < rel_cnt; j++) {
|
||||||
/* get each relocation entry */
|
/* get each relocation entry */
|
||||||
ret = llext_seek(ldr, shdr.sh_offset + j * shdr.sh_entsize);
|
ret = llext_seek(ldr, shdr->sh_offset + j * shdr->sh_entsize);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = llext_read(ldr, &rel, shdr.sh_entsize);
|
ret = llext_read(ldr, &rel, shdr->sh_entsize);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -271,7 +259,7 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, bool do_local)
|
||||||
if (link_addr == 0) {
|
if (link_addr == 0) {
|
||||||
LOG_ERR("Undefined symbol with no entry in "
|
LOG_ERR("Undefined symbol with no entry in "
|
||||||
"symbol table %s, offset %zd, link section %d",
|
"symbol table %s, offset %zd, link section %d",
|
||||||
name, (size_t)rel.r_offset, shdr.sh_link);
|
name, (size_t)rel.r_offset, shdr->sh_link);
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,23 +40,17 @@ static const char ELF_MAGIC[] = {0x7f, 'E', 'L', 'F'};
|
||||||
|
|
||||||
elf_shdr_t *llext_section_by_name(struct llext_loader *ldr, const char *search_name)
|
elf_shdr_t *llext_section_by_name(struct llext_loader *ldr, const char *search_name)
|
||||||
{
|
{
|
||||||
elf_shdr_t *shdr;
|
int i;
|
||||||
unsigned int i;
|
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
for (i = 0, pos = ldr->hdr.e_shoff;
|
|
||||||
i < ldr->hdr.e_shnum;
|
|
||||||
i++, pos += ldr->hdr.e_shentsize) {
|
|
||||||
shdr = llext_peek(ldr, pos);
|
|
||||||
if (!shdr) {
|
|
||||||
/* The peek() method isn't supported */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (i = 0; i < ldr->sect_cnt; ++i) {
|
||||||
|
elf_shdr_t *shdr = ldr->sect_hdrs + i;
|
||||||
const char *name = llext_peek(ldr,
|
const char *name = llext_peek(ldr,
|
||||||
ldr->sects[LLEXT_MEM_SHSTRTAB].sh_offset +
|
ldr->sects[LLEXT_MEM_SHSTRTAB].sh_offset +
|
||||||
shdr->sh_name);
|
shdr->sh_name);
|
||||||
|
if (!name) {
|
||||||
|
/* The peek() method isn't supported */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (!strcmp(name, search_name)) {
|
if (!strcmp(name, search_name)) {
|
||||||
return shdr;
|
return shdr;
|
||||||
}
|
}
|
||||||
|
|
@ -120,8 +114,6 @@ static int llext_load_elf_data(struct llext_loader *ldr, struct llext *ext)
|
||||||
|
|
||||||
ldr->sect_cnt = ldr->hdr.e_shnum;
|
ldr->sect_cnt = ldr->hdr.e_shnum;
|
||||||
|
|
||||||
memset(ldr->sects, 0, sizeof(ldr->sects));
|
|
||||||
|
|
||||||
size_t sect_map_sz = ldr->sect_cnt * sizeof(ldr->sect_map[0]);
|
size_t sect_map_sz = ldr->sect_cnt * sizeof(ldr->sect_map[0]);
|
||||||
|
|
||||||
ldr->sect_map = llext_alloc(sect_map_sz);
|
ldr->sect_map = llext_alloc(sect_map_sz);
|
||||||
|
|
@ -165,58 +157,42 @@ static int llext_load_elf_data(struct llext_loader *ldr, struct llext *ext)
|
||||||
*/
|
*/
|
||||||
static int llext_find_tables(struct llext_loader *ldr)
|
static int llext_find_tables(struct llext_loader *ldr)
|
||||||
{
|
{
|
||||||
int sect_cnt, i, ret;
|
int sect_cnt, i;
|
||||||
size_t pos;
|
|
||||||
elf_shdr_t shdr;
|
|
||||||
|
|
||||||
ldr->sects[LLEXT_MEM_SHSTRTAB] =
|
memset(ldr->sects, 0, sizeof(ldr->sects));
|
||||||
ldr->sects[LLEXT_MEM_STRTAB] =
|
|
||||||
ldr->sects[LLEXT_MEM_SYMTAB] = (elf_shdr_t){0};
|
|
||||||
|
|
||||||
/* Find symbol and string tables */
|
/* Find symbol and string tables */
|
||||||
for (i = 0, sect_cnt = 0, pos = ldr->hdr.e_shoff;
|
for (i = 0, sect_cnt = 0; i < ldr->sect_cnt; ++i) {
|
||||||
i < ldr->hdr.e_shnum && sect_cnt < 3;
|
elf_shdr_t *shdr = ldr->sect_hdrs + i;
|
||||||
i++, pos += ldr->hdr.e_shentsize) {
|
|
||||||
ret = llext_seek(ldr, pos);
|
|
||||||
if (ret != 0) {
|
|
||||||
LOG_ERR("failed seeking to position %zu\n", pos);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = llext_read(ldr, &shdr, sizeof(elf_shdr_t));
|
|
||||||
if (ret != 0) {
|
|
||||||
LOG_ERR("failed reading section header at position %zu\n", pos);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_DBG("section %d at 0x%zx: name %d, type %d, flags 0x%zx, "
|
LOG_DBG("section %d at 0x%zx: name %d, type %d, flags 0x%zx, "
|
||||||
"addr 0x%zx, size %zd, link %d, info %d",
|
"addr 0x%zx, size %zd, link %d, info %d",
|
||||||
i,
|
i,
|
||||||
(size_t)shdr.sh_offset,
|
(size_t)shdr->sh_offset,
|
||||||
shdr.sh_name,
|
shdr->sh_name,
|
||||||
shdr.sh_type,
|
shdr->sh_type,
|
||||||
(size_t)shdr.sh_flags,
|
(size_t)shdr->sh_flags,
|
||||||
(size_t)shdr.sh_addr,
|
(size_t)shdr->sh_addr,
|
||||||
(size_t)shdr.sh_size,
|
(size_t)shdr->sh_size,
|
||||||
shdr.sh_link,
|
shdr->sh_link,
|
||||||
shdr.sh_info);
|
shdr->sh_info);
|
||||||
|
|
||||||
switch (shdr.sh_type) {
|
switch (shdr->sh_type) {
|
||||||
case SHT_SYMTAB:
|
case SHT_SYMTAB:
|
||||||
case SHT_DYNSYM:
|
case SHT_DYNSYM:
|
||||||
LOG_DBG("symtab at %d", i);
|
LOG_DBG("symtab at %d", i);
|
||||||
ldr->sects[LLEXT_MEM_SYMTAB] = shdr;
|
ldr->sects[LLEXT_MEM_SYMTAB] = *shdr;
|
||||||
ldr->sect_map[i] = LLEXT_MEM_SYMTAB;
|
ldr->sect_map[i] = LLEXT_MEM_SYMTAB;
|
||||||
sect_cnt++;
|
sect_cnt++;
|
||||||
break;
|
break;
|
||||||
case SHT_STRTAB:
|
case SHT_STRTAB:
|
||||||
if (ldr->hdr.e_shstrndx == i) {
|
if (ldr->hdr.e_shstrndx == i) {
|
||||||
LOG_DBG("shstrtab at %d", i);
|
LOG_DBG("shstrtab at %d", i);
|
||||||
ldr->sects[LLEXT_MEM_SHSTRTAB] = shdr;
|
ldr->sects[LLEXT_MEM_SHSTRTAB] = *shdr;
|
||||||
ldr->sect_map[i] = LLEXT_MEM_SHSTRTAB;
|
ldr->sect_map[i] = LLEXT_MEM_SHSTRTAB;
|
||||||
} else {
|
} else {
|
||||||
LOG_DBG("strtab at %d", i);
|
LOG_DBG("strtab at %d", i);
|
||||||
ldr->sects[LLEXT_MEM_STRTAB] = shdr;
|
ldr->sects[LLEXT_MEM_STRTAB] = *shdr;
|
||||||
ldr->sect_map[i] = LLEXT_MEM_STRTAB;
|
ldr->sect_map[i] = LLEXT_MEM_STRTAB;
|
||||||
}
|
}
|
||||||
sect_cnt++;
|
sect_cnt++;
|
||||||
|
|
@ -241,37 +217,25 @@ static int llext_find_tables(struct llext_loader *ldr)
|
||||||
*/
|
*/
|
||||||
static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
||||||
{
|
{
|
||||||
int i, j, ret;
|
int i, j;
|
||||||
size_t pos;
|
|
||||||
elf_shdr_t shdr;
|
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
for (i = 0, pos = ldr->hdr.e_shoff;
|
for (i = 0; i < ldr->sect_cnt; ++i) {
|
||||||
i < ldr->hdr.e_shnum;
|
elf_shdr_t *shdr = ldr->sect_hdrs + i;
|
||||||
i++, pos += ldr->hdr.e_shentsize) {
|
|
||||||
ret = llext_seek(ldr, pos);
|
|
||||||
if (ret != 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = llext_read(ldr, &shdr, sizeof(elf_shdr_t));
|
name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr->sh_name);
|
||||||
if (ret != 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = llext_string(ldr, ext, LLEXT_MEM_SHSTRTAB, shdr.sh_name);
|
|
||||||
|
|
||||||
/* Identify the section type by its flags */
|
/* Identify the section type by its flags */
|
||||||
enum llext_mem mem_idx;
|
enum llext_mem mem_idx;
|
||||||
|
|
||||||
switch (shdr.sh_type) {
|
switch (shdr->sh_type) {
|
||||||
case SHT_NOBITS:
|
case SHT_NOBITS:
|
||||||
mem_idx = LLEXT_MEM_BSS;
|
mem_idx = LLEXT_MEM_BSS;
|
||||||
break;
|
break;
|
||||||
case SHT_PROGBITS:
|
case SHT_PROGBITS:
|
||||||
if (shdr.sh_flags & SHF_EXECINSTR) {
|
if (shdr->sh_flags & SHF_EXECINSTR) {
|
||||||
mem_idx = LLEXT_MEM_TEXT;
|
mem_idx = LLEXT_MEM_TEXT;
|
||||||
} else if (shdr.sh_flags & SHF_WRITE) {
|
} else if (shdr->sh_flags & SHF_WRITE) {
|
||||||
mem_idx = LLEXT_MEM_DATA;
|
mem_idx = LLEXT_MEM_DATA;
|
||||||
} else {
|
} else {
|
||||||
mem_idx = LLEXT_MEM_RODATA;
|
mem_idx = LLEXT_MEM_RODATA;
|
||||||
|
|
@ -288,8 +252,8 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mem_idx == LLEXT_MEM_COUNT ||
|
if (mem_idx == LLEXT_MEM_COUNT ||
|
||||||
!(shdr.sh_flags & SHF_ALLOC) ||
|
!(shdr->sh_flags & SHF_ALLOC) ||
|
||||||
shdr.sh_size == 0) {
|
shdr->sh_size == 0) {
|
||||||
LOG_DBG("section %d name %s skipped", i, name);
|
LOG_DBG("section %d name %s skipped", i, name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -301,10 +265,10 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
||||||
|
|
||||||
if (sect->sh_type == SHT_NULL) {
|
if (sect->sh_type == SHT_NULL) {
|
||||||
/* First section of this type, copy all info */
|
/* First section of this type, copy all info */
|
||||||
*sect = shdr;
|
memcpy(sect, shdr, sizeof(*sect));
|
||||||
} else {
|
} else {
|
||||||
/* Make sure the sections are compatible before merging */
|
/* Make sure the sections are compatible before merging */
|
||||||
if (shdr.sh_flags != sect->sh_flags) {
|
if (shdr->sh_flags != sect->sh_flags) {
|
||||||
LOG_ERR("Unsupported section flags for %s (mem %d)",
|
LOG_ERR("Unsupported section flags for %s (mem %d)",
|
||||||
name, mem_idx);
|
name, mem_idx);
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
|
|
@ -325,8 +289,8 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
||||||
* merging these sections, make sure the delta
|
* merging these sections, make sure the delta
|
||||||
* in VMAs matches that of file offsets.
|
* in VMAs matches that of file offsets.
|
||||||
*/
|
*/
|
||||||
if (shdr.sh_addr - sect->sh_addr !=
|
if (shdr->sh_addr - sect->sh_addr !=
|
||||||
shdr.sh_offset - sect->sh_offset) {
|
shdr->sh_offset - sect->sh_offset) {
|
||||||
LOG_ERR("Incompatible section addresses "
|
LOG_ERR("Incompatible section addresses "
|
||||||
"for %s (mem %d)", name, mem_idx);
|
"for %s (mem %d)", name, mem_idx);
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
|
|
@ -337,10 +301,10 @@ static int llext_map_sections(struct llext_loader *ldr, struct llext *ext)
|
||||||
* Extend the current section to include the new one
|
* Extend the current section to include the new one
|
||||||
* (overlaps are detected later)
|
* (overlaps are detected later)
|
||||||
*/
|
*/
|
||||||
size_t address = MIN(sect->sh_addr, shdr.sh_addr);
|
size_t address = MIN(sect->sh_addr, shdr->sh_addr);
|
||||||
size_t bot_ofs = MIN(sect->sh_offset, shdr.sh_offset);
|
size_t bot_ofs = MIN(sect->sh_offset, shdr->sh_offset);
|
||||||
size_t top_ofs = MAX(sect->sh_offset + sect->sh_size,
|
size_t top_ofs = MAX(sect->sh_offset + sect->sh_size,
|
||||||
shdr.sh_offset + shdr.sh_size);
|
shdr->sh_offset + shdr->sh_size);
|
||||||
|
|
||||||
sect->sh_addr = address;
|
sect->sh_addr = address;
|
||||||
sect->sh_offset = bot_ofs;
|
sect->sh_offset = bot_ofs;
|
||||||
|
|
@ -552,30 +516,15 @@ static int llext_copy_symbols(struct llext_loader *ldr, struct llext *ext,
|
||||||
base = ext->mem[ldr->sect_map[sect]];
|
base = ext->mem[ldr->sect_map[sect]];
|
||||||
section_addr = ldr->sects[ldr->sect_map[sect]].sh_addr;
|
section_addr = ldr->sects[ldr->sect_map[sect]].sh_addr;
|
||||||
} else {
|
} else {
|
||||||
/* Section header isn't stored, have to read it */
|
elf_shdr_t *shdr = ldr->sect_hdrs + sect;
|
||||||
size_t shdr_pos = ldr->hdr.e_shoff + sect * ldr->hdr.e_shentsize;
|
|
||||||
elf_shdr_t shdr;
|
|
||||||
|
|
||||||
ret = llext_seek(ldr, shdr_pos);
|
base = llext_peek(ldr, shdr->sh_offset);
|
||||||
if (ret != 0) {
|
|
||||||
LOG_ERR("failed seeking to position %zu\n", shdr_pos);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = llext_read(ldr, &shdr, sizeof(elf_shdr_t));
|
|
||||||
if (ret != 0) {
|
|
||||||
LOG_ERR("failed reading section header at position %zu\n",
|
|
||||||
shdr_pos);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
base = llext_peek(ldr, shdr.sh_offset);
|
|
||||||
if (!base) {
|
if (!base) {
|
||||||
LOG_ERR("cannot handle arbitrary sections without .peek\n");
|
LOG_ERR("cannot handle arbitrary sections without .peek\n");
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
section_addr = shdr.sh_addr;
|
section_addr = shdr->sh_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pre_located) {
|
if (pre_located) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue