Difference between static and dynamic library linking

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.

Leave a Comment