Skip to content

Document static linking with musl #27193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 24, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/doc/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,10 @@ The type of a function declared in an extern block is `extern "abi" fn(A1, ...,
An) -> R`, where `A1...An` are the declared types of its arguments and `R` is
the declared return type.

It is valid to add the `link` attribute on an empty extern block. You can use
this to satisfy the linking requirements of extern blocks elsewhere in your code
(including upstream crates) instead of adding the attribute to each extern block.

## Visibility and Privacy

These two terms are often used interchangeably, and what they are attempting to
Expand Down
2 changes: 1 addition & 1 deletion src/doc/trpl/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
* [No stdlib](no-stdlib.md)
* [Intrinsics](intrinsics.md)
* [Lang items](lang-items.md)
* [Link args](link-args.md)
* [Advanced linking](advanced-linking.md)
* [Benchmark Tests](benchmark-tests.md)
* [Box Syntax and Patterns](box-syntax-and-patterns.md)
* [Slice Patterns](slice-patterns.md)
Expand Down
151 changes: 151 additions & 0 deletions src/doc/trpl/advanced-linking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
% Advanced Linking

The common cases of linking with Rust have been covered earlier in this book,
but supporting the range of linking possibilities made available by other
languages is important for Rust to achieve seamless interaction with native
libraries.

# Link args

There is one other way to tell `rustc` how to customize linking, and that is via
the `link_args` attribute. This attribute is applied to `extern` blocks and
specifies raw flags which need to get passed to the linker when producing an
artifact. An example usage would be:

``` no_run
#![feature(link_args)]

#[link_args = "-foo -bar -baz"]
extern {}
# fn main() {}
```

Note that this feature is currently hidden behind the `feature(link_args)` gate
because this is not a sanctioned way of performing linking. Right now `rustc`
shells out to the system linker (`gcc` on most systems, `link.exe` on MSVC),
so it makes sense to provide extra command line
arguments, but this will not always be the case. In the future `rustc` may use
LLVM directly to link native libraries, in which case `link_args` will have no
meaning. You can achieve the same effect as the `link-args` attribute with the
`-C link-args` argument to `rustc`.

It is highly recommended to *not* use this attribute, and rather use the more
formal `#[link(...)]` attribute on `extern` blocks instead.

# Static linking

Static linking refers to the process of creating output that contain all
required libraries and so don't need libraries installed on every system where
you want to use your compiled project. Pure-Rust dependencies are statically
linked by default so you can use created binaries and libraries without
installing the Rust everywhere. By contrast, native libraries
(e.g. `libc` and `libm`) usually dynamically linked, but it is possible to
change this and statically link them as well.

Linking is a very platform dependent topic — on some platforms, static linking
may not be possible at all! This section assumes some basic familiarity with
linking on your platform of choice.

## Linux

By default, all Rust programs on Linux will link to the system `libc` along with
a number of other libraries. Let's look at an example on a 64-bit Linux machine
with GCC and `glibc` (by far the most common `libc` on Linux):

``` text
$ cat example.rs
fn main() {}
$ rustc example.rs
$ ldd example
linux-vdso.so.1 => (0x00007ffd565fd000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa81889c000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa81867e000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fa818475000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa81825f000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa817e9a000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa818cf9000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa817b93000)
```

Dynamic linking on Linux can be undesirable if you wish to use new library
features on old systems or target systems which do not have the required
dependencies for your program to run.

Static linking is supported via an alternative `libc`, `musl` - this must be
enabled at Rust compile-time with some prerequisites available. You can compile
your own version of Rust with `musl` enabled and install it into a custom
directory with the instructions below:

```text
$ mkdir musldist
$ PREFIX=$(pwd)/musldist
$
$ # Build musl
$ wget http://www.musl-libc.org/releases/musl-1.1.10.tar.gz
[...]
$ tar xf musl-1.1.10.tar.gz
$ cd musl-1.1.10/
musl-1.1.10 $ ./configure --disable-shared --prefix=$PREFIX
[...]
musl-1.1.10 $ make
[...]
musl-1.1.10 $ make install
[...]
musl-1.1.10 $ cd ..
$ du -h musldist/lib/libc.a
2.2M musldist/lib/libc.a
$
$ # Build libunwind.a
$ wget http://llvm.org/releases/3.6.1/llvm-3.6.1.src.tar.xz
$ tar xf llvm-3.6.1.src.tar.xz
$ cd llvm-3.6.1.src/projects/
llvm-3.6.1.src/projects $ svn co http://llvm.org/svn/llvm-project/libcxxabi/trunk/ libcxxabi
llvm-3.6.1.src/projects $ svn co http://llvm.org/svn/llvm-project/libunwind/trunk/ libunwind
llvm-3.6.1.src/projects $ sed -i 's#^\(include_directories\).*$#\0\n\1(../libcxxabi/include)#' libunwind/CMakeLists.txt
llvm-3.6.1.src/projects $ mkdir libunwind/build
llvm-3.6.1.src/projects $ cd libunwind/build
llvm-3.6.1.src/projects/libunwind/build $ cmake -DLLVM_PATH=../../.. -DLIBUNWIND_ENABLE_SHARED=0 ..
llvm-3.6.1.src/projects/libunwind/build $ make
llvm-3.6.1.src/projects/libunwind/build $ cp lib/libunwind.a $PREFIX/lib/
llvm-3.6.1.src/projects/libunwind/build $ cd cd ../../../../
$ du -h musldist/lib/libunwind.a
164K musldist/lib/libunwind.a
$
$ # Build musl-enabled rust
$ git clone https://github.com/rust-lang/rust.git muslrust
$ cd muslrust
muslrust $ ./configure --target=x86_64-unknown-linux-musl --musl-root=$PREFIX --prefix=$PREFIX
muslrust $ make
muslrust $ make install
muslrust $ cd ..
$ du -h musldist/bin/rustc
12K musldist/bin/rustc
```

You now have a build of a `musl`-enabled Rust! Because we've installed it to a
custom prefix we need to make sure our system can the binaries and appropriate
libraries when we try and run it:

```text
$ export PATH=$PREFIX/bin:$PATH
$ export LD_LIBRARY_PATH=$PREFIX/lib:$LD_LIBRARY_PATH
```

Let's try it out!

```text
$ echo 'fn main() { println!("hi!"); panic!("failed"); }' > example.rs
$ rustc --target=x86_64-unknown-linux-musl example.rs
$ ldd example
not a dynamic executable
$ ./example
hi!
thread '<main>' panicked at 'failed', example.rs:1
```

Success! This binary can be copied to almost any Linux machine with the same
machine architecture and run without issues.

`cargo build` also permits the `--target` option so you should be able to build
your crates as normal. However, you may need to recompile your native libraries
against `musl` before they can be linked against.
25 changes: 0 additions & 25 deletions src/doc/trpl/link-args.md

This file was deleted.