|
| 1 | +% Advanced Linking |
| 2 | + |
| 3 | +The common cases of linking with Rust have been covered earlier in this book, |
| 4 | +but supporting the range of linking possibilities made available by other |
| 5 | +languages is important for Rust to achieve seamless interaction with native |
| 6 | +libraries. |
| 7 | + |
| 8 | +# Link args |
| 9 | + |
| 10 | +There is one other way to tell `rustc` how to customize linking, and that is via |
| 11 | +the `link_args` attribute. This attribute is applied to `extern` blocks and |
| 12 | +specifies raw flags which need to get passed to the linker when producing an |
| 13 | +artifact. An example usage would be: |
| 14 | + |
| 15 | +``` no_run |
| 16 | +#![feature(link_args)] |
| 17 | +
|
| 18 | +#[link_args = "-foo -bar -baz"] |
| 19 | +extern {} |
| 20 | +# fn main() {} |
| 21 | +``` |
| 22 | + |
| 23 | +Note that this feature is currently hidden behind the `feature(link_args)` gate |
| 24 | +because this is not a sanctioned way of performing linking. Right now `rustc` |
| 25 | +shells out to the system linker (`gcc` on most systems, `link.exe` on MSVC), |
| 26 | +so it makes sense to provide extra command line |
| 27 | +arguments, but this will not always be the case. In the future `rustc` may use |
| 28 | +LLVM directly to link native libraries, in which case `link_args` will have no |
| 29 | +meaning. You can achieve the same effect as the `link-args` attribute with the |
| 30 | +`-C link-args` argument to `rustc`. |
| 31 | + |
| 32 | +It is highly recommended to *not* use this attribute, and rather use the more |
| 33 | +formal `#[link(...)]` attribute on `extern` blocks instead. |
| 34 | + |
| 35 | +# Static linking |
| 36 | + |
| 37 | +Static linking refers to the process of creating output that contain all |
| 38 | +required libraries and so don't need libraries installed on every system where |
| 39 | +you want to use your compiled project. Pure-Rust dependencies are statically |
| 40 | +linked by default so you can use created binaries and libraries without |
| 41 | +installing the Rust everywhere. By contrast, native libraries |
| 42 | +(e.g. `libc` and `libm`) usually dynamically linked, but it is possible to |
| 43 | +change this and statically link them as well. |
| 44 | + |
| 45 | +Linking is a very platform dependent topic — on some platforms, static linking |
| 46 | +may not be possible at all! This section assumes some basic familiarity with |
| 47 | +linking on your platform of choice. |
| 48 | + |
| 49 | +## Linux |
| 50 | + |
| 51 | +By default, all Rust programs on Linux will link to the system `libc` along with |
| 52 | +a number of other libraries. Let's look at an example on a 64-bit Linux machine |
| 53 | +with GCC and `glibc` (by far the most common `libc` on Linux): |
| 54 | + |
| 55 | +``` text |
| 56 | +$ cat example.rs |
| 57 | +fn main() {} |
| 58 | +$ rustc example.rs |
| 59 | +$ ldd example |
| 60 | + linux-vdso.so.1 => (0x00007ffd565fd000) |
| 61 | + libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa81889c000) |
| 62 | + libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa81867e000) |
| 63 | + librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fa818475000) |
| 64 | + libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa81825f000) |
| 65 | + libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa817e9a000) |
| 66 | + /lib64/ld-linux-x86-64.so.2 (0x00007fa818cf9000) |
| 67 | + libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa817b93000) |
| 68 | +``` |
| 69 | + |
| 70 | +Dynamic linking on Linux can be undesirable if you wish to use new library |
| 71 | +features on old systems or target systems which do not have the required |
| 72 | +dependencies for your program to run. |
| 73 | + |
| 74 | +Static linking is supported via an alternative `libc`, `musl` - this must be |
| 75 | +enabled at Rust compile-time with some prerequisites available. You can compile |
| 76 | +your own version of Rust with `musl` enabled and install it into a custom |
| 77 | +directory with the instructions below: |
| 78 | + |
| 79 | +```text |
| 80 | +$ mkdir musldist |
| 81 | +$ PREFIX=$(pwd)/musldist |
| 82 | +$ |
| 83 | +$ # Build musl |
| 84 | +$ wget http://www.musl-libc.org/releases/musl-1.1.10.tar.gz |
| 85 | +[...] |
| 86 | +$ tar xf musl-1.1.10.tar.gz |
| 87 | +$ cd musl-1.1.10/ |
| 88 | +musl-1.1.10 $ ./configure --disable-shared --prefix=$PREFIX |
| 89 | +[...] |
| 90 | +musl-1.1.10 $ make |
| 91 | +[...] |
| 92 | +musl-1.1.10 $ make install |
| 93 | +[...] |
| 94 | +musl-1.1.10 $ cd .. |
| 95 | +$ du -h musldist/lib/libc.a |
| 96 | +2.2M musldist/lib/libc.a |
| 97 | +$ |
| 98 | +$ # Build libunwind.a |
| 99 | +$ wget http://llvm.org/releases/3.6.1/llvm-3.6.1.src.tar.xz |
| 100 | +$ tar xf llvm-3.6.1.src.tar.xz |
| 101 | +$ cd llvm-3.6.1.src/projects/ |
| 102 | +llvm-3.6.1.src/projects $ svn co http://llvm.org/svn/llvm-project/libcxxabi/trunk/ libcxxabi |
| 103 | +llvm-3.6.1.src/projects $ svn co http://llvm.org/svn/llvm-project/libunwind/trunk/ libunwind |
| 104 | +llvm-3.6.1.src/projects $ sed -i 's#^\(include_directories\).*$#\0\n\1(../libcxxabi/include)#' libunwind/CMakeLists.txt |
| 105 | +llvm-3.6.1.src/projects $ mkdir libunwind/build |
| 106 | +llvm-3.6.1.src/projects $ cd libunwind/build |
| 107 | +llvm-3.6.1.src/projects/libunwind/build $ cmake -DLLVM_PATH=../../.. -DLIBUNWIND_ENABLE_SHARED=0 .. |
| 108 | +llvm-3.6.1.src/projects/libunwind/build $ make |
| 109 | +llvm-3.6.1.src/projects/libunwind/build $ cp lib/libunwind.a $PREFIX/lib/ |
| 110 | +llvm-3.6.1.src/projects/libunwind/build $ cd cd ../../../../ |
| 111 | +$ du -h musldist/lib/libunwind.a |
| 112 | +164K musldist/lib/libunwind.a |
| 113 | +$ |
| 114 | +$ # Build musl-enabled rust |
| 115 | +$ git clone https://github.com/rust-lang/rust.git muslrust |
| 116 | +$ cd muslrust |
| 117 | +muslrust $ ./configure --target=x86_64-unknown-linux-musl --musl-root=$PREFIX --prefix=$PREFIX |
| 118 | +muslrust $ make |
| 119 | +muslrust $ make install |
| 120 | +muslrust $ cd .. |
| 121 | +$ du -h musldist/bin/rustc |
| 122 | +12K musldist/bin/rustc |
| 123 | +``` |
| 124 | + |
| 125 | +You now have a build of a `musl`-enabled Rust! Because we've installed it to a |
| 126 | +custom prefix we need to make sure our system can the binaries and appropriate |
| 127 | +libraries when we try and run it: |
| 128 | + |
| 129 | +```text |
| 130 | +$ export PATH=$PREFIX/bin:$PATH |
| 131 | +$ export LD_LIBRARY_PATH=$PREFIX/lib:$LD_LIBRARY_PATH |
| 132 | +``` |
| 133 | + |
| 134 | +Let's try it out! |
| 135 | + |
| 136 | +```text |
| 137 | +$ echo 'fn main() { println!("hi!"); panic!("failed"); }' > example.rs |
| 138 | +$ rustc --target=x86_64-unknown-linux-musl example.rs |
| 139 | +$ ldd example |
| 140 | + not a dynamic executable |
| 141 | +$ ./example |
| 142 | +hi! |
| 143 | +thread '<main>' panicked at 'failed', example.rs:1 |
| 144 | +``` |
| 145 | + |
| 146 | +Success! This binary can be copied to almost any Linux machine with the same |
| 147 | +machine architecture and run without issues. |
| 148 | + |
| 149 | +`cargo build` also permits the `--target` option so you should be able to build |
| 150 | +your crates as normal. However, you may need to recompile your native libraries |
| 151 | +against `musl` before they can be linked against. |
0 commit comments