Skip to content

[ATfE] Redirect libc abort() to semihosting exit#665

Merged
voltur01 merged 4 commits intoarm:arm-softwarefrom
voltur01:add_semihosting_abort
Jan 14, 2026
Merged

[ATfE] Redirect libc abort() to semihosting exit#665
voltur01 merged 4 commits intoarm:arm-softwarefrom
voltur01:add_semihosting_abort

Conversation

@voltur01
Copy link
Contributor

@voltur01 voltur01 commented Jan 12, 2026

The default libc abort() traps, thus exception handlers under semihosting crash QEMU because of nested exceptions - redirect abort() to semihosting exit instead to cleanly finish execution with QEMU.

The default libc abort() traps, thus exception handlers under semihosting crash QEMU because of nested exceptions - redirect abort() to exit() instead to cleanly finish execution with QEMU.
@voltur01 voltur01 requested a review from a team as a code owner January 12, 2026 14:48
__builtin_unreachable(); /* semihosting call doesn't return */
}

void abort() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we may be better off putting this in the CFI example rather than in the general support code.

AIUI abort() is meant to abort the program immediately without doing any cleanup, whereas exit() will run destructors, functions registered with atexit().

For a real world CFI I can see a case for aborting immediately without running any additional code, even exit handlers.

For our specific CFI example that's not a problem so we can locally override abort.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is a very good point, thanks!

I am afraid it is more than just the sample: all our exception handlers call __print_faulting_instruction which ends up in abort() here:

so any sample or test that causes an exception will also cause QEMU crash (which even messes up the terminal, so not nice either way).

Would it be an option to call the semihosting exit service directly in abort() without delegating to exit() to do it? Actually, I just should call __llvm_libc_exit directly https://github.com/arm/arm-toolchain/blob/a3871cfc3d301ebc93be61e4882c19b4ec0f0fec/arm-software/embedded/llvmlibc-support/semihost/semihost.cpp#L28C6-L28C22 - it does not do any extra processing.

Let me update!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the code and description.

Copy link
Contributor

Choose a reason for hiding this comment

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

For all that I said about exit() it seems like the baremetal implementation of llvm-libc exit just calls __llvm_libc_exit directly https://github.com/llvm/llvm-project/blob/main/libc/src/__support/OSUtil/baremetal/exit.cpp#L18

I think this means that with the current implementation of __llvm_libc_exit we can't support static destructors. It looks like we would need something like a call to __cxa_finalize() which do seem to be compiled into the library.

We would likely want to split out the code doing the semihosting exit in __llvm_libc_exit() into a separate function that could be called directly from abort().

Given that we don't have a working __llvm_libc_exit() function yet and it may take some research to work out what we need to do, I don't think we should hold this change up. We could leave a TODO as a comment or by raising an issue to implement calling atexit functions.

An example that generates a call to __cxa_atexit() to register a destructor.

extern void foo();
extern void bar();

struct C {
  C() { foo(); }
  ~C() { bar(); }
  int f() { return 0; }
};

C c;

int main(void) {
  return c.f();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, agreed, let me create a new function for just semihosting exit, then update existing __llvm_libc_exit() with a TODO and create an issue to resolve it separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@voltur01 voltur01 changed the title [ATfE] Redirect libc abort() to semihosting exit() [ATfE] Redirect libc abort() to semihosting exit Jan 13, 2026
Copy link
Contributor

@smithp35 smithp35 left a comment

Choose a reason for hiding this comment

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

One last comment, but otherwise LGTM.

extern "C" {

void __llvm_libc_exit(int status) {
void semihosting_call_exit(int status) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make this static or use leading __ if it is going to be callable externally, to avoid potential name clashes with user-code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course, marked as static similar to semihosting_call itself - thank you for review and patience!

Copy link
Contributor

@smithp35 smithp35 left a comment

Choose a reason for hiding this comment

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

LGTM thanks

@voltur01 voltur01 merged commit 4cf00db into arm:arm-software Jan 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants