1

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?

finerbuddy
  • 11
  • 2
  • By 'introduced' I meant 'not written by me'. Introduced by compiler isn't accurate, that's true. – finerbuddy Mar 20 '22 at 14:42
  • What problem are you trying to solve? No, not the problem of identifying which functions in a binary come from source code and which are "introduced by the compiler", but the problem to which you believe this is the solution, so that's what you're asking about. If you explain what the real problem is, perhaps a good solution can be suggested. – Sam Varshavchik Mar 20 '22 at 15:06
  • @SamVarshavchik I am extracting call-graphs from ELF binaries and visualize them. The problem is that there is too much clutter in the visualization and I'm trying to remove the functions that were not written by programmers but introduced by the compiler/linker. – finerbuddy Mar 20 '22 at 15:17
  • You would probably have to look at the names. Names starting with one or two undescores are reserved to the runtime. And everyting in the standard library starts with `std::`. Other names, like [frame_dummy](https://stackoverflow.com/questions/11444847/what-does-frame-dummy-mean-in-the-context-of-profiling), I have no idea. – BoP Mar 20 '22 at 15:47

0 Answers0