diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87e28d87f..83ea76fb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,6 +86,20 @@ jobs: - name: Linux (GCC 13.3.0 + code-checker + valgrind) os: ubuntu-24.04 RUN_ANALYZER: code-checker,valgrind + - name: Linux (GCC + musl + libunwind) + os: ubuntu-latest + container: alpine:3.21 + APK_PACKAGES: libunwind-dev libunwind-static + CMAKE_DEFINES: -DSENTRY_LIBUNWIND_SHARED=OFF + CC: gcc + CXX: g++ + - name: Linux (clang + musl + libunwind) + os: ubuntu-latest + container: alpine:3.21 + APK_PACKAGES: clang libunwind-dev libunwind-static + CMAKE_DEFINES: -DSENTRY_LIBUNWIND_SHARED=OFF + CC: clang + CXX: clang++ - name: macOS 14 (xcode llvm) os: macos-14 ERROR_ON_WARNINGS: 1 @@ -135,6 +149,7 @@ jobs: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} + container: ${{ matrix.container }} env: TEST_X86: ${{ matrix.TEST_X86 }} @@ -149,16 +164,25 @@ jobs: VS_GENERATOR_TOOLSET: ${{ matrix.VS_GENERATOR_TOOLSET }} steps: + - name: Installing Alpine Linux Dependencies + if: ${{ contains(matrix.container, 'alpine') }} + run: | + apk update + apk add bash build-base cargo cmake curl curl-dev git icu linux-headers mitmproxy perl python3-dev sudo xz-dev ${{ matrix.APK_PACKAGES }} + curl -sSL --retry 5 https://dot.net/v1/dotnet-install.sh | bash -eo pipefail /dev/stdin --channel 8.0 --install-dir /usr/share/dotnet + ln -s /usr/share/dotnet/dotnet /usr/local/bin/dotnet + - uses: actions/checkout@v4 with: submodules: recursive - uses: actions/setup-python@v5 + if: ${{ !matrix.container }} with: python-version: "3.11" cache: "pip" - name: Installing Linux Dependencies - if: ${{ runner.os == 'Linux' && matrix.os != 'ubuntu-22.04' && !env['TEST_X86'] }} + if: ${{ runner.os == 'Linux' && matrix.os != 'ubuntu-22.04' && !env['TEST_X86'] && !matrix.container }} run: | sudo apt update sudo apt install cmake clang-19 llvm g++-12 valgrind zlib1g-dev libcurl4-openssl-dev @@ -173,13 +197,13 @@ jobs: sudo make install - name: Installing Linux GCC 9.4.0 Dependencies - if: ${{ runner.os == 'Linux' && matrix.os == 'ubuntu-22.04' && !env['TEST_X86'] }} + if: ${{ runner.os == 'Linux' && matrix.os == 'ubuntu-22.04' && !env['TEST_X86'] && !matrix.container }} run: | sudo apt update sudo apt install cmake llvm kcov g++ valgrind zlib1g-dev libcurl4-openssl-dev - name: Installing Linux 32-bit Dependencies - if: ${{ runner.os == 'Linux' && env['TEST_X86'] }} + if: ${{ runner.os == 'Linux' && env['TEST_X86'] && !matrix.container }} run: | sudo dpkg --add-architecture i386 sudo apt update @@ -252,6 +276,16 @@ jobs: cat /etc/hosts shell: bash + - name: Prepare env for Alpine Linux + if: ${{ contains(matrix.container, 'alpine') }} + run: | + apk add moreutils + # comment out "::1 localhost ..." to avoid conflicts with proxy tests + sed '/^::1/ s/^/#/' /etc/hosts | sponge /etc/hosts + python3 -m venv .venv --system-site-packages + source .venv/bin/activate + echo "PATH=$PATH" >> $GITHUB_ENV + - name: Test shell: bash run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index e08e57fbb..a538ad2e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ **Fixes**: - Remove compile-time check for the `libcurl` feature `AsynchDNS`. ([#1206](https://github.com/getsentry/sentry-native/pull/1206)) +- Support musl on Linux ([#1233](https://github.com/getsentry/sentry-native/pull/1233)) ## 0.8.4 diff --git a/CMakeLists.txt b/CMakeLists.txt index da3fbf1b0..c8a5ce6f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,15 @@ if(LINUX) set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -m32 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE") set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS OFF) endif() + + execute_process( + COMMAND ${CMAKE_C_COMPILER} -dumpmachine + OUTPUT_VARIABLE TARGET_TRIPLET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(TARGET_TRIPLET MATCHES "musl") + set(MUSL TRUE) + endif() endif() # CMAKE_POSITION_INDEPENDENT_CODE must be set BEFORE adding any libraries (including subprojects) @@ -199,6 +208,8 @@ endif() if(ANDROID) set(SENTRY_WITH_LIBUNWINDSTACK TRUE) +elseif(MUSL) + set(SENTRY_WITH_LIBUNWIND TRUE) elseif(NOT WIN32) set(SENTRY_WITH_LIBBACKTRACE TRUE) endif() @@ -504,6 +515,24 @@ if(SENTRY_WITH_LIBUNWINDSTACK) endif() endif() +if(SENTRY_WITH_LIBUNWIND) + if(LINUX) + option(SENTRY_LIBUNWIND_SHARED "Link to shared libunwind" ${SENTRY_BUILD_SHARED_LIBS}) + find_path(LIBUNWIND_INCLUDE_DIR libunwind.h PATH_SUFFIXES libunwind REQUIRED) + if(SENTRY_LIBUNWIND_SHARED) + find_library(LIBUNWIND_LIBRARIES unwind REQUIRED) + else() + find_library(LIBUNWIND_LIBRARIES libunwind.a REQUIRED) + find_library(LZMA_LIBRARY lzma) + if(LZMA_LIBRARY) + list(APPEND LIBUNWIND_LIBRARIES ${LZMA_LIBRARY}) + endif() + endif() + target_include_directories(sentry PRIVATE ${LIBUNWIND_INCLUDE_DIR}) + target_link_libraries(sentry PRIVATE ${LIBUNWIND_LIBRARIES}) + endif() +endif() + if(SENTRY_BACKEND_CRASHPAD) if(SENTRY_BUILD_SHARED_LIBS) set(CRASHPAD_ENABLE_INSTALL OFF CACHE BOOL "Enable crashpad installation" FORCE) diff --git a/external/crashpad b/external/crashpad index 2d97a484b..a17b30d42 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit 2d97a484bb628b05a46203e4a05c6c827796e5cd +Subproject commit a17b30d42ec667c92a99285429e4edf2f7196698 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8dd12f349..87025d23b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -144,6 +144,12 @@ if(SENTRY_WITH_LIBBACKTRACE) ) endif() +if(SENTRY_WITH_LIBUNWIND) + target_compile_definitions(sentry PRIVATE SENTRY_WITH_UNWINDER_LIBUNWIND) + sentry_target_sources_cwd(sentry + unwinder/sentry_unwinder_libunwind.c + ) +endif() if(SENTRY_WITH_LIBUNWINDSTACK) target_compile_definitions(sentry PRIVATE SENTRY_WITH_UNWINDER_LIBUNWINDSTACK) sentry_target_sources_cwd(sentry diff --git a/src/unwinder/sentry_unwinder.c b/src/unwinder/sentry_unwinder.c index cc7ffecfb..f82cba632 100644 --- a/src/unwinder/sentry_unwinder.c +++ b/src/unwinder/sentry_unwinder.c @@ -15,6 +15,7 @@ DEFINE_UNWINDER(libunwindstack); DEFINE_UNWINDER(libbacktrace); DEFINE_UNWINDER(dbghelp); +DEFINE_UNWINDER(libunwind); static size_t unwind_stack( @@ -28,6 +29,9 @@ unwind_stack( #endif #ifdef SENTRY_WITH_UNWINDER_DBGHELP TRY_UNWINDER(dbghelp); +#endif +#ifdef SENTRY_WITH_UNWINDER_LIBUNWIND + TRY_UNWINDER(libunwind); #endif return 0; } diff --git a/src/unwinder/sentry_unwinder_libunwind.c b/src/unwinder/sentry_unwinder_libunwind.c new file mode 100644 index 000000000..844558dd7 --- /dev/null +++ b/src/unwinder/sentry_unwinder_libunwind.c @@ -0,0 +1,47 @@ +#include "sentry_boot.h" +#include "sentry_logger.h" +#define UNW_LOCAL_ONLY +#include + +size_t +sentry__unwind_stack_libunwind( + void *addr, const sentry_ucontext_t *uctx, void **ptrs, size_t max_frames) +{ + if (addr) { + return 0; + } + + unw_cursor_t cursor; + if (uctx) { + int ret = unw_init_local2(&cursor, (unw_context_t *)uctx->user_context, + UNW_INIT_SIGNAL_FRAME); + if (ret != 0) { + SENTRY_WARN("Failed to initialize libunwind with ucontext"); + return 0; + } + } else { + unw_context_t uc; + int ret = unw_getcontext(&uc); + if (ret != 0) { + SENTRY_WARN("Failed to retrieve context with libunwind"); + return 0; + } + + ret = unw_init_local(&cursor, &uc); + if (ret != 0) { + SENTRY_WARN("Failed to initialize libunwind with local context"); + return 0; + } + } + + size_t frame_idx = 0; + while (unw_step(&cursor) > 0 && frame_idx < max_frames - 1) { + unw_word_t ip = 0; + unw_get_reg(&cursor, UNW_REG_IP, &ip); + ptrs[frame_idx] = (void *)ip; + unw_word_t sp = 0; + unw_get_reg(&cursor, UNW_REG_SP, &sp); + frame_idx++; + } + return frame_idx + 1; +}