I have this dead simple C++ source code.
#include <iostream>
int add(int a, int b) {
return a + b;
}
int main(void) {
std::cout << add(2, 3) << std::endl;
}
After compiling this into an ELF executable I want to list the functions inside it and check which were actually defined in the source code and which were introduced by the compiler.
I list all functions like this:
eu-readelf -s executable | llvm-cxxfilt | grep FUNC | sort -u
This is the result:
1: 0000000000000000 0 FUNC WEAK DEFAULT UNDEF __cxa_finalize@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)@GLIBCXX_3.4 (3)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF __libc_start_main@GLIBC_2.34 (4)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF __cxa_atexit@GLIBC_2.2.5 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::ostream::operator<<(std::ostream& (*)(std::ostream&))@GLIBCXX_3.4 (3)
5: 0000000000001070 44 FUNC LOCAL DEFAULT 14 __cxx_global_var_init
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::ios_base::Init::Init()@GLIBCXX_3.4 (3)
7: 00000000000010a0 11 FUNC LOCAL DEFAULT 14 _GLOBAL__sub_I_thing.cpp
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::ostream::operator<<(int)@GLIBCXX_3.4 (3)
9: 00000000000010e0 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones
10: 0000000000001110 0 FUNC LOCAL DEFAULT 14 register_tm_clones
11: 0000000000001150 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux
12: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::ios_base::Init::~Init()@GLIBCXX_3.4 (3)
14: 00000000000011a0 0 FUNC LOCAL DEFAULT 14 frame_dummy
25: 0000000000000000 0 FUNC WEAK DEFAULT UNDEF __cxa_finalize@GLIBC_2.2.5
26: 00000000000011d0 52 FUNC GLOBAL DEFAULT 14 main
27: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)@GLIBCXX_3.4
29: 0000000000001204 0 FUNC GLOBAL HIDDEN 15 _fini
30: 00000000000011b0 18 FUNC GLOBAL DEFAULT 14 add(int, int)
31: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF __libc_start_main@GLIBC_2.34
32: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF __cxa_atexit@GLIBC_2.2.5
33: 00000000000010b0 38 FUNC GLOBAL DEFAULT 14 _start
34: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::ostream::operator<<(std::ostream& (*)(std::ostream&))@GLIBCXX_3.4
35: 0000000000001000 0 FUNC GLOBAL HIDDEN 12 _init
41: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::ios_base::Init::Init()@GLIBCXX_3.4
42: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::ostream::operator<<(int)@GLIBCXX_3.4
46: 0000000000000000 0 FUNC GLOBAL DEFAULT UNDEF std::ios_base::Init::~Init()@GLIBCXX_3.4
Only main
and add
are actually defined in the source code, but I can't find a way how to identify that while only analyzing the binary file. Is this even possible?
Could the debug info be used for that, maybe?