Skip to content

windows-gnullvm: Avoid linking to libunwind statically - #121794

Closed
kleisauke wants to merge 1 commit into
rust-lang:masterfrom
kleisauke:windows-gnullvm-change-unwind-linkage
Closed

windows-gnullvm: Avoid linking to libunwind statically#121794
kleisauke wants to merge 1 commit into
rust-lang:masterfrom
kleisauke:windows-gnullvm-change-unwind-linkage

Conversation

@kleisauke

@kleisauke kleisauke commented Feb 29, 2024

Copy link
Copy Markdown
Contributor

Avoid linking against the static variant of libunwind, which is not always available. Instead, prefer to use the unwind library from the toolchain, which the linker will automatically include, depending on what's available.

@kleisauke

This comment was marked as resolved.

@rustbot

rustbot commented Feb 29, 2024

Copy link
Copy Markdown
Collaborator

r? @fmease

rustbot has assigned @fmease.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot

rustbot commented Feb 29, 2024

Copy link
Copy Markdown
Collaborator

Failed to set assignee to mati865: invalid assignee

Note: Only org members with at least the repository "read" role, users with write permissions, or people who have commented on the PR may be assigned.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 29, 2024
@rustbot

rustbot commented Feb 29, 2024

Copy link
Copy Markdown
Collaborator

These commits modify compiler targets.
(See the Target Tier Policy.)

@rust-log-analyzer

This comment has been minimized.

@kleisauke
kleisauke force-pushed the windows-gnullvm-change-unwind-linkage branch from 71a9b1b to ba888f8 Compare February 29, 2024 12:18
@rust-log-analyzer

This comment has been minimized.

@kleisauke
kleisauke force-pushed the windows-gnullvm-change-unwind-linkage branch from ba888f8 to 5b24cc1 Compare February 29, 2024 12:24
@mati865

mati865 commented Feb 29, 2024

Copy link
Copy Markdown
Member

I don't see how crt-static would affect libunwind linkage with these changes. I think it would require special handling like here:

#[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))]
#[link(name = "unwind", cfg(not(target_feature = "crt-static")))]
extern "C" {}

I'll test it during the weekend.

Also I think it might be the best to just build the unwinder during Rust build and always link it statically like here:

#[link(name = "unwind", kind = "static", modifiers = "-bundle")]

Avoid linking against the static variant of libunwind, which is not
always available. Instead, prefer to use the unwind library from the
toolchain, which the linker will automatically include, depending
on what's available.
@kleisauke
kleisauke force-pushed the windows-gnullvm-change-unwind-linkage branch from 5b24cc1 to 7af0f34 Compare March 1, 2024 08:29
@kleisauke

Copy link
Copy Markdown
Contributor Author

Ah, you're right, it looks like that only applies to Linux and Fuchsia targets.

# Only applies for Linux and Fuchsia targets
# If crt-static is enabled, static link to `libunwind.a` provided by system
# If crt-static is disabled, dynamic link to `libunwind.so` provided by system
system-llvm-libunwind = []

I just clarified my commit message.

FWIW, here's a Dockerfile to reproduce this:

Details
# Build with:
# docker build -t llvm-mingw .
FROM docker.io/mstorsjo/llvm-mingw:latest

# Supported architectures: x86_64, i686, aarch64 and armv7
ARG ARCH=x86_64

# Path settings
ENV RUSTUP_HOME="/usr/local/rustup" \
    CARGO_HOME="/usr/local/cargo" \
    PATH="/usr/local/cargo/bin:$PATH"

# Install curl
RUN apt-get update -qq && \
    apt-get install -qqy --no-install-recommends curl && \
    apt-get clean -y && \
    rm -rf /var/lib/apt/lists/*

# Install Rust
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \
      --no-modify-path \
      --profile minimal \
      --target $ARCH-pc-windows-gnullvm \
      --default-toolchain nightly \
      --component rust-src

WORKDIR /build

# Special flags for Rust
ENV CARGO_PROFILE_RELEASE_DEBUG=false \
    CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 \
    CARGO_PROFILE_RELEASE_INCREMENTAL=false \
    CARGO_PROFILE_RELEASE_LTO=true \
    CARGO_PROFILE_RELEASE_OPT_LEVEL=z \
    CARGO_PROFILE_RELEASE_PANIC=abort

RUN rm /opt/llvm-mingw/$ARCH-w64-mingw32/lib/libunwind.a

RUN cargo new foo --lib --vcs none && \
    cd foo && \
    echo "pub fn foo() {}" > src/lib.rs && \
    cargo rustc --release --crate-type=cdylib --target=$ARCH-pc-windows-gnullvm -Zbuild-std=std,panic_abort && \
    llvm-readobj --coff-imports target/$ARCH-pc-windows-gnullvm/release/foo.dll

@fmease

fmease commented Mar 1, 2024

Copy link
Copy Markdown
Member

Not my area of expertise
r? compiler

@rustbot rustbot assigned nnethercote and unassigned fmease Mar 1, 2024
@nnethercote

Copy link
Copy Markdown
Contributor

r? @petrochenkov
cc @mati865

@rustbot rustbot assigned petrochenkov and unassigned nnethercote Mar 1, 2024
@mati865

mati865 commented Mar 2, 2024

Copy link
Copy Markdown
Member

The problem linking dynamic libunwind is the need of shipping libunwind together with the library/binary. I'll try if I can get crt-static feature to work or build libunwind together with std.

@kleisauke

Copy link
Copy Markdown
Contributor Author

Ah, I see. LLVM just uses -lunwind, so it will link against the static libunwind.a variant when libunwind.dll{,.a} is not available.
https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/clang/lib/Driver/ToolChains/CommonArgs.cpp#L1829-L1831

If you pass -Clink-args=-static-libgcc, it will forcibly link against libunwind.a, but this is a bit odd from a Rust point of view. I'll close.

@mati865

mati865 commented Mar 4, 2024

Copy link
Copy Markdown
Member

Opened #122003 that builds libunwind for these targets so we become independent of host toolchain when it comes to libunwind. Did some testing during Sunday and it seems to work fine but I'll try it with your Dockerfile using toolchain built the official way.

bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 5, 2024
…try>

Build libunwind for pc-windows-gnullvm

Alternative to rust-lang#121794

The changes in this PR:
- build libunwind for `pc-windows-gnullvm` targets
- join paths with `join()` instead of slashes to avoid mixing slashes on windows, this changes it them from `"H:\\projects\\rust\\src/llvm-project/libunwind\\include"` to `"H:\\projects\\rust\\src\\llvm-project\\libunwind\\include"`
- include `libunwind/src`, some of the includes are located inside `src` and without this change the build fails with:
```
running: "h:/msys64/clang64/bin/clang++.exe" "-O3" "-ffunction-sections" "-fdata-sections" "--target=x86_64-pc-windows-gnullvm" "-I" "H:\\projects\\rust\\src\\llvm-project\\libunwind\\include" "-nostdinc++" "-fno-exceptions" "-fno-rtti" "-fstrict-aliasing" "-funwind-tables" "-fvisibility=hidden" "-fvisibility-global-new-delete-hidden" "-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS" "-D_LIBUNWIND_HIDE_SYMBOLS=1" "-D_LIBUNWIND_IS_NATIVE_ONLY=1" "-o" "H:\\projects\\rust\\build\\x86_64-pc-windows-gnullvm\\native\\libunwind\\Unwind-EHABI.o" "-c" "\\\\?\\H:\\projects\\rust\\src\\llvm-project\\libunwind\\src\\Unwind-EHABI.cpp"
cargo:warning=\\?\H:\projects\rust\src\llvm-project\libunwind\src\Unwind-EHABI.cpp:12:10: fatal error: 'Unwind-EHABI.h' file not found
cargo:warning=   12 | #include "Unwind-EHABI.h"
cargo:warning=      |          ^~~~~~~~~~~~~~~~
cargo:warning=1 error generated.
    exit code: 1
```
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Dec 11, 2024
…=petrochenkov

link libunwind dynamically and allow controlling it via `crt-static` on gnullvm targets

Alternative to rust-lang#121794

```
$ cargo b -r
    Finished `release` profile [optimized] target(s) in 0.38s

$ ntldd target/release/hello.exe | rg unwind
        libunwind.dll => H:\msys64\clang64\bin\libunwind.dll (0x0000020c35df0000)

$ RUSTFLAGS="-C target-feature=+crt-static" cargo b -r
    Finished `release` profile [optimized] target(s) in 0.23s

$ ntldd target/release/hello.exe | rg unwind
```
jieyouxu added a commit to jieyouxu/rust that referenced this pull request Dec 12, 2024
…=petrochenkov

link libunwind dynamically and allow controlling it via `crt-static` on gnullvm targets

Alternative to rust-lang#121794

```
$ cargo b -r
    Finished `release` profile [optimized] target(s) in 0.38s

$ ntldd target/release/hello.exe | rg unwind
        libunwind.dll => H:\msys64\clang64\bin\libunwind.dll (0x0000020c35df0000)

$ RUSTFLAGS="-C target-feature=+crt-static" cargo b -r
    Finished `release` profile [optimized] target(s) in 0.23s

$ ntldd target/release/hello.exe | rg unwind
```
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Dec 12, 2024
…=petrochenkov

link libunwind dynamically and allow controlling it via `crt-static` on gnullvm targets

Alternative to rust-lang#121794

```
$ cargo b -r
    Finished `release` profile [optimized] target(s) in 0.38s

$ ntldd target/release/hello.exe | rg unwind
        libunwind.dll => H:\msys64\clang64\bin\libunwind.dll (0x0000020c35df0000)

$ RUSTFLAGS="-C target-feature=+crt-static" cargo b -r
    Finished `release` profile [optimized] target(s) in 0.23s

$ ntldd target/release/hello.exe | rg unwind
```
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Dec 12, 2024
Rollup merge of rust-lang#122003 - mati865:gnullvm-build-libunwind, r=petrochenkov

link libunwind dynamically and allow controlling it via `crt-static` on gnullvm targets

Alternative to rust-lang#121794

```
$ cargo b -r
    Finished `release` profile [optimized] target(s) in 0.38s

$ ntldd target/release/hello.exe | rg unwind
        libunwind.dll => H:\msys64\clang64\bin\libunwind.dll (0x0000020c35df0000)

$ RUSTFLAGS="-C target-feature=+crt-static" cargo b -r
    Finished `release` profile [optimized] target(s) in 0.23s

$ ntldd target/release/hello.exe | rg unwind
```
github-actions Bot pushed a commit to tautschnig/verify-rust-std that referenced this pull request Mar 11, 2025
…=petrochenkov

link libunwind dynamically and allow controlling it via `crt-static` on gnullvm targets

Alternative to rust-lang#121794

```
$ cargo b -r
    Finished `release` profile [optimized] target(s) in 0.38s

$ ntldd target/release/hello.exe | rg unwind
        libunwind.dll => H:\msys64\clang64\bin\libunwind.dll (0x0000020c35df0000)

$ RUSTFLAGS="-C target-feature=+crt-static" cargo b -r
    Finished `release` profile [optimized] target(s) in 0.23s

$ ntldd target/release/hello.exe | rg unwind
```
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 8, 2026
…r=petrochenkov

windows-gnullvm: always link libunwind statically

Previously shared library was used by default, meaning that programs and libraries couldn't be loaded if `libunwind.dll` was missing from the PATH. Using Wine (on Linux) because it better shows the problem (and is more convenient):
```
❯ cargo new hello &> /dev/null

❯ cargo rustc --target x86_64-pc-windows-gnullvm &> /dev/null

❯ wine target/x86_64-pc-windows-gnullvm/debug/hello.exe
0024:err:module:import_dll Library libunwind.dll (which is needed by L"Z:\\tmp\\hello\\target\\x86_64-pc-windows-gnullvm\\debug\\hello.exe") not found
0024:err:module:loader_init Importing dlls for L"Z:\\tmp\\hello\\target\\x86_64-pc-windows-gnullvm\\debug\\hello.exe" failed, status c0000135

❯ llvm-readobj --coff-imports target/x86_64-pc-windows-gnullvm/debug/hello.exe
...
Import {
  Name: libunwind.dll
  ImportLookupTableRVA: 0x3D308
  ImportAddressTableRVA: 0x3D678
  Symbol: _GCC_specific_handler (0)
  Symbol: _Unwind_DeleteException (0)
  Symbol: _Unwind_GetDataRelBase (0)
  Symbol: _Unwind_GetIPInfo (0)
  Symbol: _Unwind_GetLanguageSpecificData (0)
  Symbol: _Unwind_GetRegionStart (0)
  Symbol: _Unwind_GetTextRelBase (0)
  Symbol: _Unwind_RaiseException (0)
  Symbol: _Unwind_Resume (0)
  Symbol: _Unwind_SetGR (0)
  Symbol: _Unwind_SetIP (0)
}
...
```
Optionally libunwind could be linked statically via `+crt-static`:
```
❯ cargo rustc --target x86_64-pc-windows-gnullvm -- -C target-feature=+crt-static &> /dev/null

❯ wine target/x86_64-pc-windows-gnullvm/debug/hello.exe
Hello, world!

❯ llvm-readobj --coff-imports target/x86_64-pc-windows-gnullvm/debug/hello.exe | rg 'libunwind.dll' || echo "doesn't depend on shared libunwind"
doesn't depend on shared libunwind
```

After a discussion of approach in rust-lang#159782 with @bjorn3 (thanks BTW!), I changed the proposed approach to always link static libunwind.

I don't have a good solution for rust-lang#121794 that will resurface. I guess the user has three options:
- symlink `libunwind.dll.a` as `libunwind.a`
- add `--unwindlib=none -lunwind` to the linker args
- create linker wrapper
- use self-contained mode which is likely is undesirable

I think the ease of use (not having to deal with additional DLL dependency) outweights the benefit of working with incomplete C toolchain.

The size bloat is also not a problem, sizes (in bytes) of the binary for the literal hello world project:
- debug build:
  - shared libunwind 4194816
  - static libunwind 4323328
- release build:
  - shared libunwind 382464
  - static libunwind 423424

Debug diff +125.5 KiB, release diff: +40 KiB.
Size of `libunwind.dll` that has to be provided when linking shared libunwind: 204288 bytes (199.5 KiB).
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 8, 2026
…r=petrochenkov

windows-gnullvm: always link libunwind statically

Previously shared library was used by default, meaning that programs and libraries couldn't be loaded if `libunwind.dll` was missing from the PATH. Using Wine (on Linux) because it better shows the problem (and is more convenient):
```
❯ cargo new hello &> /dev/null

❯ cargo rustc --target x86_64-pc-windows-gnullvm &> /dev/null

❯ wine target/x86_64-pc-windows-gnullvm/debug/hello.exe
0024:err:module:import_dll Library libunwind.dll (which is needed by L"Z:\\tmp\\hello\\target\\x86_64-pc-windows-gnullvm\\debug\\hello.exe") not found
0024:err:module:loader_init Importing dlls for L"Z:\\tmp\\hello\\target\\x86_64-pc-windows-gnullvm\\debug\\hello.exe" failed, status c0000135

❯ llvm-readobj --coff-imports target/x86_64-pc-windows-gnullvm/debug/hello.exe
...
Import {
  Name: libunwind.dll
  ImportLookupTableRVA: 0x3D308
  ImportAddressTableRVA: 0x3D678
  Symbol: _GCC_specific_handler (0)
  Symbol: _Unwind_DeleteException (0)
  Symbol: _Unwind_GetDataRelBase (0)
  Symbol: _Unwind_GetIPInfo (0)
  Symbol: _Unwind_GetLanguageSpecificData (0)
  Symbol: _Unwind_GetRegionStart (0)
  Symbol: _Unwind_GetTextRelBase (0)
  Symbol: _Unwind_RaiseException (0)
  Symbol: _Unwind_Resume (0)
  Symbol: _Unwind_SetGR (0)
  Symbol: _Unwind_SetIP (0)
}
...
```
Optionally libunwind could be linked statically via `+crt-static`:
```
❯ cargo rustc --target x86_64-pc-windows-gnullvm -- -C target-feature=+crt-static &> /dev/null

❯ wine target/x86_64-pc-windows-gnullvm/debug/hello.exe
Hello, world!

❯ llvm-readobj --coff-imports target/x86_64-pc-windows-gnullvm/debug/hello.exe | rg 'libunwind.dll' || echo "doesn't depend on shared libunwind"
doesn't depend on shared libunwind
```

After a discussion of approach in rust-lang#159782 with @bjorn3 (thanks BTW!), I changed the proposed approach to always link static libunwind.

I don't have a good solution for rust-lang#121794 that will resurface. I guess the user has three options:
- symlink `libunwind.dll.a` as `libunwind.a`
- add `--unwindlib=none -lunwind` to the linker args
- create linker wrapper
- use self-contained mode which is likely is undesirable

I think the ease of use (not having to deal with additional DLL dependency) outweights the benefit of working with incomplete C toolchain.

The size bloat is also not a problem, sizes (in bytes) of the binary for the literal hello world project:
- debug build:
  - shared libunwind 4194816
  - static libunwind 4323328
- release build:
  - shared libunwind 382464
  - static libunwind 423424

Debug diff +125.5 KiB, release diff: +40 KiB.
Size of `libunwind.dll` that has to be provided when linking shared libunwind: 204288 bytes (199.5 KiB).
rust-bors Bot pushed a commit that referenced this pull request Sep 8, 2026
Rollup merge of #160712 - mati865:gnullvm-static-libunwind, r=petrochenkov

windows-gnullvm: always link libunwind statically

Previously shared library was used by default, meaning that programs and libraries couldn't be loaded if `libunwind.dll` was missing from the PATH. Using Wine (on Linux) because it better shows the problem (and is more convenient):
```
❯ cargo new hello &> /dev/null

❯ cargo rustc --target x86_64-pc-windows-gnullvm &> /dev/null

❯ wine target/x86_64-pc-windows-gnullvm/debug/hello.exe
0024:err:module:import_dll Library libunwind.dll (which is needed by L"Z:\\tmp\\hello\\target\\x86_64-pc-windows-gnullvm\\debug\\hello.exe") not found
0024:err:module:loader_init Importing dlls for L"Z:\\tmp\\hello\\target\\x86_64-pc-windows-gnullvm\\debug\\hello.exe" failed, status c0000135

❯ llvm-readobj --coff-imports target/x86_64-pc-windows-gnullvm/debug/hello.exe
...
Import {
  Name: libunwind.dll
  ImportLookupTableRVA: 0x3D308
  ImportAddressTableRVA: 0x3D678
  Symbol: _GCC_specific_handler (0)
  Symbol: _Unwind_DeleteException (0)
  Symbol: _Unwind_GetDataRelBase (0)
  Symbol: _Unwind_GetIPInfo (0)
  Symbol: _Unwind_GetLanguageSpecificData (0)
  Symbol: _Unwind_GetRegionStart (0)
  Symbol: _Unwind_GetTextRelBase (0)
  Symbol: _Unwind_RaiseException (0)
  Symbol: _Unwind_Resume (0)
  Symbol: _Unwind_SetGR (0)
  Symbol: _Unwind_SetIP (0)
}
...
```
Optionally libunwind could be linked statically via `+crt-static`:
```
❯ cargo rustc --target x86_64-pc-windows-gnullvm -- -C target-feature=+crt-static &> /dev/null

❯ wine target/x86_64-pc-windows-gnullvm/debug/hello.exe
Hello, world!

❯ llvm-readobj --coff-imports target/x86_64-pc-windows-gnullvm/debug/hello.exe | rg 'libunwind.dll' || echo "doesn't depend on shared libunwind"
doesn't depend on shared libunwind
```

After a discussion of approach in #159782 with @bjorn3 (thanks BTW!), I changed the proposed approach to always link static libunwind.

I don't have a good solution for #121794 that will resurface. I guess the user has three options:
- symlink `libunwind.dll.a` as `libunwind.a`
- add `--unwindlib=none -lunwind` to the linker args
- create linker wrapper
- use self-contained mode which is likely is undesirable

I think the ease of use (not having to deal with additional DLL dependency) outweights the benefit of working with incomplete C toolchain.

The size bloat is also not a problem, sizes (in bytes) of the binary for the literal hello world project:
- debug build:
  - shared libunwind 4194816
  - static libunwind 4323328
- release build:
  - shared libunwind 382464
  - static libunwind 423424

Debug diff +125.5 KiB, release diff: +40 KiB.
Size of `libunwind.dll` that has to be provided when linking shared libunwind: 204288 bytes (199.5 KiB).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants