Skip to content

Commit c98f9c4

Browse files
committed
Static linking
1 parent a865156 commit c98f9c4

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

src/doc/trpl/advanced-linking.md

+126
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,129 @@ meaning.
4747
It is highly recommended to *not* use this attribute, and rather use the more
4848
formal `#[link(...)]` attribute on `extern` blocks instead.
4949

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

0 commit comments

Comments
 (0)