Skip to content

Commit

Permalink
🐛 ACTUALLY Fix undefined references in GCC
Browse files Browse the repository at this point in the history
The error looks like this:

```
arm-none-eabi/picolibc/arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(libc_stdlib_abort.c.o): in function `abort':
newlib/libc/stdlib/abort.c:63: undefined reference to `_exit'
arm-none-eabi/lib/thumb/v7e-m+fp/hard/libstdc++.a(vterminate.o): in function `__gnu_cxx::__verbose_terminate_handler()':
vterminate.cc:(.text._ZN9__gnu_cxx27__verbose_terminate_handlerEv+0xf4): undefined reference to `_impure_ptr'
arm-none-eabi/picolibc/arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc.a(libc_signal_signal.c.o): in function `raise':
newlib/libc/signal/signal.c:151: undefined reference to `_exit'
```

The solution, have conan add these flags to the link stage of the
binary:

```
-Wl,--whole-archive <insert_archive_file.a> -Wl,--no-whole-archive
```

The GCC linker will find unknown symbols and resolve them by searching
the .a archive files. If it doesn't find one, then it complains. Here is
the problem. Because we never call any of these functions, the linker
never searches for them. Then at final link stage, it finds some symbols
like abort() that calls _exit(), realizes it never saw it in any of our
code, then yells at us.

Now I'm not sure why it doesn't try to find `_exit` through `abort`. But
with `whole-archive` it forces all of the symbols to be in the symbol
table to be added to the linker's symbol list ensuring that they are
available for resolution.

:arrow_up: to 1.0.0 because I want to get the full benefit of semver
version control
  • Loading branch information
kammce committed Mar 11, 2024
1 parent e91429f commit f07d59c
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 31 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/1.0.0.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: 🚀 Deploy 1.0.0

on:
workflow_dispatch:

jobs:
deploy:
uses: libhal/ci/.github/workflows/[email protected]
with:
version: 1.0.0
secrets: inherit
7 changes: 6 additions & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,18 @@ def package(self):
cmake.install()

def package_info(self):
self.cpp_info.libs = ["libhal-exceptions"]
self.cpp_info.set_property("cmake_target_name", "libhal::exceptions")
lib_path = os.path.join(self.package_folder,
'lib', 'liblibhal-exceptions.a')

# Keep this for now, will update this for the runtime select
if self._is_arm_cortex:
self.cpp_info.exelinkflags = [
"-Wl,--wrap=__cxa_allocate_exception",
"-Wl,--wrap=__cxa_free_exception",
"-Wl,--wrap=__cxa_call_unexpected",
# Ensure that all symbols are added to the linker's symbol table
"-Wl,--whole-archive",
lib_path,
"-Wl,--no-whole-archive",
]
2 changes: 1 addition & 1 deletion include/libhal-exceptions/control.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,4 @@ class single_thread_exception_allocator : public exception_allocator
std::array<std::uint8_t, size> m_buffer{};
bool m_allocated = false;
};
} // namespace hal
} // namespace hal
2 changes: 1 addition & 1 deletion src/builtin/gcc/impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extern "C"
std::terminate();
}

void* __wrap___cxa_allocate_exception(unsigned int p_size) // NOLINT
void* __wrap___cxa_allocate_exception(unsigned int p_size) noexcept // NOLINT
{
// Size of the GCC exception object header is 128 bytes. Will have to update
// this if the size of the EO increases. 😅
Expand Down
32 changes: 4 additions & 28 deletions src/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,6 @@

namespace __cxxabiv1 { // NOLINT
std::terminate_handler __terminate_handler = +[]() { // NOLINT
// So you may be wondering what this code is doing here? Its actually a
// bit of weird circular logic. So the linker will garbage collect any
// functions that are not used in your code. If you somehow have an
// application without any exceptions thrown, the exception code will be
// eliminated. This would be great, but due the fact that our build system
// adds the `-Wl,--wrap=symbol` to the compiler to swap the function
// implementations, this results in the compiler yelling at the user that
// they are missing a wrapped function. In order to prevent the compiler
// from throwing away this function and then turning around demanding that
// we supply it, we simply need to call throw somewhere in the code. That
// will force it to link in the original implementations which will be
// swapped out with our wrapped implementations.
//
// Use a volatile bool that is always set to false to ensure that the
// "throw 5" is NEVER called.
//
// This location was choosen because it always links in for GCC.
volatile bool force_exceptions_to_link = false;
if (force_exceptions_to_link) {
throw 5;
}

while (true) {
continue;
}
Expand All @@ -63,11 +41,9 @@ std::terminate_handler get_terminate() noexcept
}

// TODO(#11): Add macro to IFDEF this out if the user want to save 256 bytes.
using default_single_thread_exception_allocator =
single_thread_exception_allocator<256>;

default_single_thread_exception_allocator default_allocator{};
exception_allocator* __exception_allocator = &default_allocator; // NOLINT
using default_exception_allocator = single_thread_exception_allocator<256>;
default_exception_allocator __default_allocator{}; // NOLINT
exception_allocator* __exception_allocator = &__default_allocator; // NOLINT

void set_exception_allocator(exception_allocator& p_allocator) noexcept
{
Expand All @@ -78,4 +54,4 @@ exception_allocator* get_exception_allocator() noexcept
{
return __exception_allocator;
}
} // namespace hal
} // namespace hal

0 comments on commit f07d59c

Please sign in to comment.