Huge program size C++ with std::regex - c++

I want to compile a small C++ program with std::regex (standard regular expressions library).
Compiler: gcc/g++ 4.9.2 on Fedora 21.
#include <string.h>
#include <iostream>
#include <regex>
using namespace std;
int main () {
cout << "Content-Type: text/html\n\n";
std::string s ("there is a subsequence in the string\n");
std::regex e ("\\b(sub)([^ ]*)"); // matches words beginning by "sub"
std::cout << std::regex_replace (s,e,"sub-$2");
}
It's not possible to compile program with std::regex without -std=c++11, so the suitable instruction for compiling in terminal is:
g++ -std=c++11 code.cpp -o prog
My main question is: the source code is very small, but why is the final file size of the compiled program so huge: 480 kilobytes?
Is it because of the influence of -std=c++11?
What happened and how can I reduce the size of the final binary program?
UPD1.
Using -Os flag is actually good way to reduce program size with std::regex to 100-120 KB from 480 KB.
But it's strange, that even optimized file with std::regexp more than standart 7-12 kylobytes for C/C++ programs with few strings source code.
For example, it's possible to turn the same regex replace trick with RE2 regexp library (in Fedora 21 "yum install re2-devel") in 8.5 KB binary file:
#include <string.h>
#include <iostream>
#include "re2/re2.h"
using namespace std;
int main () {
cout << "Content-Type: text/html\n\n";
std::string s ("there is a subsequence in the string\n");
RE2::GlobalReplace(&s, "\\b(sub)([^ ]*)", "sub-\\2");
cout << s;
}
Compiled with:
g++ -lre2 code.cpp -o prog
UPD2.
objdump for std::regex program:
0 .interp 00000013 08048154 08048154 00000154 2**0
1 .note.ABI-tag 00000020 08048168 08048168 00000168 2**2
2 .note.gnu.build-id 00000024 08048188 08048188 00000188 2**2
3 .gnu.hash 000000b0 080481ac 080481ac 000001ac 2**2
4 .dynsym 000006c0 0804825c 0804825c 0000025c 2**2
5 .dynstr 00000b36 0804891c 0804891c 0000091c 2**0
6 .gnu.version 000000d8 08049452 08049452 00001452 2**1
7 .gnu.version_r 000000d0 0804952c 0804952c 0000152c 2**2
8 .rel.dyn 00000038 080495fc 080495fc 000015fc 2**2
9 .rel.plt 000002b8 08049634 08049634 00001634 2**2
10 .init 00000023 080498ec 080498ec 000018ec 2**2
11 .plt 00000580 08049910 08049910 00001910 2**4
12 .text 0001f862 08049e90 08049e90 00001e90 2**4
13 .fini 00000014 080696f4 080696f4 000216f4 2**2
14 .rodata 00000dc8 08069740 08069740 00021740 2**6
15 .eh_frame_hdr 00003ab4 0806a508 0806a508 00022508 2**2
16 .eh_frame 0000f914 0806dfbc 0806dfbc 00025fbc 2**2
17 .gcc_except_table 00000e00 0807d8d0 0807d8d0 000358d0 2**2
18 .init_array 00000008 0807feec 0807feec 00036eec 2**2
19 .fini_array 00000004 0807fef4 0807fef4 00036ef4 2**2
20 .jcr 00000004 0807fef8 0807fef8 00036ef8 2**2
21 .dynamic 00000100 0807fefc 0807fefc 00036efc 2**2
22 .got 00000004 0807fffc 0807fffc 00036ffc 2**2
23 .got.plt 00000168 08080000 08080000 00037000 2**2
24 .data 00000240 08080180 08080180 00037180 2**6
25 .bss 000001b4 080803c0 080803c0 000373c0 2**6
26 .comment 0000002c 00000000 00000000 000373c0 2**0
objdump for RE2 program:
0 .interp 00000013 08048154 08048154 00000154 2**0
1 .note.ABI-tag 00000020 08048168 08048168 00000168 2**2
2 .note.gnu.build-id 00000024 08048188 08048188 00000188 2**2
3 .gnu.hash 00000034 080481ac 080481ac 000001ac 2**2
4 .dynsym 00000180 080481e0 080481e0 000001e0 2**2
5 .dynstr 00000298 08048360 08048360 00000360 2**0
6 .gnu.version 00000030 080485f8 080485f8 000005f8 2**1
7 .gnu.version_r 000000a0 08048628 08048628 00000628 2**2
8 .rel.dyn 00000010 080486c8 080486c8 000006c8 2**2
9 .rel.plt 00000090 080486d8 080486d8 000006d8 2**2
10 .init 00000023 08048768 08048768 00000768 2**2
11 .plt 00000130 08048790 08048790 00000790 2**4
12 .text 00000332 080488c0 080488c0 000008c0 2**4
13 .fini 00000014 08048bf4 08048bf4 00000bf4 2**2
14 .rodata 00000068 08048c08 08048c08 00000c08 2**2
15 .eh_frame_hdr 00000044 08048c70 08048c70 00000c70 2**2
16 .eh_frame 0000015c 08048cb4 08048cb4 00000cb4 2**2
17 .gcc_except_table 00000028 08048e10 08048e10 00000e10 2**0
18 .init_array 00000008 08049ee4 08049ee4 00000ee4 2**2
19 .fini_array 00000004 08049eec 08049eec 00000eec 2**2
20 .jcr 00000004 08049ef0 08049ef0 00000ef0 2**2
21 .dynamic 00000108 08049ef4 08049ef4 00000ef4 2**2
22 .got 00000004 08049ffc 08049ffc 00000ffc 2**2
23 .got.plt 00000054 0804a000 0804a000 00001000 2**2
24 .data 00000004 0804a054 0804a054 00001054 2**0
25 .bss 00000090 0804a080 0804a080 00001058 2**6
26 .comment 0000002c 00000000 00000000 00001058 2**0
Main difference is in 12.text: in first case used size - 0001f862 (129122); second - only 00000332 (818).

In the case of RE2, most of the actual implementation is in a shared library, which doesn't become part of your executable file. It is loaded into memory separately when you run the program.
In the case of std::regex, this is actually just an alias for std::basic_regex<char>, which is a template. The compiler instantiates the template and builds it into your program directly. Although it is possible for templates to be instantiated inside shared libraries, they often aren't, and the std::basic_regex<char> is not in the shared library in your case.
As an example. Here is how to create a separate shared library with the regex instantiation:
main.cpp:
#include <iostream>
#include <string>
#include "regex.hpp"
int main () {
std::cout << "Content-Type: text/html\n\n";
std::string s {"There is a subsequence in the string\n"};
std::basic_regex<char> e {"\\b(sub)([^ ]*)"};
std::cout << std::regex_replace (s,e,"sub-$2");
}
regex.hpp:
#include <regex>
extern template class std::basic_regex<char>;
extern template std::string
std::regex_replace(
const std::string&,
const std::regex&,
const char*,
std::regex_constants::match_flag_type
);
regex.cpp:
#include "regex.hpp"
template class std::basic_regex<char>;
template std::string
std::regex_replace(
const std::string&,
const std::regex&,
const char*,
std::regex_constants::match_flag_type
);
build steps:
g++ -std=c++11 -Os -c -o main.o main.cpp
g++ -std=c++11 -Os -c -fpic regex.cpp
g++ -shared -o libregex.so regex.o
g++ -o main main.o libregex.so -Wl,-rpath,. -L. -lregex
On my system, the resulting file sizes are:
main: 13936
libregex.so: 196936

First, as it's already said in comments, you should measure the size of optimized binary after doing strip or using the size utility. Otherwise you pay too much attention to debug info stored in the binary. That info normally doesn't occupy the RAM even if you really run that binary.
Second, the actual answer — most of the binary size comes from regex itself and other things in std behind it. You can inspect this using readelf utility, like: readelf -sW prog | c++filt — shows all the functions in the binary with their sizes. It seems that quite a large portion of regex implementation is left as template functions which instantiated in your binary. GCC authors might instantiate more in libstdc++ instead, to allow sharing, like they do with some other things, e.g. some methods of string.
One more not very famous binary size optimization technique: ICF (identical code folding) implemented in gold of binutils. Add -fuse-ld=gold -Wl,--icf=safe to your linker flags.

You can reduce the size by setting optimization:
g++ -std=c++11 -O3 -o prog code.cpp
The -O3 flag means maximum optimization. In my machine that reduces the executable from 519K to 142K
You can also use -Os to optimize for size. On my machine that further reduces the size to 120k.
g++ -std=c++11 -Os -o prog code.cpp

Related

How to find EH_FRAME

Suppose I wanted a program written in C++ read its own eh_frame, in order to get information required for stack unwinding and exception handling. How to find out where it begins?
Suppose I wanted a program written in C++ read its own eh_frame
The .eh_frame_hdr is linked into its own GNU_EH_FRAME program header, precisely so it's easy for runtime libraries to locate .eh_frame at runtime.
Here is a typical layout:
readelf -Wl /bin/date
Elf file type is DYN (Position-Independent Executable file)
Entry point 0x3cd0
There are 11 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x000268 0x000268 R 0x8
INTERP 0x0002a8 0x00000000000002a8 0x00000000000002a8 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x002790 0x002790 R 0x1000
LOAD 0x003000 0x0000000000003000 0x0000000000003000 0x010139 0x010139 R E 0x1000
LOAD 0x014000 0x0000000000014000 0x0000000000014000 0x005ad8 0x005ad8 R 0x1000
LOAD 0x01a250 0x000000000001b250 0x000000000001b250 0x001090 0x001248 RW 0x1000
DYNAMIC 0x01adf8 0x000000000001bdf8 0x000000000001bdf8 0x0001e0 0x0001e0 RW 0x8
NOTE 0x0002c4 0x00000000000002c4 0x00000000000002c4 0x000044 0x000044 R 0x4
GNU_EH_FRAME 0x017fe0 0x0000000000017fe0 0x0000000000017fe0 0x00040c 0x00040c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x01a250 0x000000000001b250 0x000000000001b250 0x000db0 0x000db0 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .data.rel.ro .dynamic .got .got.plt .data .bss
06 .dynamic
07 .note.gnu.build-id .note.ABI-tag
08 .eh_frame_hdr
09
10 .init_array .fini_array .data.rel.ro .dynamic .got
So start with GNU_EH_FRAME segment, and follow the links.
You could use (like RefPerSys and GCC do) Ian Lance Taylor's libbacktrace for that purpose.
(but you'll better compile your C++ code with DWARF debug information, so g++ -Wall -g -O ...)

my executable file is so big even though my main doesn't call anything (C++)

i'm a beginner coder here.
so I have a file with main that looks like this
#include <iostream>
#include "Helper.hpp"
using namespace std;
int main(int argc, char* argv[])
{
cout<<argc<<endl;
return 0;
}
and I linked it with 2 other .cpp
I'm just wondering why the executable comes out to be so big (330kb)
when the main doesn't even call any functions.
are all the methods and variables in my other 2 .cpp wrapped up in the executable even though
I haven't actually called any functions? I only have 1 extern int declared. no static functions
this is the makefile call I used to compile
CXX=g++
CXXFLAGS =-Wall -pedantic -g -O0 -std=c++11
test: Helper.cpp Helper.hpp HCTree.cpp HCTree.hpp
$(CXX) $(CXXFLAGS) -o test test.cpp Helper.cpp HCTree.cpp
and I call "make test"
Reason is that your binary will include parts of the standard libraries and the symbol maps, even if you compile the extreme trivial case as follows:
main.cpp
using namespace std;
int main(int argc, char* argv[])
{
return 0;
}
~# g++ -Wall -pedantic -g -O0 -std=c++11 -o test main.cpp
~# ls -ltr
-rwxrwxr-x 1 jordan jordan 10264 Mar 1 08:14 test
after compiling it, my binary is around 10k. If you are compiling it for linux, you can see what is in the binary by calling readelf -a or by examining the symbols in the file with nm.
~# readelf -a test
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: AArch64
Version: 0x1
Entry point address: 0x610
Start of program headers: 64 (bytes into file)
Start of section headers: 8152 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 33
Section header string table index: 32
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000238 00000238
000000000000001b 0000000000000000 A 0 0 1
[ 2] .note.gnu.build-i NOTE 0000000000000254 00000254
0000000000000024 0000000000000000 A 0 0 4
[ 3] .note.ABI-tag NOTE 0000000000000278 00000278
0000000000000020 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000298 00000298
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000000002b8 000002b8
00000000000000d8 0000000000000018 A 6 3 8
[ 6] .dynstr STRTAB 0000000000000390 00000390
0000000000000082 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000000412 00000412
0000000000000012 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000000428 00000428
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 0000000000000448 00000448
00000000000000f0 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000000538 00000538
0000000000000060 0000000000000018 AI 5 21 8
[11] .init PROGBITS 0000000000000598 00000598
0000000000000014 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 00000000000005b0 000005b0
0000000000000060 0000000000000010 AX 0 0 16
[13] .text PROGBITS 0000000000000610 00000610
00000000000001ac 0000000000000000 AX 0 0 8
[14] .fini PROGBITS 00000000000007bc 000007bc
0000000000000010 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 00000000000007cc 000007cc
0000000000000004 0000000000000004 AM 0 0 4
[16] .eh_frame_hdr PROGBITS 00000000000007d0 000007d0
0000000000000044 0000000000000000 A 0 0 4
[17] .eh_frame PROGBITS 0000000000000818 00000818
00000000000000dc 0000000000000000 A 0 0 8
[18] .init_array INIT_ARRAY 0000000000010d88 00000d88
0000000000000008 0000000000000008 WA 0 0 8
[19] .fini_array FINI_ARRAY 0000000000010d90 00000d90
0000000000000008 0000000000000008 WA 0 0 8
[20] .dynamic DYNAMIC 0000000000010d98 00000d98
00000000000001f0 0000000000000010 WA 6 0 8
[21] .got PROGBITS 0000000000010f88 00000f88
0000000000000078 0000000000000008 WA 0 0 8
[22] .data PROGBITS 0000000000011000 00001000
0000000000000010 0000000000000000 WA 0 0 8
[23] .bss NOBITS 0000000000011010 00001010
0000000000000008 0000000000000000 WA 0 0 1
[24] .comment PROGBITS 0000000000000000 00001010
000000000000002a 0000000000000001 MS 0 0 1
[25] .debug_aranges PROGBITS 0000000000000000 0000103a
0000000000000030 0000000000000000 0 0 1
[26] .debug_info PROGBITS 0000000000000000 0000106a
0000000000000098 0000000000000000 0 0 1
[27] .debug_abbrev PROGBITS 0000000000000000 00001102
0000000000000078 0000000000000000 0 0 1
[28] .debug_line PROGBITS 0000000000000000 0000117a
0000000000000050 0000000000000000 0 0 1
[29] .debug_str PROGBITS 0000000000000000 000011ca
00000000000000bf 0000000000000001 MS 0 0 1
[30] .symtab SYMTAB 0000000000000000 00001290
0000000000000900 0000000000000018 31 73 8
[31] .strtab STRTAB 0000000000000000 00001b90
000000000000030d 0000000000000000 0 0 1
[32] .shstrtab STRTAB 0000000000000000 00001e9d
000000000000013a 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000001f8 0x00000000000001f8 R 0x8
INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238
0x000000000000001b 0x000000000000001b R 0x1
[Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000008f4 0x00000000000008f4 R E 0x10000
LOAD 0x0000000000000d88 0x0000000000010d88 0x0000000000010d88
0x0000000000000288 0x0000000000000290 RW 0x10000
DYNAMIC 0x0000000000000d98 0x0000000000010d98 0x0000000000010d98
0x00000000000001f0 0x00000000000001f0 RW 0x8
NOTE 0x0000000000000254 0x0000000000000254 0x0000000000000254
0x0000000000000044 0x0000000000000044 R 0x4
GNU_EH_FRAME 0x00000000000007d0 0x00000000000007d0 0x00000000000007d0
0x0000000000000044 0x0000000000000044 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000000d88 0x0000000000010d88 0x0000000000010d88
0x0000000000000278 0x0000000000000278 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .dynamic .got .data .bss
04 .dynamic
05 .note.gnu.build-id .note.ABI-tag
06 .eh_frame_hdr
07
08 .init_array .fini_array .dynamic .got
Dynamic section at offset 0xd98 contains 27 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000c (INIT) 0x598
0x000000000000000d (FINI) 0x7bc
0x0000000000000019 (INIT_ARRAY) 0x10d88
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x10d90
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x298
0x0000000000000005 (STRTAB) 0x390
0x0000000000000006 (SYMTAB) 0x2b8
0x000000000000000a (STRSZ) 130 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x10f88
0x0000000000000002 (PLTRELSZ) 96 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x538
0x0000000000000007 (RELA) 0x448
0x0000000000000008 (RELASZ) 240 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW PIE
0x000000006ffffffe (VERNEED) 0x428
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x412
0x000000006ffffff9 (RELACOUNT) 6
0x0000000000000000 (NULL) 0x0
Relocation section '.rela.dyn' at offset 0x448 contains 10 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000010d88 000000000403 R_AARCH64_RELATIV 718
000000010d90 000000000403 R_AARCH64_RELATIV 6d0
000000010fc8 000000000403 R_AARCH64_RELATIV 7b8
000000010fe8 000000000403 R_AARCH64_RELATIV 738
000000010ff0 000000000403 R_AARCH64_RELATIV 71c
000000011008 000000000403 R_AARCH64_RELATIV 11008
000000010fd0 000300000401 R_AARCH64_GLOB_DA 0000000000000000 _ITM_deregisterTMClone + 0
000000010fd8 000400000401 R_AARCH64_GLOB_DA 0000000000000000 __cxa_finalize#GLIBC_2.17 + 0
000000010fe0 000600000401 R_AARCH64_GLOB_DA 0000000000000000 __gmon_start__ + 0
000000010ff8 000800000401 R_AARCH64_GLOB_DA 0000000000000000 _ITM_registerTMCloneTa + 0
Relocation section '.rela.plt' at offset 0x538 contains 4 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000010fa0 000400000402 R_AARCH64_JUMP_SL 0000000000000000 __cxa_finalize#GLIBC_2.17 + 0
000000010fa8 000500000402 R_AARCH64_JUMP_SL 0000000000000000 __libc_start_main#GLIBC_2.17 + 0
000000010fb0 000600000402 R_AARCH64_JUMP_SL 0000000000000000 __gmon_start__ + 0
000000010fb8 000700000402 R_AARCH64_JUMP_SL 0000000000000000 abort#GLIBC_2.17 + 0
The decoding of unwind sections for machine type AArch64 is not currently supported.
Symbol table '.dynsym' contains 9 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000598 0 SECTION LOCAL DEFAULT 11
2: 0000000000011000 0 SECTION LOCAL DEFAULT 22
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
4: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize#GLIBC_2.17 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main#GLIBC_2.17 (2)
6: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND abort#GLIBC_2.17 (2)
8: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
Symbol table '.symtab' contains 96 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000238 0 SECTION LOCAL DEFAULT 1
2: 0000000000000254 0 SECTION LOCAL DEFAULT 2
3: 0000000000000278 0 SECTION LOCAL DEFAULT 3
4: 0000000000000298 0 SECTION LOCAL DEFAULT 4
5: 00000000000002b8 0 SECTION LOCAL DEFAULT 5
6: 0000000000000390 0 SECTION LOCAL DEFAULT 6
7: 0000000000000412 0 SECTION LOCAL DEFAULT 7
8: 0000000000000428 0 SECTION LOCAL DEFAULT 8
9: 0000000000000448 0 SECTION LOCAL DEFAULT 9
10: 0000000000000538 0 SECTION LOCAL DEFAULT 10
11: 0000000000000598 0 SECTION LOCAL DEFAULT 11
12: 00000000000005b0 0 SECTION LOCAL DEFAULT 12
13: 0000000000000610 0 SECTION LOCAL DEFAULT 13
14: 00000000000007bc 0 SECTION LOCAL DEFAULT 14
15: 00000000000007cc 0 SECTION LOCAL DEFAULT 15
16: 00000000000007d0 0 SECTION LOCAL DEFAULT 16
17: 0000000000000818 0 SECTION LOCAL DEFAULT 17
18: 0000000000010d88 0 SECTION LOCAL DEFAULT 18
19: 0000000000010d90 0 SECTION LOCAL DEFAULT 19
20: 0000000000010d98 0 SECTION LOCAL DEFAULT 20
21: 0000000000010f88 0 SECTION LOCAL DEFAULT 21
22: 0000000000011000 0 SECTION LOCAL DEFAULT 22
23: 0000000000011010 0 SECTION LOCAL DEFAULT 23
24: 0000000000000000 0 SECTION LOCAL DEFAULT 24
25: 0000000000000000 0 SECTION LOCAL DEFAULT 25
26: 0000000000000000 0 SECTION LOCAL DEFAULT 26
27: 0000000000000000 0 SECTION LOCAL DEFAULT 27
28: 0000000000000000 0 SECTION LOCAL DEFAULT 28
29: 0000000000000000 0 SECTION LOCAL DEFAULT 29
30: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc/aarch64-linu
31: 0000000000000278 0 NOTYPE LOCAL DEFAULT 3 $d
32: 0000000000000610 0 NOTYPE LOCAL DEFAULT 13 $x
33: 00000000000007cc 0 NOTYPE LOCAL DEFAULT 15 $d
34: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc/aarch64-linu
35: 0000000000000648 0 NOTYPE LOCAL DEFAULT 13 $x
36: 0000000000000648 20 FUNC LOCAL DEFAULT 13 call_weak_fn
37: 0000000000000598 0 NOTYPE LOCAL DEFAULT 11 $x
38: 00000000000007bc 0 NOTYPE LOCAL DEFAULT 14 $x
39: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc/aarch64-linu
40: 00000000000005a4 0 NOTYPE LOCAL DEFAULT 11 $x
41: 00000000000007c4 0 NOTYPE LOCAL DEFAULT 14 $x
42: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
43: 0000000000000660 0 NOTYPE LOCAL DEFAULT 13 $x
44: 0000000000000660 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
45: 0000000000000690 0 FUNC LOCAL DEFAULT 13 register_tm_clones
46: 0000000000011008 0 NOTYPE LOCAL DEFAULT 22 $d
47: 00000000000006d0 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
48: 0000000000011010 1 OBJECT LOCAL DEFAULT 23 completed.9126
49: 0000000000010d90 0 NOTYPE LOCAL DEFAULT 19 $d
50: 0000000000010d90 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin
51: 0000000000000718 0 FUNC LOCAL DEFAULT 13 frame_dummy
52: 0000000000010d88 0 NOTYPE LOCAL DEFAULT 18 $d
53: 0000000000010d88 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_
54: 000000000000082c 0 NOTYPE LOCAL DEFAULT 17 $d
55: 0000000000011010 0 NOTYPE LOCAL DEFAULT 23 $d
56: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.cpp
57: 000000000000071c 0 NOTYPE LOCAL DEFAULT 13 $x
58: 0000000000000890 0 NOTYPE LOCAL DEFAULT 17 $d
59: 0000000000000000 0 FILE LOCAL DEFAULT ABS elf-init.oS
60: 0000000000000738 0 NOTYPE LOCAL DEFAULT 13 $x
61: 00000000000008a8 0 NOTYPE LOCAL DEFAULT 17 $d
62: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
63: 00000000000008f0 0 NOTYPE LOCAL DEFAULT 17 $d
64: 00000000000008f0 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
65: 0000000000000000 0 FILE LOCAL DEFAULT ABS
66: 0000000000010d90 0 NOTYPE LOCAL DEFAULT 18 __init_array_end
67: 0000000000010d98 0 OBJECT LOCAL DEFAULT ABS _DYNAMIC
68: 0000000000010d88 0 NOTYPE LOCAL DEFAULT 18 __init_array_start
69: 00000000000007d0 0 NOTYPE LOCAL DEFAULT 16 __GNU_EH_FRAME_HDR
70: 0000000000010fc0 0 OBJECT LOCAL DEFAULT ABS _GLOBAL_OFFSET_TABLE_
71: 0000000000000598 0 FUNC LOCAL DEFAULT 11 _init
72: 00000000000005b0 0 NOTYPE LOCAL DEFAULT 12 $x
73: 00000000000007b8 4 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
74: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab
75: 0000000000011000 0 NOTYPE WEAK DEFAULT 22 data_start
76: 0000000000011010 0 NOTYPE GLOBAL DEFAULT 23 __bss_start__
77: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize##GLIBC_2.1
78: 0000000000011018 0 NOTYPE GLOBAL DEFAULT 23 _bss_end__
79: 0000000000011010 0 NOTYPE GLOBAL DEFAULT 22 _edata
80: 00000000000007bc 0 FUNC GLOBAL HIDDEN 14 _fini
81: 0000000000011018 0 NOTYPE GLOBAL DEFAULT 23 __bss_end__
82: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main##GLIBC_
83: 0000000000011000 0 NOTYPE GLOBAL DEFAULT 22 __data_start
84: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
85: 0000000000011008 0 OBJECT GLOBAL HIDDEN 22 __dso_handle
86: 0000000000000000 0 FUNC GLOBAL DEFAULT UND abort##GLIBC_2.17
87: 00000000000007cc 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
88: 0000000000000738 124 FUNC GLOBAL DEFAULT 13 __libc_csu_init
89: 0000000000011018 0 NOTYPE GLOBAL DEFAULT 23 _end
90: 0000000000000610 0 FUNC GLOBAL DEFAULT 13 _start
91: 0000000000011018 0 NOTYPE GLOBAL DEFAULT 23 __end__
92: 0000000000011010 0 NOTYPE GLOBAL DEFAULT 23 __bss_start
93: 000000000000071c 24 FUNC GLOBAL DEFAULT 13 main
94: 0000000000011010 0 OBJECT GLOBAL HIDDEN 22 __TMC_END__
95: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable
Version symbols section '.gnu.version' contains 9 entries:
Addr: 0x0000000000000412 Offset: 0x000412 Link: 5 (.dynsym)
000: 0 (*local*) 0 (*local*) 0 (*local*) 0 (*local*)
004: 2 (GLIBC_2.17) 2 (GLIBC_2.17) 0 (*local*) 2 (GLIBC_2.17)
008: 0 (*local*)
Version needs section '.gnu.version_r' contains 1 entry:
Addr: 0x0000000000000428 Offset: 0x000428 Link: 6 (.dynstr)
000000: Version: 1 File: libc.so.6 Cnt: 1
0x0010: Name: GLIBC_2.17 Flags: none Version: 2
Displaying notes found in: .note.gnu.build-id
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 3e05092df57e4b081d4823e60f0e74fa226ac79f
Displaying notes found in: .note.ABI-tag
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.7.0
~# nm -a test
nm -a test
0000000000000000 a
U abort##GLIBC_2.17
0000000000011010 b .bss
0000000000011018 B __bss_end__
0000000000011018 B _bss_end__
0000000000011010 B __bss_start
0000000000011010 B __bss_start__
0000000000000648 t call_weak_fn
0000000000000000 n .comment
0000000000011010 b completed.9126
0000000000000000 a crtstuff.c
0000000000000000 a crtstuff.c
w __cxa_finalize##GLIBC_2.17
0000000000011000 d .data
0000000000011000 D __data_start
0000000000011000 W data_start
0000000000000000 N .debug_abbrev
0000000000000000 N .debug_aranges
0000000000000000 N .debug_info
0000000000000000 N .debug_line
0000000000000000 N .debug_str
0000000000000660 t deregister_tm_clones
00000000000006d0 t __do_global_dtors_aux
0000000000010d90 d __do_global_dtors_aux_fini_array_entry
0000000000011008 D __dso_handle
0000000000010d98 d .dynamic
0000000000010d98 a _DYNAMIC
0000000000000390 r .dynstr
00000000000002b8 r .dynsym
0000000000011010 D _edata
0000000000000818 r .eh_frame
00000000000007d0 r .eh_frame_hdr
0000000000000000 a elf-init.oS
0000000000011018 B __end__
0000000000011018 B _end
00000000000007bc t .fini
00000000000007bc T _fini
0000000000010d90 d .fini_array
0000000000000718 t frame_dummy
0000000000010d88 d __frame_dummy_init_array_entry
00000000000008f0 r __FRAME_END__
0000000000010fc0 a _GLOBAL_OFFSET_TABLE_
w __gmon_start__
00000000000007d0 r __GNU_EH_FRAME_HDR
0000000000000298 r .gnu.hash
0000000000000412 r .gnu.version
0000000000000428 r .gnu.version_r
0000000000010f88 d .got
0000000000000598 t .init
0000000000000598 t _init
0000000000010d88 d .init_array
0000000000010d90 d __init_array_end
0000000000010d88 d __init_array_start
0000000000000238 r .interp
00000000000007cc R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
00000000000007b8 T __libc_csu_fini
0000000000000738 T __libc_csu_init
U __libc_start_main##GLIBC_2.17
000000000000071c T main
0000000000000000 a main.cpp
0000000000000278 r .note.ABI-tag
0000000000000254 r .note.gnu.build-id
00000000000005b0 t .plt
0000000000000690 t register_tm_clones
0000000000000448 r .rela.dyn
0000000000000538 r .rela.plt
00000000000007cc r .rodata
0000000000000610 T _start
0000000000000610 t .text
0000000000011010 D __TMC_END__
0000000000000000 a /usr/lib/gcc/aarch64-linux-gnu/9/../../../aarch64-linux-gnu/crti.o
0000000000000000 a /usr/lib/gcc/aarch64-linux-gnu/9/../../../aarch64-linux-gnu/crtn.o
0000000000000000 a /usr/lib/gcc/aarch64-linux-gnu/9/../../../aarch64-linux-gnu/Scrt1.o
You can lower the size by stripping the symbols as follows:
~# strip test
~# ls -ltr
-rwxrwxr-x 1 jordan jordan 6056 Mar 1 08:21 test
so the file went from 10k to 6k.

Is linking the entire standard library to every C/C++ file space efficient?

Having the entire standard library or any other library already compiled and ready to be linked to form an executable is a nice feature and it makes the compilation faster, but as far as I know, the entire library is linked even if only a few functions in it are used.
So for instance on my machine, the following code is 1.6 kB when compiled to object code but it becomes almost 17 kB when I link it to the standard library.
#include <stdio.h>
int main(void)
{
printf("Hello world\n");
}
Is there any other way to recompile only the parts of the standard library (or any other library) that are necessary in order to make the program more space-efficient?
Sorry if the question is already asked, I googled it but couldn't find any answers to it.
If we dump the contents of the generated ELF executable we will see no parts of the C runtime library embedded in the binary, because the GLIBC library is linked in dynamically (e.g. from libc.so.6).
$ gcc -Os -s a.c
$ ls -la a.out
-rwxrwxrwx 1 user user 14408 Feb 18 12:56 a.out
$ ldd a.out
linux-vdso.so.1 (0x00007ffd157f8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007feb0a33f000)
/lib64/ld-linux-x86-64.so.2 (0x00007feb0a518000)
$ objdump -T a.out
a.out: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 puts
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_finalize
We notice also that GCC optimized a printf with no placeholders into a puts (for the file size it doesn't matter).
To see "inside" the ELF we can dump its sections:
$ readelf -SW a.out
There are 28 section headers, starting at offset 0x3148:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 00000000000002a8 0002a8 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 00000000000002c4 0002c4 000020 00 A 0 0 4
[ 3] .note.gnu.build-id NOTE 00000000000002e4 0002e4 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000308 000308 000024 00 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000000330 000330 0000a8 18 A 6 1 8
[ 6] .dynstr STRTAB 00000000000003d8 0003d8 000082 00 A 0 0 1
[ 7] .gnu.version VERSYM 000000000000045a 00045a 00000e 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000000468 000468 000020 00 A 6 1 8
[ 9] .rela.dyn RELA 0000000000000488 000488 0000c0 18 A 5 0 8
[10] .rela.plt RELA 0000000000000548 000548 000018 18 AI 5 23 8
[11] .init PROGBITS 0000000000001000 001000 000017 00 AX 0 0 4
[12] .plt PROGBITS 0000000000001020 001020 000020 10 AX 0 0 16
[13] .plt.got PROGBITS 0000000000001040 001040 000008 08 AX 0 0 8
[14] .text PROGBITS 0000000000001050 001050 000171 00 AX 0 0 16
[15] .fini PROGBITS 00000000000011c4 0011c4 000009 00 AX 0 0 4
[16] .rodata PROGBITS 0000000000002000 002000 000010 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 0000000000002010 002010 00003c 00 A 0 0 4
[18] .eh_frame PROGBITS 0000000000002050 002050 000100 00 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000003de8 002de8 000008 08 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000003df0 002df0 000008 08 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000003df8 002df8 0001e0 10 WA 6 0 8
[22] .got PROGBITS 0000000000003fd8 002fd8 000028 08 WA 0 0 8
[23] .got.plt PROGBITS 0000000000004000 003000 000020 08 WA 0 0 8
[24] .data PROGBITS 0000000000004020 003020 000010 00 WA 0 0 8
[25] .bss NOBITS 0000000000004030 003030 000008 00 WA 0 0 1
[26] .comment PROGBITS 0000000000000000 003030 00001c 01 MS 0 0 1
[27] .shstrtab STRTAB 0000000000000000 00304c 0000f7 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
So ~14 kB is pretty much the minimal ELF executable size these days due to PIC, relro, eh_frame and other sections generated by the GNU linker.
You can reduce the size somewhat by turning off relro (which reduces the security a little).
$ gcc -Os -s -z norelro -Wl,--gc-sections a.c
$ $ ls -la a.out
-rwxrwxrwx 1 user user 10960 Feb 18 13:06 a.out

Why there is no .bss segment in COFF object file?

A simple hello world program:
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
After dumpbin it with /HEADERS flag, i get those segments:
8 .bss
A0 .debug$S
62 .drectve
F .rdata
8B .text$mn
If compile the program with /TC, so that it's a C program, i get those segments after the same use of a dumpbin:
2000 .data
1000 .gfids
7000 .rdata
1000 .reloc
10000 .text
point is:
How do i get this similar kind of output:
# objdump -hrt hello.o
hello.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000011 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 00000048 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000048 2**2
ALLOC
3 .rodata.str1.1 0000000d 00000000 00000000 00000048 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .comment 00000033 00000000 00000000 00000055 2**0
CONTENTS, READONLY
SYMBOL TABLE:
00000000 l df *ABS* 00000000 hello.c
00000000 l d .text 00000000
00000000 l d .data 00000000
00000000 l d .bss 00000000
00000000 l d .rodata.str1.1 00000000
00000000 l d .comment 00000000
00000000 g F .text 00000011 main
00000000 *UND* 00000000 puts

Function with weak atributte can not be overwritten

I would like to overwrite function (interupt handlers) with the weak attribute, but linker does not link my definition. Codes are shorted for better reading.
vectors.c
void NMI_Handler (void) __attribute__((weak));
void HardFault_Handler (void) __attribute__((weak));
__attribute__ ((section(".vectors"), used))
void (* const gVectors[])(void) =
{
NMI_Handler,
HardFault_Handler
};
void NMI_Handler (void) { while(1); }
void HardFault_Handler (void) { while(1); }
I redefine default definition in the file cpuexcept.cpp
extern "C" __attribute__((naked))
void NMI_Handler()
{
EXCEPT_ENTRY(CPUExcept);
}
extern "C" __attribute__((naked))
void HardFault_Handler()
{
EXCEPT_ENTRY(CPUExcept);
}
If I compile and dump it, output (library lib.a) is:
cpuexcept.oo: file format elf32-littlearm
rw-rw-rw- 0/0 4728 Jun 26 16:20 2012 cpuexcept.oo
architecture: arm, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
private flags = 5000000: [Version5 EABI]
Sections:
Idx Name Size VMA LMA File off Algn Flags
0 .text 0000051c 00000000 00000000 00000034 2**2 CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 00000000 00000000 00000550 2**0 CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000550 2**0 ALLOC
3 .rodata 000001dc 00000000 00000000 00000550 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .comment 00000012 00000000 00000000 0000072c 2**0 CONTENTS, READONLY
5 .ARM.attributes 00000031 00000000 00000000 0000073e 2**0 CONTENTS, READONLY
SYMBOL TABLE:
00000000 l df *ABS* 00000000 cpuexcept.cpp
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
000004e0 g F .text 0000000a NMI_Handler
000004ec g F .text 0000000a HardFault_Handler
000004e0 <NMI_Handler>:
4e0: e92d 0ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
4e4: 4668 mov r0, sp
4e6: f7ff fffe bl c0 <CPUExcept> 4e6: R_ARM_THM_CALL CPUExcept
4ea: bf00 nop
000004ec <HardFault_Handler>:
4ec: e92d 0ff0 stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
4f0: 4668 mov r0, sp
4f2: f7ff fffe bl c0 <CPUExcept> 4f2: R_ARM_THM_CALL CPUExcept
4f6: bf00 nop
vectors.o: file format elf32-littlearm
rw-rw-rw- 0/0 4464 Jun 27 13:52 2012 vectors.o
architecture: arm, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000
private flags = 5000000: [Version5 EABI]
Sections:
Idx Name Size VMA LMA File off Algn Flags
0 .text 00000114 00000000 00000000 00000034 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00000000 00000000 00000148 2**0 CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000148 2**0 ALLOC
3 .vectors 00000130 00000000 00000000 00000148 2**2 CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
4 .comment 00000012 00000000 00000000 00000278 2**0 CONTENTS, READONLY
5 .ARM.attributes 00000031 00000000 00000000 0000028a 2**0 CONTENTS, READONLY
SYMBOL TABLE:
00000000 l df *ABS* 00000000 vectors.c
00000000 l d .text 00000000 .text
00000000 l d .data 00000000 .data
00000000 l d .bss 00000000 .bss
00000000 l d .vectors 00000000 .vectors
00000000 l d .comment 00000000 .comment
00000000 l d .ARM.attributes 00000000 .ARM.attributes
00000000 w F .text 00000002 NMI_Handler
00000004 w F .text 00000002 HardFault_Handler
00000000 <NMI_Handler>:
0: e7fe b.n 0 <NMI_Handler>
2: bf00 nop
00000004 <HardFault_Handler>:
4: e7fe b.n 4 <HardFault_Handler>
6: bf00 nop
Default function with the weak attribute is linked in to target application. My definition is linked correct, if I define function f() in cpuexcept.cpp and I use it in main function or if my definiton of handler is in other .c module.
I use arm-none-eabi-gcc 4.6.2 (YAGARTO) compiler in cygwin.
You could try to define your function like
void NMI_Handler(void)
and not as
void NMI_Handler()
As the weak definition is also void NMI_Handler (void) __attribute__((weak));
So the linker will not see any difference.