Search code examples
c++templatesgccclang

Using clang or gcc, how do I list the templates instantiated in an object file (.o)?


I have a large program that's generating object files that are much larger than I expect. My suspicion is that somewhere in the program, someone is using inefficient template metaprogramming that's generating O(n**2) template types. Is there a command-line tool that I can use to list all of the template types that exist in an object file (.o)?

Normally I would suspect nm or objdump is the right tool for this kind of thing, but it's not obvious to me what flags to pass to list the template types.

I've verified that the information is in the .o file using this simple test program:

template <typename T, typename... Ts>
    struct foo : public foo<Ts...> {};

template <>
    struct foo<int> {};

void bar() {
  foo<int, int, int, int, int, int, int, int> x;
}

Then running:

gcc -g -c test.cc -o test.o && strings test.o

Outputs:

foo<int, int, int, int, int, int, int, int>
GNU C++17 13.2.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables
_Z3barv
foo<int, int, int, int, int, int, int>
foo<int, int, int, int>
foo<int, int, int, int, int, int>
foo<int, int, int>
foo<int>
foo<int, int>
foo<int, int, int, int, int>
/tmp
test.cc
/tmp
test.cc
test.cc
GCC: (Debian 13.2.0-10) 13.2.0
test.cc
_Z3barv
.symtab
.strtab
.shstrtab
.text
.data
.bss
.rela.debug_info
.debug_abbrev
.rela.debug_aranges
.rela.debug_line
.debug_str
.debug_line_str
.comment
.note.GNU-stack
.rela.eh_frame

I'm looking for a command that will output foo<int>, foo<int, int>, etc. from test.o.


Solution

  • In my previous, now deleted, answer I kicked off with:

    By the time you've got to an object file, classes have no representation, therefore classes that instantiate templates have no representation

    To which you commented:

    Re: "classes that instantiate templates have no representation". I made a simple test.cc file that instantiates many template classes that have no methods or members. If I compile it with debugging information and run strings test.o, I can see the names of the instantiated templates in the object file. They're definitely in there.

    Which is so. I should have chosen my words more carefully. Classes have no linkage representation. But that's quite immaterial if you can readily make an object file containing the strings your're looking for. The strings you are seeing are DWARF debugging info, which GCC by default packages into the object the file (or optionally with a separate .dwo file). I didn't consider getting the debug info, but indeed it is - or it contains - the information you want.

    Naturally you can get at the debug info in a straightforwardly structured format unpolluted with whatever other strings the object file might contain. If I compile my previous example.cpp like so:

    $ g++ -g -ggnu-pubnames -c example.cpp
    

    then the compiler is instructed (GCC manual: 3.10 Options for Debugging Your Program):

    -ggnu-pubnames

    Generate .debug_pubnames and .debug_pubtypes sections in a format suitable for conversion into a GDB index. This option is only useful with a linker that can produce GDB index version 7.

    Then, the .debug_pubtypes section will itemise every type that is used in the object file, and they can be specifically extracted with:

    $ readelf --debug-dump=pubtypes example.o | c++filt
    Contents of the .debug_gnu_pubtypes section:
    
      Length:                              1670
      Version:                             2
      Offset into .debug_info section:     0
      Size of area in .debug_info section: 4479
    
        Offset  Kind          Name
        37      g,type        std::integral_constant<bool, true>
        be9     s,type        bool
        37      g,type        std::integral_constant<bool, true>
        b2      g,type        std::integral_constant<bool, false>
        b2      g,type        std::integral_constant<bool, false>
        12d     g,type        std::integral_constant<long unsigned int, 0>
        c0b     s,type        long unsigned int
        12d     g,type        std::integral_constant<long unsigned int, 0>
        c21     s,type        unsigned char
        c28     s,type        short unsigned int
        c2f     s,type        unsigned int
        c36     s,type        long long unsigned int
        c42     s,type        __int128 unsigned
        1a8     g,type        std::__make_unsigned_selector_base
        1a8     g,type        std::__make_unsigned_selector_base
        264     s,type        std::size_t
        c65     s,type        signed char
        c6c     s,type        short int
        c73     s,type        int
        c7f     s,type        long int
        c86     s,type        long long int
        c8d     s,type        __int128
        c94     s,type        wchar_t
        c9b     s,type        char16_t
        ca2     s,type        char32_t
        284     g,type        std::__is_memcmp_ordered_with<std::byte, std::byte, true>
        284     g,type        std::__is_memcmp_ordered_with<std::byte, std::byte, true>
        d6e     s,type        long double
        d75     s,type        double
        d7c     s,type        float
        2b8     g,type        std::in_place_t
        2b8     g,type        std::in_place_t
        2f3     g,type        std::piecewise_construct_t
        2f3     g,type        std::piecewise_construct_t
        32e     g,type        std::integral_constant<long unsigned int, 2>
        32e     g,type        std::integral_constant<long unsigned int, 2>
        cc3     g,type        __gnu_cxx::__numeric_traits_integer<long long unsigned int>
        cc3     g,type        __gnu_cxx::__numeric_traits_integer<long long unsigned int>
        d07     g,type        __gnu_cxx::__numeric_traits_integer<long unsigned int>
        d07     g,type        __gnu_cxx::__numeric_traits_integer<long unsigned int>
        d3c     g,type        __gnu_cxx::__numeric_traits_integer<unsigned int>
        d3c     g,type        __gnu_cxx::__numeric_traits_integer<unsigned int>
        3b1     g,type        std::__array_traits<int, 2>
        3b1     g,type        std::__array_traits<int, 2>
        3de     g,type        std::array<int, 2>
        3de     g,type        std::array<int, 2>
        de9     g,type        point<int, 2>
        7c5     g,type        std::__array_traits<double, 3>
        7c5     g,type        std::__array_traits<double, 3>
        7f2     g,type        std::array<double, 3>
        7f2     g,type        std::array<double, 3>
        ee3     g,type        point<double, 3>
        1177    s,type        char
    

    This appears to be exactly what you need, and I am wiser than I was :)