Skip to content

Commit 7af865b

Browse files
committed
Static linking
1 parent e5c1884 commit 7af865b

File tree

1 file changed

+127
-0
lines changed

1 file changed

+127
-0
lines changed

src/doc/trpl/advanced-linking.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,130 @@ meaning.
3030
It is highly recommended to *not* use this attribute, and rather use the more
3131
formal `#[link(...)]` attribute on `extern` blocks instead.
3232

33+
# Static linking
34+
35+
Static linking refers to the process of creating output that contain all
36+
required libraries and so don't need libraries installed on every system where
37+
you want to use your compiled project. Pure-Rust dependencies are statically
38+
linked by default so you can use created binaries and libraries without
39+
installing the Rust everywhere. By contrast, native libraries
40+
(e.g. `libc` and `libm`) usually dynamically linked, but it is possible to
41+
change this and statically link them as well.
42+
43+
Linking is a very platform dependent topic - on some platforms, static linking
44+
may not be possible at all! This section assumes some basic familiarity with
45+
linking on your platform on choice.
46+
47+
## Linux
48+
49+
By default, all Rust programs on Linux will link to the system `libc` along with
50+
a number of other libraries. Let's look at an example on a 64-bit linux machine
51+
with GCC and `glibc` (by far the most common `libc` on Linux):
52+
53+
``` text
54+
$ cat example.rs
55+
fn main() {}
56+
$ rustc example.rs
57+
$ ldd example
58+
linux-vdso.so.1 => (0x00007ffd565fd000)
59+
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa81889c000)
60+
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa81867e000)
61+
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fa818475000)
62+
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa81825f000)
63+
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa817e9a000)
64+
/lib64/ld-linux-x86-64.so.2 (0x00007fa818cf9000)
65+
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa817b93000)
66+
```
67+
68+
Dynamic linking on Linux can be undesirable if you wish to use new library
69+
features on old systems or target systems which do not have the required
70+
dependencies for your program to run.
71+
72+
The first step in using static linking is examining the Rust linking arguments
73+
with an option to rustc. Newlines have been added for readability:
74+
75+
``` text
76+
$ rustc example.rs -Z print-link-args
77+
"cc"
78+
"-Wl,--as-needed"
79+
"-m64"
80+
[...]
81+
"-o" "example"
82+
"example.o"
83+
"-Wl,--whole-archive" "-lmorestack" "-Wl,--no-whole-archive"
84+
"-Wl,--gc-sections"
85+
"-pie"
86+
"-nodefaultlibs"
87+
[...]
88+
"-Wl,--whole-archive" "-Wl,-Bstatic"
89+
"-Wl,--no-whole-archive" "-Wl,-Bdynamic"
90+
"-ldl" "-lpthread" "-lrt" "-lgcc_s" "-lpthread" "-lc" "-lm" "-lcompiler-rt"
91+
```
92+
93+
Arguments with a `-L` before them set up the linker search path and arguments
94+
ending with `.rlib` are linking Rust crates statically into your application.
95+
Neither of these are relevent for static linking so have been ommitted.
96+
97+
The first step in being able to statically link is to obtain an object file.
98+
This can be achieved with `rustc --emit obj example.rs`, and creates a file
99+
called `example.o`, which you can see being passed in the command line above -
100+
rustc automatically deletes it when finished with it by default. As you now have
101+
the object file, you should be able to run the link command obtained with
102+
`print-link-args` to create perform the linking stage yourself.
103+
104+
In order to statically link, there are a number of changes you must make. Below
105+
is the command required to perform a static link; we will go through them each
106+
in turn.
107+
108+
``` text
109+
$ rustc example.rs -Z print-link-args
110+
"cc"
111+
"-static"
112+
"-m64"
113+
[...]
114+
"-o" "example"
115+
"example.o"
116+
"-Wl,--whole-archive" "-lmorestack" "-Wl,--no-whole-archive"
117+
"-Wl,--gc-sections"
118+
"-nodefaultlibs"
119+
[...]
120+
"-Wl,--whole-archive"
121+
"-Wl,--no-whole-archive"
122+
"-ldl" "-lpthread" "-lrt" "-lgcc_eh" "-lpthread" "-lc" "-lm" "-lcompiler-rt"
123+
```
124+
125+
- `-static` was added - this is the signal to the compiler to use a static
126+
glibc, among other things
127+
- `-Wl,--as-needed` was removed - this can be left in, but is unnecessary
128+
as it only applies to dynamic librares
129+
- `-pie` was removed - this is not compatible with static binaries
130+
- both `-Wl,-B*` options were removed - everything will be linked statically,
131+
so informing the linker of how certain libraries should be linked is not
132+
appropriate
133+
- `-lgcc_s` was changed to `-lgcc_eh` - `gcc_s` is the GCC support library,
134+
which Rust uses for unwinding support. This is only available as a dynamic
135+
library, so we must specify the static version of the library providing
136+
unwinding support.
137+
138+
By running this command, you will likely see some warnings like
139+
140+
``` text
141+
warning: Using 'getpwuid_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
142+
```
143+
144+
These should be considered carefully! They indicate calls in glibc which
145+
*cannot* be statically linked without significant extra effort. An application
146+
using these calls will find it is not as portable as 'static binary' would imply.
147+
Rust supports targeting musl as an alternative libc to be able to fully
148+
statically link these calls.
149+
150+
As we are confident that our code does not use these calls, we can now see the
151+
fruits of our labour:
152+
153+
```
154+
$ ldd example
155+
not a dynamic executable
156+
```
157+
158+
This binary can be copied to virtually any 64-bit Linux machine and work
159+
without requiring external libraries.

0 commit comments

Comments
 (0)