Skip to content

Commit

Permalink
[CHERI] Correctly add .cfi_undefined to _start
Browse files Browse the repository at this point in the history
The existing approach of using inline assembly to inject .cfi_undefined
into _start was fragile: after upgrading to LLVM 16, the undefined
information was placed before the normal stack setup metadata which
meant it was being ignored by libunwind and it continues unwinding.
For dynamically linked libraries this should not cause any problems
since (as of the last commit) RTLD's entry point has the required
termination annotation, but for statically linked binaries we keep
unwinding and eventually trigger a bounds violation while parsing
nonsense data.

Make this current annotation less fragile by using a naked function
that has the .cfi_undefined annotation and calls the real C entry.
  • Loading branch information
arichardson committed Aug 20, 2024
1 parent 047c38e commit 389d945
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 5 deletions.
21 changes: 19 additions & 2 deletions lib/csu/aarch64c/crt1_c.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

struct Struct_Obj_Entry;
void _start(void *, void (*)(void), struct Struct_Obj_Entry *) __dead2 __exported;
void __start(void *, void (*)(void), struct Struct_Obj_Entry *) __dead2;

#ifdef GCRT
/* Profiling support. */
Expand All @@ -69,11 +70,10 @@ Elf_Auxinfo *__auxargs;
* linker takes care of initialization otherwise.
*/
void
_start(void *auxv,
__start(void *auxv,
void (*cleanup)(void), /* from shared loader */
struct Struct_Obj_Entry *obj) /* from shared loader */
{
__asm__ volatile(".cfi_undefined c30");
int argc = 0;
char **argv = NULL;
char **env = NULL;
Expand Down Expand Up @@ -131,3 +131,20 @@ _start(void *auxv,

__libc_start1(argc, argv, env, cleanup, main, data_cap, code_cap);
}

/*
* The real entry point _start just sets the unwind info for CRA to undefined
* which tells libunwind to stop unwinding and then calls the real __start.
* This is needed because ".cfi_undefined" inline assembly in a non-naked
* function could be overridden by the default unwinding information.
*/
__attribute__((naked, used)) void
_start(void *auxv,
void (*cleanup)(void), /* from shared loader */
struct Struct_Obj_Entry *obj) /* from shared loader */
{
__asm__(
".cfi_undefined c30\n"
"bl %0"
:: "i"(__start));
}
1 change: 0 additions & 1 deletion lib/csu/riscv/crt1_c.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ void __start(int argc, char **argv, char **env, void (*cleanup)(void)) __dead2;
void
__start(int argc, char **argv, char **env, void (*cleanup)(void))
{
__asm__ volatile(".cfi_undefined ra");
#ifdef SHOULD_PROCESS_CAP_RELOCS
/*
* Initialize __cap_relocs for static executables. The run-time linker
Expand Down
1 change: 1 addition & 0 deletions lib/csu/riscv/crt1_s.S
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

#include <machine/asm.h>
ENTRY(_start)
.cfi_undefined ra # Stop unwinding here
mv a3, a2 # cleanup
addi a1, a0, 8 # get argv
ld a0, 0(a0) # load argc
Expand Down
21 changes: 19 additions & 2 deletions lib/csu/riscv64c/crt1_c.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

struct Struct_Obj_Entry;
void _start(void *, void (*)(void), struct Struct_Obj_Entry *) __dead2 __exported;
void __start(void *, void (*)(void), struct Struct_Obj_Entry *) __dead2;

#ifdef GCRT
/* Profiling support. */
Expand All @@ -69,11 +70,10 @@ Elf_Auxinfo *__auxargs;
* linker takes care of initialization otherwise.
*/
void
_start(void *auxv,
__start(void *auxv,
void (*cleanup)(void), /* from shared loader */
struct Struct_Obj_Entry *obj) /* from shared loader */
{
__asm__ volatile(".cfi_undefined cra");
int argc = 0;
char **argv = NULL;
char **env = NULL;
Expand Down Expand Up @@ -128,3 +128,20 @@ _start(void *auxv,

__libc_start1(argc, argv, env, cleanup, main, NULL, NULL);
}

/*
* The real entry point _start just sets the unwind info for CRA to undefined
* which tells libunwind to stop unwinding and then calls the real __start.
* This is needed because ".cfi_undefined" inline assembly in a non-naked
* function could be overridden by the default unwinding information.
*/
__attribute__((naked, used)) void
_start(void *auxv,
void (*cleanup)(void), /* from shared loader */
struct Struct_Obj_Entry *obj) /* from shared loader */
{
__asm__(
".cfi_undefined cra\n"
"ccall %0"
:: "i"(__start));
}

0 comments on commit 389d945

Please sign in to comment.