A Surface Level Overview Of The Differences

Introduction

To use static linking on your object files, use the -static flag.

gcc hello.o -o dynamically_linked
gcc -static hello.o -o statically_linked

ELF Size Difference

$ ls -l ./*_linked

-rwxrwxr-x 1 kali kali  15952 Aug  4 10:27 ./dynamically_linked
-rwxrwxr-x 1 kali kali 758424 Aug  4 10:27 ./statically_linked

$ ls -l ./*_linked -h

-rwxrwxr-x 1 kali kali    16K Aug  4 10:27 ./dynamically_linked
-rwxrwxr-x 1 kali kali   741K Aug  4 10:27 ./statically_linked

(15952/758424)*100 = 2.1%

The dynamically linked elf is just 2% of the size of the statically linked one.

Disassembly Difference

objdump -D dynamically_linked > dyn_obj -M intel
objdump -D statically_linked > stat_obj -M intel

Size Difference

$ ls -l ./*_obj

-rw-rw-r-- 1 kali kali   34436 Aug  4 10:40 ./dyn_obj
-rw-rw-r-- 1 kali kali 9892579 Aug  4 10:40 ./stat_obj

$ ls -l ./*_obj -h

-rw-rw-r-- 1 kali kali     34K Aug  4 10:40 ./dyn_obj
-rw-rw-r-- 1 kali kali    9.5M Aug  4 10:40 ./stat_obj

(34436/9892579)*100 = ~0.35%

The disassembly for the dynamically linked elf is ~0.35% of the total size of the statically linked one. That’s not even 1%.

Content Difference

The dynamically linked elf generates a disassembly of 801 lines, whereas the statically linked elf generated a disassembly of 187385 lines.

`readelf` Interpretation

readelf ./dynamically_linked -a > dyn_read
readelf ./statically_linked -a > stat_read 

Size Difference

$ ls -l ./*_read
-rw-rw-r-- 1 kali kali  15584 Aug  4 11:06 ./dyn_read
-rw-rw-r-- 1 kali kali 167259 Aug  4 11:06 ./stat_read

$ ls -l ./*_read -h
-rw-rw-r-- 1 kali kali  16K Aug  4 11:06 ./dyn_read
-rw-rw-r-- 1 kali kali 164K Aug  4 11:06 ./stat_read

(15584/167259)*100 = 9.31%

The output of readelf is significantly smaller for the dynamically linked elf.

File Header

PropertyDynamically LinkedStatically Linked
OS/ABIUNIX - System VUNIX - GNU
TypeET_DYNEX_EXEC
Entry Point Address0x10500x401600
Start of Shdrs13968756760
Program Headers Count1411
Section Headers Count3126
Shstrtab Index3025

The most interesting part here is the difference in ABI. This means, an operating system supports multiple ABI contracts?

  • The GNU ABI is just an extension of the System V ABI.

Section Headers

# Dynamically Linked          # Statically Linked
NULL                          NULL
.note.gnu.property            .note.gnu.property
.note.gnu.build-id            .note.gnu.build-id
.interp
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt                     .rela.plt
.init                         .init
.plt                          .plt
.plt.got
.text                         .text
.fini                         .fini
.rodata                       .rodata
                              rodata.cst32
.eh_frame_hdr
.eh_frame                     .eh_frame
                              .gcc_except_table
.note.ABI-tag                 .note.ABI-tag
                              .tdata
                              .tbss
.init_array                   .init_array
.fini_array                   .fini_array
                              .data.rel.ro
.dynamic
.got                          .got
.got.plt                      .got.plt
.data                         .data
.bss                          .bss
.comment                      .comment
.symtab                       .symtab
.strtab                       .strtab
.shstrtab                     .shstrtab

The statically linked ELF lacks these sections, which are mostly used by the dynamic interpreter program.

.interp
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.plt.got
.eh_frame_hdr
.dynamic

Why?

.interp, .dynsym, .dynstr, .rela.dyn, .plt.got. eh_frame_hdr, .dynamic are self-explanatory. These are specific to runtime resolution.

.gnu.hash, .gnu.version, .gnu.version_r are also runtime used during runtime resolution.

  • To make symbol lookup fast, ELF uses hash tables for dynamic symbols.
  • Since a static ELF has all the relocations performed during link-time, there is no need for a hash table for lookup.

These sections are new in the static ELF.

rodata.cst32
.gcc_except_table
.tdata
.tbss
.data.rel.ro
  • We can avoid this for now.

Comparing the size for section headers,

HeaderDynamically LinkedStatically Linked
.rela.plt0x180x210
.plt0x200xb0
.text0x1030x74ff9
rodata0x120x1bba4
.eh_frame0xac0xb800
.init_array0x100x08
.fini_array0x100x08
.got0x280x88
.got.plt0x200xc8
.data0x100x1a00
.bss0x080x5808
.symtab0x3600xc3c0
.strtab0x1db0x7c84
.shstrtab0x11a0x00f0

Clearly, the size of major sections in a statically linked ELF Is much higher.

Program Headers

# Dynamically Linked           # Statically Linked
PHDR
INTERP
LOAD                           LOAD
LOAD                           LOAD
LOAD                           LOAD
LOAD                           LOAD
DYNAMIC                        
NOTE                           NOTE
NOTE                           NOTE
NOTE                           NOTE
                               TLS
GNU_PROPERTY                   GNU_PROPERTY
GNU_EH_FRAME                   
GNU_STACK                      GNU_STACK
GNU_RELRO                      GNU_RELRO

Section to segment mapping also differs because of different sections.

The size of every program header varies a lot as well. Except GNU_STACK, which is unused in both.

Dynamic Section

There is reason for it to be present in a statically linked ELF.

Relocations

Since there is no dynamic section, .rela.dyn doesn’t make any sense. It is also absent in the statically linked ELF.

There are 22 entries in .rela.plt and their type is R_X86_64_IRELATIV, which is a new relocation type. All of these entries are unknown, with no symbol name. Their info field also lacks symbol index.

Symbol Tables

.dynsym as expected, absent. So as .dynstr

.symtab , on the other hand, is heavily populated. It houses 2088 entries.

Conclusion

A statically linked ELF is a self-contained ELF, and the high size of specific sections shows why it is called a self-contained elf.

Thanks. Bye.

Last updated on