diff --git a/arch/arm/include/elf.h b/arch/arm/include/elf.h index 460cb34ad4efb..b0bcf2c676cf5 100644 --- a/arch/arm/include/elf.h +++ b/arch/arm/include/elf.h @@ -264,10 +264,72 @@ #define DT_ARM_PREEMPTMAP 0x70000002 #define DT_ARM_RESERVED2 0x70000003 +/* Loader state the FDPIC relocations need: the object's data base, and the + * descriptor pool cursor, which must survive from one relocation to the + * next. It arrives through the arch_data channel. + */ + +#define ARCH_ELFDATA 1 + +#define ARCH_ELFDATA_INIT(d, l) \ + do \ + { \ + (d)->fdpic = (l)->fdpic; \ + (d)->gotbase = (l)->gotbase; \ + (d)->descpool = (l)->descpool; \ + (d)->ndesc = (l)->ndesc; \ + (d)->usedesc = (l)->usedesc; \ + } \ + while (0) + +#define ARCH_ELFDATA_FINI(d, l) \ + do \ + { \ + (l)->usedesc = (d)->usedesc; \ + } \ + while (0) + +/* Which relocation table is being walked. A relocation out of DT_JMPREL + * overwrites a word the linker pre-loaded with a lazy binding stub, which is + * not an addend and must not be added to. + */ + +#define ARCH_ELFDATA_SET_PLTREL(d, v) (d)->pltrel = (v) + /**************************************************************************** * Public Types ****************************************************************************/ +#ifndef __ASSEMBLY__ + +/* A function descriptor is what an FDPIC function pointer is. Its shape is + * common, so struct fdpic_desc_s from include/nuttx/fdpic.h serves; only a + * pointer to one is kept here, thus the tag is enough. + */ + +struct fdpic_desc_s; + +struct arch_elfdata_s +{ + uint8_t fdpic; /* The object is an FDPIC one */ + uintptr_t gotbase; /* DT_PLTGOT: this object's data base */ + uint16_t ndesc; /* Capacity, in descriptors */ + uint16_t usedesc; /* Next free slot */ + + /* The pool the descriptors are taken from */ + + FAR struct fdpic_desc_s *descpool; + + uint8_t pltrel; /* Relocation comes from DT_JMPREL, so the word + * it overwrites is a lazy binding stub and not + * an addend + */ +}; + +typedef struct arch_elfdata_s arch_elfdata_t; + +#endif /* __ASSEMBLY__ */ + typedef struct __EIT_entry { unsigned long fnoffset; diff --git a/libs/libc/elf/elf_bind.c b/libs/libc/elf/elf_bind.c index 4e0727222dd46..fae5b5f3cec1e 100644 --- a/libs/libc/elf/elf_bind.c +++ b/libs/libc/elf/elf_bind.c @@ -58,6 +58,25 @@ # define ARCH_ELFDATA_PARM NULL #endif +/* Move loader state in and out of the arch_data block, and say which + * relocation table is being walked. Nothing for an architecture whose + * relocations do not need any of it. + */ + +#if defined(ARCH_ELFDATA) && defined(ARCH_ELFDATA_SET_PLTREL) +# define ARCH_ELFDATA_PLTREL(v) ARCH_ELFDATA_SET_PLTREL(&arch_data, v) +#else +# define ARCH_ELFDATA_PLTREL(v) +#endif + +#if defined(ARCH_ELFDATA) && defined(ARCH_ELFDATA_INIT) +# define ARCH_ELFDATA_SETUP(l) ARCH_ELFDATA_INIT(&arch_data, l) +# define ARCH_ELFDATA_TEARDOWN(l) ARCH_ELFDATA_FINI(&arch_data, l) +#else +# define ARCH_ELFDATA_SETUP(l) +# define ARCH_ELFDATA_TEARDOWN(l) +#endif + /**************************************************************************** * Private Types ****************************************************************************/ @@ -720,7 +739,8 @@ static int libelf_relocatedyn(FAR struct module_s *modp, * for it names this base. */ - loadinfo->gotbase = dyn[i].d_un.d_ptr; + loadinfo->gotbase = libelf_addr(loadinfo, + dyn[i].d_un.d_ptr); break; /* The constructor and destructor tables. Section headers are @@ -778,6 +798,13 @@ static int libelf_relocatedyn(FAR struct module_s *modp, } } + /* After the loop, because DT_PLTGOT is read there. Both relocation + * tables are walked under this one arch_data, so the pool cursor + * survives from one to the next. + */ + + ARCH_ELFDATA_SETUP(loadinfo); + symhdr = &loadinfo->shdr[loadinfo->dsymtabidx]; sym = lib_malloc(symhdr->sh_size); if (!sym) @@ -815,6 +842,10 @@ static int libelf_relocatedyn(FAR struct module_s *modp, ret = OK; lrelent = reldata.relsz[idx_rel] / reldata.relentsz[idx_rel]; + /* Say which table this is, for an architecture that cares. */ + + ARCH_ELFDATA_PLTREL(idx_rel == I_PLT); + for (i = 0; i < lrelent; i++) { /* Process each relocation entry @@ -909,7 +940,30 @@ static int libelf_relocatedyn(FAR struct module_s *modp, addr += rela->r_addend; } - *(FAR uintptr_t *)addr = (uintptr_t)ep; + /* An import may be a descriptor under FDPIC, which is + * built rather than assigned, so the relocation type + * decides what to write. Everything else stores the + * resolved address, which R_ARM_JUMP_SLOT and + * R_ARM_GLOB_DAT do, so one path serves both. + */ + + Elf_Sym extsym = + { + 0 + }; + + extsym.st_value = (uintptr_t)ep; + + ret = up_relocate(rel, &extsym, addr, ARCH_ELFDATA_PARM); + if (ret < 0) + { + berr("ERROR: Section %d reloc %d: " + "Relocation failed: %d\n", relidx, i, ret); + lib_free(sym); + lib_free(rels); + lib_free(dyn); + return ret; + } } else if (loadinfo->fdpic) { @@ -975,6 +1029,12 @@ static int libelf_relocatedyn(FAR struct module_s *modp, } } + /* Hand back what the relocations consumed. The error paths above do + * not bother: the load is being abandoned, so the cursor has no reader. + */ + + ARCH_ELFDATA_TEARDOWN(loadinfo); + lib_free(sym); lib_free(rels); lib_free(dyn); diff --git a/libs/libc/machine/arm/armv7-m/arch_elf.c b/libs/libc/machine/arm/armv7-m/arch_elf.c index f167b8c3daf55..e0468c71edef6 100644 --- a/libs/libc/machine/arm/armv7-m/arch_elf.c +++ b/libs/libc/machine/arm/armv7-m/arch_elf.c @@ -32,6 +32,7 @@ #include #include +#include /**************************************************************************** * Public Functions @@ -126,7 +127,8 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, relotype = ELF32_R_TYPE(rel->r_info); if (sym == NULL && relotype != R_ARM_NONE && relotype != R_ARM_V4BX && - relotype != R_ARM_RELATIVE && relotype != R_ARM_JUMP_SLOT) + relotype != R_ARM_RELATIVE && relotype != R_ARM_JUMP_SLOT && + relotype != R_ARM_GLOB_DAT) { return -EINVAL; } @@ -175,6 +177,97 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, } break; + case R_ARM_FUNCDESC_VALUE: + { + /* The target is a descriptor: entry point and data base. The + * addend sits in the word that becomes the entry point and + * carries the Thumb bit, so it must be kept. The base written is + * this object's own, which is what makes a callback work. + */ + + struct fdpic_desc_s *desc = + (struct fdpic_desc_s *)addr; + arch_elfdata_t *data = (arch_elfdata_t *)arch_data; + + if (data == NULL) + { + berr("ERROR: FUNCDESC_VALUE without loader state\n"); + return -EINVAL; + } + + /* A descriptor only means anything in an FDPIC object. An object + * that carries these relocations without saying it is FDPIC cannot + * be run: nothing would install its data base. + */ + + if (!data->fdpic) + { + berr("ERROR: FUNCDESC_VALUE in a non-FDPIC object\n"); + return -ENOEXEC; + } + + binfo("Performing FUNCDESC_VALUE link " + "at addr=%08" PRIxPTR " to sym=%p st_value=%08" PRIx32 "\n", + addr, sym, sym->st_value); + + if (data->pltrel) + { + /* A lazy descriptor holds its PLT stub address, not an + * addend. Overwrite it, do not add to it. + */ + + desc->entry = sym->st_value; + } + else + { + desc->entry = sym->st_value + desc->entry; + } + + desc->got = data->gotbase; + } + break; + + case R_ARM_FUNCDESC: + { + /* A pointer to a descriptor, which the loader has to supply. + * Carve one out of the pool reserved behind the writable segment + * and store its address. + */ + + struct fdpic_desc_s *desc; + arch_elfdata_t *data = (arch_elfdata_t *)arch_data; + + if (data == NULL) + { + berr("ERROR: FUNCDESC without loader state\n"); + return -EINVAL; + } + + if (!data->fdpic) + { + berr("ERROR: FUNCDESC in a non-FDPIC object\n"); + return -ENOEXEC; + } + + if (data->usedesc >= data->ndesc) + { + berr("ERROR: Out of function descriptors\n"); + return -ENOMEM; + } + + desc = data->descpool + data->usedesc++; + + binfo("Performing FUNCDESC link " + "at addr=%08" PRIxPTR " to sym=%p st_value=%08" PRIx32 "\n", + addr, sym, sym->st_value); + + desc->entry = sym->st_value + *(uint32_t *)addr; + desc->got = data->gotbase; + + *(uint32_t *)addr = (uint32_t)(uintptr_t)desc; + } + break; + case R_ARM_ABS32: case R_ARM_TARGET1: /* New ABI: TARGET1 always treated as ABS32 */ { @@ -501,6 +594,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, break; case R_ARM_RELATIVE: + case R_ARM_GLOB_DAT: case R_ARM_JUMP_SLOT: { *(uint32_t *)addr = (uint32_t)sym->st_value; diff --git a/libs/libc/machine/arm/armv8-m/arch_elf.c b/libs/libc/machine/arm/armv8-m/arch_elf.c index 27a6e673f22b9..a0e0ec998c3e2 100644 --- a/libs/libc/machine/arm/armv8-m/arch_elf.c +++ b/libs/libc/machine/arm/armv8-m/arch_elf.c @@ -32,6 +32,7 @@ #include #include +#include /**************************************************************************** * Public Functions @@ -126,7 +127,8 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, relotype = ELF32_R_TYPE(rel->r_info); if (sym == NULL && relotype != R_ARM_NONE && relotype != R_ARM_V4BX && - relotype != R_ARM_RELATIVE && relotype != R_ARM_JUMP_SLOT) + relotype != R_ARM_RELATIVE && relotype != R_ARM_JUMP_SLOT && + relotype != R_ARM_GLOB_DAT) { return -EINVAL; } @@ -175,6 +177,97 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, } break; + case R_ARM_FUNCDESC_VALUE: + { + /* The target is a descriptor: entry point and data base. The + * addend sits in the word that becomes the entry point and + * carries the Thumb bit, so it must be kept. The base written is + * this object's own, which is what makes a callback work. + */ + + struct fdpic_desc_s *desc = + (struct fdpic_desc_s *)addr; + arch_elfdata_t *data = (arch_elfdata_t *)arch_data; + + if (data == NULL) + { + berr("ERROR: FUNCDESC_VALUE without loader state\n"); + return -EINVAL; + } + + /* A descriptor only means anything in an FDPIC object. An object + * that carries these relocations without saying it is FDPIC cannot + * be run: nothing would install its data base. + */ + + if (!data->fdpic) + { + berr("ERROR: FUNCDESC_VALUE in a non-FDPIC object\n"); + return -ENOEXEC; + } + + binfo("Performing FUNCDESC_VALUE link " + "at addr=%08" PRIxPTR " to sym=%p st_value=%08" PRIx32 "\n", + addr, sym, sym->st_value); + + if (data->pltrel) + { + /* A lazy descriptor holds its PLT stub address, not an + * addend. Overwrite it, do not add to it. + */ + + desc->entry = sym->st_value; + } + else + { + desc->entry = sym->st_value + desc->entry; + } + + desc->got = data->gotbase; + } + break; + + case R_ARM_FUNCDESC: + { + /* A pointer to a descriptor, which the loader has to supply. + * Carve one out of the pool reserved behind the writable segment + * and store its address. + */ + + struct fdpic_desc_s *desc; + arch_elfdata_t *data = (arch_elfdata_t *)arch_data; + + if (data == NULL) + { + berr("ERROR: FUNCDESC without loader state\n"); + return -EINVAL; + } + + if (!data->fdpic) + { + berr("ERROR: FUNCDESC in a non-FDPIC object\n"); + return -ENOEXEC; + } + + if (data->usedesc >= data->ndesc) + { + berr("ERROR: Out of function descriptors\n"); + return -ENOMEM; + } + + desc = data->descpool + data->usedesc++; + + binfo("Performing FUNCDESC link " + "at addr=%08" PRIxPTR " to sym=%p st_value=%08" PRIx32 "\n", + addr, sym, sym->st_value); + + desc->entry = sym->st_value + *(uint32_t *)addr; + desc->got = data->gotbase; + + *(uint32_t *)addr = (uint32_t)(uintptr_t)desc; + } + break; + case R_ARM_ABS32: case R_ARM_TARGET1: /* New ABI: TARGET1 always treated as ABS32 */ { @@ -501,6 +594,7 @@ int up_relocate(const Elf32_Rel *rel, const Elf32_Sym *sym, uintptr_t addr, break; case R_ARM_RELATIVE: + case R_ARM_GLOB_DAT: case R_ARM_JUMP_SLOT: { *(uint32_t *)addr = (uint32_t)sym->st_value;