Lets write a simple helloworld program and using which we will try to understand the difference between static and dynamic linking.
$ vim helloworld.c
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello World !");
return 0;
}
Now, we will try to compile this program as,
$ gcc -o helloworld helloworld.c
$ file helloworld
helloworld: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b397310c8ddfabbdbe75f8021bea768321218984, for GNU/Linux 3.2.0, not stripped
$ ldd helloworld
linux-vdso.so.1 (0x00007fffd518b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f976be6a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f976c085000)
Above two commands, “file” and “ldd” shows that by default “gcc” created dynamically linked executable.
Now, lets try to create “statically linked” executable of same program as,
$ gcc -o helloworld_static helloworld.c -static
$ file helloworld_static
helloworld_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=f87f0fec9544c37c004bb11e2a6369633020b31d, for GNU/Linux 3.2.0, not stripped
$ ldd helloworld_static
not a dynamic executable
Above two commands shows we now have a “statically linked” executable.
Now lets try to compare the file sizes of both executable as,
$ ls -alh helloworld
-rwxrwxr-x 1 myuser myuser 7.2K Sep 2 21:08 helloworld
$ size helloworld
text data bss dec hex filename
1124 276 4 1404 57c helloworld
$ ls -alh helloworld_static
-rwxrwxr-x 1 myuser myuser 712K Sep 2 21:08 helloworld_static
$ size helloworld_static
text data bss dec hex filename
658287 4096 3644 666027 a29ab helloworld_static
So above two commands, shows that dynamically linked executable “helloworld” generates much smaller in size (7.2K) binary compared to statically linked executable “helloworld_static” (712K)
So the first difference is that, statically linked executable requires more space compared to dymanically linked executables which is the disadvantage of static linking and advantage of dynamic linking.
Another difference is, If several processes call the same object module of a shared library simultaneously, Only one copy of shared library resides in memory for dynamically linked executables where as for static linking all things of library gets copied as part of static linking hence Several copies each for a process resides in memory.
$ strace ./helloworld
execve("./helloworld", ["./helloworld"], 0x7ffc2f026cb0 /* 52 vars */) = 0
brk(NULL) = 0x55b2e6aed000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffcd6360fa0) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=137444, ...}) = 0
mmap(NULL, 137444, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f67a48bc000
close(3) = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360q\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
fstat(3, {st_mode=S_IFREG|0755, st_size=2029224, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f67a48ba000
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\t\233\222%\274\260\320\31\331\326\10\204\276X>\263"..., 68, 880) = 68
mmap(NULL, 2036952, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f67a46c8000
mprotect(0x7f67a46ed000, 1847296, PROT_NONE) = 0
mmap(0x7f67a46ed000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x25000) = 0x7f67a46ed000
mmap(0x7f67a4865000, 303104, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19d000) = 0x7f67a4865000
mmap(0x7f67a48b0000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f67a48b0000
mmap(0x7f67a48b6000, 13528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f67a48b6000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f67a48bb540) = 0
mprotect(0x7f67a48b0000, 12288, PROT_READ) = 0
mprotect(0x55b2e5e30000, 4096, PROT_READ) = 0
mprotect(0x7f67a490b000, 4096, PROT_READ) = 0
munmap(0x7f67a48bc000, 137444) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
brk(NULL) = 0x55b2e6aed000
brk(0x55b2e6b0e000) = 0x55b2e6b0e000
write(1, "Hello World !", 13Hello World !) = 13
exit_group(0) = ?
+++ exited with 0 +++
$ strace ./helloworld_static
execve("./helloworld_static", ["./helloworld_static"], 0x7ffe79742f30 /* 52 vars */) = 0
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd2317cbb0) = -1 EINVAL (Invalid argument)
brk(NULL) = 0x1e80000
brk(0x1e811c0) = 0x1e811c0
arch_prctl(ARCH_SET_FS, 0x1e80880) = 0
uname({sysname="Linux", nodename="devlab", ...}) = 0
readlink("/proc/self/exe", "/home/devlab/Desktop/lynxbee/hel"..., 4096) = 46
brk(0x1ea21c0) = 0x1ea21c0
brk(0x1ea3000) = 0x1ea3000
mprotect(0x4bd000, 12288, PROT_READ) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
write(1, "Hello World !", 13Hello World !) = 13
exit_group(0) = ?
+++ exited with 0 +++
The above commands where we tried to run both executables with strace shows, dynamic linking has more system calls, which means dynamic linked executables will need more time for running/execution.