Skip to content

Commit eb3009d

Browse files
committed
LLEXT: fix advanced uses of detached sections
When detached sections are used, STB_GLOBAL relocations also have to be processed depending on the relocation type. This commit unified STB_GLOBAL and STB_LOCAL flows by making them use the same relocation type parser, adds support for R_XTENSA_GLOB_DAT and R_XTENSA_JMP_SLOT type relocations on Xtensa and fixes STT_SECTION address calculation for such sections. Signed-off-by: Guennadi Liakhovetski <[email protected]>
1 parent da9c7c4 commit eb3009d

File tree

3 files changed

+137
-44
lines changed

3 files changed

+137
-44
lines changed

arch/xtensa/core/elf.c

Lines changed: 102 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,78 +12,143 @@
1212

1313
LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL);
1414

15+
/*
16+
* ELF relocation tables on Xtensa contain relocations of different types. They
17+
* specify how the relocation should be performed. Which relocations are used
18+
* depends on the type of the ELF object (e.g. shared or partially linked
19+
* object), structure of the object (single or multiple source files), compiler
20+
* flags used (e.g. -fPIC), etc. Also not all relocation table entries should be
21+
* acted upon. Some of them describe relocations that have already been
22+
* resolved by the linker. We have to distinguish them from actionable
23+
* relocations and only need to handle the latter ones.
24+
*/
1525
#define R_XTENSA_NONE 0
1626
#define R_XTENSA_32 1
1727
#define R_XTENSA_RTLD 2
1828
#define R_XTENSA_GLOB_DAT 3
1929
#define R_XTENSA_JMP_SLOT 4
2030
#define R_XTENSA_RELATIVE 5
2131
#define R_XTENSA_PLT 6
32+
#define R_XTENSA_ASM_EXPAND 11
2233
#define R_XTENSA_SLOT0_OP 20
2334

24-
/**
25-
* @brief Architecture specific function for relocating shared elf
26-
*
27-
* Elf files contain a series of relocations described in multiple sections.
28-
* These relocation instructions are architecture specific and each architecture
29-
* supporting modules must implement this.
30-
*/
31-
void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext,
32-
const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset)
35+
static void xtensa_elf_relocate(struct llext_loader *ldr, struct llext *ext,
36+
const elf_rela_t *rel, uint8_t *text, uintptr_t addr,
37+
uint8_t *loc, int type, uint32_t stb)
3338
{
34-
uint8_t *text = ext->mem[LLEXT_MEM_TEXT];
35-
int type = ELF32_R_TYPE(rel->r_info);
36-
elf_word *got_entry = (elf_word *)(text + got_offset);
37-
uintptr_t sh_addr;
38-
39-
if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
40-
elf_shdr_t *shdr = llext_peek(ldr, ldr->hdr.e_shoff +
41-
sym->st_shndx * ldr->hdr.e_shentsize);
42-
sh_addr = shdr->sh_addr ? : (uintptr_t)llext_peek(ldr, shdr->sh_offset);
43-
} else {
44-
sh_addr = ldr->sects[LLEXT_MEM_TEXT].sh_addr;
45-
}
39+
elf_word *got_entry = (elf_word *)loc;
4640

4741
switch (type) {
4842
case R_XTENSA_RELATIVE:
49-
/* Relocate a local symbol: Xtensa specific */
50-
*got_entry += (uintptr_t)text - sh_addr;
43+
/* Relocate a local symbol: Xtensa specific. Seems to only be used with PIC */
44+
*got_entry += (uintptr_t)text - addr;
45+
break;
46+
case R_XTENSA_GLOB_DAT:
47+
case R_XTENSA_JMP_SLOT:
48+
if (stb == STB_GLOBAL) {
49+
*got_entry = addr;
50+
}
5151
break;
5252
case R_XTENSA_32:
53-
*got_entry += sh_addr;
53+
/* Used for both LOCAL and GLOBAL bindings */
54+
*got_entry += addr;
5455
break;
5556
case R_XTENSA_SLOT0_OP:
57+
/* Apparently only actionable with LOCAL bindings */
5658
;
57-
uint8_t *opc = (uint8_t *)got_entry;
58-
59-
/* Check the opcode: is this an L32R? And does it have to be relocated? */
60-
if ((opc[0] & 0xf) != 1 || opc[1] || opc[2])
61-
break;
62-
6359
elf_sym_t rsym;
64-
6560
int ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset +
6661
ELF_R_SYM(rel->r_info) * sizeof(elf_sym_t));
62+
6763
if (!ret) {
6864
ret = llext_read(ldr, &rsym, sizeof(elf_sym_t));
6965
}
70-
if (ret)
66+
if (ret) {
67+
LOG_ERR("Failed to read a symbol table entry, LLEXT linking might fail.");
7168
return;
69+
}
7270

71+
/*
72+
* So far in all observed use-cases
73+
* llext_loaded_sect_ptr(ldr, ext, rsym.st_shndx) was already
74+
* available as the "addr" argument of this function, supplied
75+
* by arch_elf_relocate_local() from its non-STT_SECTION branch.
76+
*/
7377
uintptr_t link_addr = (uintptr_t)llext_loaded_sect_ptr(ldr, ext, rsym.st_shndx) +
7478
rsym.st_value + rel->r_addend;
75-
7679
ssize_t value = (link_addr - (((uintptr_t)got_entry + 3) & ~3)) >> 2;
7780

78-
opc[1] = value & 0xff;
79-
opc[2] = (value >> 8) & 0xff;
81+
/* Check the opcode */
82+
if ((loc[0] & 0xf) == 1 && !loc[1] && !loc[2]) {
83+
/* L32R: low nibble is 1 */
84+
loc[1] = value & 0xff;
85+
loc[2] = (value >> 8) & 0xff;
86+
} else if ((loc[0] & 0xf) == 5 && !(loc[0] & 0xc0) && !loc[1] && !loc[2]) {
87+
/* CALLn: low nibble is 5 */
88+
loc[0] = (loc[0] & 0x3f) | ((value << 6) & 0xc0);
89+
loc[1] = (value >> 2) & 0xff;
90+
loc[2] = (value >> 10) & 0xff;
91+
} else {
92+
LOG_DBG("%p: unhandled OPC or no relocation %02x%02x%02x inf %#x offs %#x",
93+
(void *)loc, loc[2], loc[1], loc[0],
94+
rel->r_info, rel->r_offset);
95+
break;
96+
}
8097

98+
break;
99+
case R_XTENSA_ASM_EXPAND:
100+
/* Nothing to do */
81101
break;
82102
default:
83-
LOG_DBG("unsupported relocation type %u", type);
103+
LOG_DBG("Unsupported relocation type %u", type);
84104

85105
return;
86106
}
87107

88-
LOG_DBG("relocation to %#x type %u at %p", *got_entry, type, (void *)got_entry);
108+
LOG_DBG("Applied relocation to %#x type %u at %p",
109+
*(uint32_t *)((uintptr_t)got_entry & ~3), type, (void *)got_entry);
110+
}
111+
112+
/**
113+
* @brief Architecture specific function for STB_LOCAL ELF relocations
114+
*/
115+
void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext, const elf_rela_t *rel,
116+
const elf_sym_t *sym, size_t got_offset,
117+
const struct llext_load_param *ldr_parm)
118+
{
119+
uint8_t *text = ext->mem[LLEXT_MEM_TEXT];
120+
uint8_t *loc = text + got_offset;
121+
int type = ELF32_R_TYPE(rel->r_info);
122+
uintptr_t sh_addr;
123+
124+
if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
125+
elf_shdr_t *shdr = llext_peek(ldr, ldr->hdr.e_shoff +
126+
sym->st_shndx * ldr->hdr.e_shentsize);
127+
sh_addr = shdr->sh_addr &&
128+
(!ldr_parm->section_detached || !ldr_parm->section_detached(shdr)) ?
129+
shdr->sh_addr : (uintptr_t)llext_peek(ldr, shdr->sh_offset);
130+
} else {
131+
sh_addr = ldr->sects[LLEXT_MEM_TEXT].sh_addr;
132+
}
133+
134+
xtensa_elf_relocate(ldr, ext, rel, text, sh_addr, loc, type, ELF_ST_BIND(sym->st_info));
135+
}
136+
137+
/**
138+
* @brief Architecture specific function for STB_GLOBAL ELF relocations
139+
*/
140+
void arch_elf_relocate_global(struct llext_loader *ldr, struct llext *ext, const elf_rela_t *rel,
141+
const elf_sym_t *sym, size_t got_offset, const void *link_addr)
142+
{
143+
uint8_t *text = ext->mem[LLEXT_MEM_TEXT];
144+
elf_word *got_entry = (elf_word *)(text + got_offset);
145+
int type = ELF32_R_TYPE(rel->r_info);
146+
147+
/* For global relocations we expect the initial value for R_XTENSA_RELATIVE to be zero */
148+
if (type == R_XTENSA_RELATIVE && *got_entry) {
149+
LOG_WRN("global: non-zero relative value %#x", *got_entry);
150+
}
151+
152+
xtensa_elf_relocate(ldr, ext, rel, text, (uintptr_t)link_addr, (uint8_t *)got_entry, type,
153+
ELF_ST_BIND(sym->st_info));
89154
}

include/zephyr/llext/llext.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,16 +336,31 @@ int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc,
336336
ssize_t llext_find_section(struct llext_loader *loader, const char *search_name);
337337

338338
/**
339-
* @brief Architecture specific function for updating addresses via relocation table
339+
* @brief Architecture specific function for local binding relocations
340340
*
341341
* @param[in] loader Extension loader data and context
342342
* @param[in] ext Extension to call function in
343343
* @param[in] rel Relocation data provided by elf
344344
* @param[in] sym Corresponding symbol table entry
345-
* @param[in] got_offset Offset within a relocation table
345+
* @param[in] got_offset Offset within a relocation table or in the code
346+
* @param[in] ldr_parm Loader parameters
346347
*/
347-
void arch_elf_relocate_local(struct llext_loader *loader, struct llext *ext,
348-
const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset);
348+
void arch_elf_relocate_local(struct llext_loader *loader, struct llext *ext, const elf_rela_t *rel,
349+
const elf_sym_t *sym, size_t got_offset,
350+
const struct llext_load_param *ldr_parm);
351+
352+
/**
353+
* @brief Architecture specific function for global binding relocations
354+
*
355+
* @param[in] loader Extension loader data and context
356+
* @param[in] ext Extension to call function in
357+
* @param[in] rel Relocation data provided by elf
358+
* @param[in] sym Corresponding symbol table entry
359+
* @param[in] got_offset Offset within a relocation table or in the code
360+
* @param[in] link_addr target address for table-based relocations
361+
*/
362+
void arch_elf_relocate_global(struct llext_loader *loader, struct llext *ext, const elf_rela_t *rel,
363+
const elf_sym_t *sym, size_t got_offset, const void *link_addr);
349364

350365
/**
351366
* @}

subsys/llext/llext_link.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ __weak int arch_elf_relocate(elf_rela_t *rel, uintptr_t loc,
3333
}
3434

3535
__weak void arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext,
36-
const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset)
36+
const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset,
37+
const struct llext_load_param *ldr_parm)
38+
{
39+
}
40+
41+
__weak void arch_elf_relocate_global(struct llext_loader *ldr, struct llext *ext,
42+
const elf_rela_t *rel, const elf_sym_t *sym, size_t got_offset,
43+
const void *link_addr)
3744
{
3845
}
3946

@@ -249,11 +256,12 @@ static void llext_link_plt(struct llext_loader *ldr, struct llext *ext, elf_shdr
249256
}
250257

251258
/* Resolve the symbol */
252-
*(const void **)(text + got_offset) = link_addr;
259+
arch_elf_relocate_global(ldr, ext, &rela, &sym, got_offset, link_addr);
253260
break;
254261
case STB_LOCAL:
255262
if (ldr_parm->relocate_local) {
256-
arch_elf_relocate_local(ldr, ext, &rela, &sym, got_offset);
263+
arch_elf_relocate_local(ldr, ext, &rela, &sym, got_offset,
264+
ldr_parm);
257265
}
258266
}
259267

@@ -327,6 +335,11 @@ int llext_link(struct llext_loader *ldr, struct llext *ext, const struct llext_l
327335
strcmp(name, ".rela.dyn") == 0) {
328336
tgt = NULL;
329337
} else {
338+
/*
339+
* Entries in .rel.X and .rela.X sections describe references in
340+
* section .X to local or global symbols. They point to entries
341+
* in the symbol table, describing respective symbols
342+
*/
330343
tgt = ldr->sect_hdrs + shdr->sh_info;
331344
}
332345

0 commit comments

Comments
 (0)