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

86 Views Asked by At

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.

2

There are 2 best solutions below

3
Mike Kinghan On BEST ANSWER

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 :)

4
WyattK1093 On

Using nm is one way to do this.

the -C flag will give a readable output that shows templates rather than the mangled names:

nm -C myfile.o

example output:

myfile.o:
0000000000000000 U Box<int>::func(int)
...