Skip to content
Open
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
26 changes: 26 additions & 0 deletions binfmt/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ static int elf_loadbinary(FAR struct binary_s *binp,
int nexports)
{
struct mod_loadinfo_s loadinfo;
#if defined(CONFIG_BINFMT_CONSTRUCTORS) && !defined(CONFIG_ARCH_ADDRENV)
FAR void (**array)(void);
int i;
#endif
Elf_Sym sym;
int ret;

Expand Down Expand Up @@ -286,6 +290,28 @@ static int elf_loadbinary(FAR struct binary_s *binp,
}
#endif

#if defined(CONFIG_BINFMT_CONSTRUCTORS) && !defined(CONFIG_ARCH_ADDRENV)
/* Run the constructors, as libelf_insert() does for dlopen(). An
* executable runs its own in crt0. A module with a PIC base is skipped:
* this task holds its own base, not the module's.
*/

if (loadinfo.ehdr.e_type != ET_EXEC && binp->picbase == NULL)
{
array = (FAR void (**)(void))loadinfo.preiarr;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but the elf constructor must be called in the target address environment, and done at https://github.com/apache/nuttx/blob/master/arch/arm/src/common/crt0.c.

@casaroli casaroli Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, and I have pushed.

The guard is now:

#if defined(CONFIG_BINFMT_CONSTRUCTORS) && !defined(CONFIG_ARCH_ADDRENV)
  if (loadinfo.ehdr.e_type != ET_EXEC && binp->picbase == NULL)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but we should move so init/deinit into dlopen, not binfmt since binfmt is only used for executable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think only ET_EXEC is fully linked with crt0. libelf_insert(), already runs the arrays for the objects, only exec() does not

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exec need done in crt0.c to ensure the context is right.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cortex-m cannot use crt0. CONFIG_BINFMT_ELF_EXECUTABLE depends on ARCH_HAVE_ELF_EXECUTABLE, and no armv7-m or armv8-m chip selects it. only 13 chips do, and almost all of them have an MMU: mpfs, qemu-rv, k230, eic7700x, two litex cores, qemu and goldfish cortex-a7, four arm64 chips, x86_64.

so crt0 is the address environment case. i already skip it: ET_EXEC is skipped, and so is any build with CONFIG_ARCH_ADDRENV.

what is left is flat and relocatable. no crt0, no _start, binfmt jumps to e_entry, nothing runs .init_array. I think that is the case CONFIG_BINFMT_CONSTRUCTORS was written for, but it does not work today.

a startup object in the relocatable output would work, and binfmt would enter it instead of main. i can do that if you want (in separate PR?), should relocatable elf lose its constructors until then?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

elf_loadbinary runs inside the parent process, executing constructors here will generate the strange behavior. For example, the opened file handles belong the parent process, the child process can't accept them at all.

for (i = 0; i < loadinfo.nprei; i++)
{
array[i]();
}

array = (FAR void (**)(void))loadinfo.initarr;
for (i = 0; i < loadinfo.ninit; i++)
{
array[i]();
}
}
#endif

libelf_uninitialize(&loadinfo);
return OK;

Expand Down
Loading