Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions arch/arm/include/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
64 changes: 62 additions & 2 deletions libs/libc/elf/elf_bind.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
****************************************************************************/
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Comment thread
acassis marked this conversation as resolved.
{
Expand Down Expand Up @@ -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);
Expand Down
96 changes: 95 additions & 1 deletion libs/libc/machine/arm/armv7-m/arch_elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <nuttx/debug.h>

#include <nuttx/elf.h>
#include <nuttx/fdpic.h>

/****************************************************************************
* Public Functions
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 */
{
Expand Down Expand Up @@ -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;
Expand Down
Loading
Loading