C++ lambda not work with backtrace_symbols, symbol not exported in dynamic-symbol-table,

71 Views Asked by At

cpp compiled with -rdynamic already however,lambda symbols still not exported to dynamic-symbol-tables. so, backtrace and backtrace_symbols can not find lambda-symbols.

My question is, any compile/link flags or hack methods to force compiler(g++) generate lambda function mangled symbols into dynamic-symbol-table?

Why do I need this?

in production-env, gdb is not always available, though the mangled symbols are ugly to read, it is still better than nothing.

Some findings

you may say this is gcc's internal-implementation behavior, but in the testcode below, I found lambda returned from a class's public method is exported into dynamic-symbol-table. lambda returned from a global method is not. why is that?

versions

g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

kdy@kdy-System-Product-Name:~$ uname -a
Linux kdy-System-Product-Name 6.2.0-39-generic #40~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 16 10:53:04 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <functional>

namespace test {
/* Obtain a backtrace and print it to stdout. */
void print_trace() {
  void *array[10];
  size_t size;
  char **strings;
  size_t i;

  size = backtrace (array, 10);
  strings = backtrace_symbols (array, size);

  printf ("Obtained %zd stack frames.\n", size);

  for (i = 0; i < size; i++)
     printf ("%s\n", strings[i]);
  free (strings);
}

/* A dummy function to make the backtrace more interesting. */
void dummy_function() {
  print_trace ();
}

std::function<void()> get_lambda1(int b) {
  int a = 0;
  return [a, b]() {
    dummy_function();
  };
}

class Test {
  public:
    std::function<void()> get_lambda2(int b) {
      int a = 1;
      return [a, b]() {
        dummy_function();
      };
    };
};

}  // namespace test

int main()
{
  printf("symbol of global functor\n");
  test::get_lambda1(0)();
  printf("\n\nsymbol of classmember functor\n");
  test::Test().get_lambda2(1)();
  return 0;
}
   

compile the codeblock above with g++ -rdynamic test.cpp

nm -D a.out |grep get_lambda2, you will see

0000000000002d04 W _ZNKSt9_Any_data9_M_accessIZN4test4Test11get_lambda2EiEUlvE_EERKT_v
0000000000002da2 W _ZNSt14_Function_base13_Base_managerIZN4test4Test11get_lambda2EiEUlvE_E10_M_destroyERSt9_Any_dataSt17integral_constantIbLb1EE
0000000000002c5a W _ZNSt14_Function_base13_Base_managerIZN4test4Test11get_lambda2EiEUlvE_E10_M_managerERSt9_Any_dataRKS5_St18_Manager_operation
0000000000002be6 W _ZNSt14_Function_base13_Base_managerIZN4test4Test11get_lambda2EiEUlvE_E14_M_get_pointerERKSt9_Any_data
0000000000002d6d W _ZNSt14_Function_base13_Base_managerIZN4test4Test11get_lambda2EiEUlvE_E15_M_init_functorIRKS3_EEvRSt9_Any_dataOT_
0000000000002aa2 W _ZNSt14_Function_base13_Base_managerIZN4test4Test11get_lambda2EiEUlvE_E15_M_init_functorIS3_EEvRSt9_Any_dataOT_
0000000000002a7d W _ZNSt14_Function_base13_Base_managerIZN4test4Test11get_lambda2EiEUlvE_E21_M_not_empty_functionIS3_EEbRKT_
0000000000002dd3 W _ZNSt14_Function_base13_Base_managerIZN4test4Test11get_lambda2EiEUlvE_E9_M_createIRKS3_EEvRSt9_Any_dataOT_St17integral_constantIbLb1EE
0000000000002b9c W _ZNSt14_Function_base13_Base_managerIZN4test4Test11get_lambda2EiEUlvE_E9_M_createIS3_EEvRSt9_Any_dataOT_St17integral_constantIbLb1EE

which is lambda function in get_lambda2's mangled symbols, nm -D a.out |grep get_lambda1, you will see only

000000000000230e T _ZN4test11get_lambda1Ei

in fact, get_lambda1 related lambda function symbols are already generated, but not in dynamic-symbol-table, this can be checked by nm a.out |grep get_lambda1

the dynamic-symbol-table's difference between get_lambda1 and get_lambda2 causes the functor returned by get_lambda1 can not print the symbols. see the executing result.

dy@kdy-System-Product-Name:~$ ./a.out 
symbol of global functor
Obtained 10 stack frames.
./a.out(_ZN4test11print_traceEv+0x2c) [0x5651c2e3a256]
./a.out(_ZN4test14dummy_functionEv+0xd) [0x5651c2e3a2f6]
./a.out(+0x230b) [0x5651c2e3a30b]
./a.out(+0x2790) [0x5651c2e3a790]
./a.out(+0x2676) [0x5651c2e3a676]
./a.out(+0x2564) [0x5651c2e3a564]
./a.out(_ZNKSt8functionIFvvEEclEv+0x36) [0x5651c2e3aa7a]
./a.out(main+0x48) [0x5651c2e3a3b8]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f2165e29d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f2165e29e40]


symbol of classmember functor
Obtained 10 stack frames.
./a.out(_ZN4test11print_traceEv+0x2c) [0x5651c2e3a256]
./a.out(_ZN4test14dummy_functionEv+0xd) [0x5651c2e3a2f6]
./a.out(_ZZN4test4Test11get_lambda2EiENKUlvE_clEv+0x15) [0x5651c2e3a917]
./a.out(_ZSt13__invoke_implIvRZN4test4Test11get_lambda2EiEUlvE_JEET_St14__invoke_otherOT0_DpOT1_+0x24) [0x5651c2e3ad6a]
./a.out(_ZSt10__invoke_rIvRZN4test4Test11get_lambda2EiEUlvE_JEENSt9enable_ifIX16is_invocable_r_vIT_T0_DpT1_EES5_E4typeEOS6_DpOS7_+0x24) [0x5651c2e3ac38]
./a.out(_ZNSt17_Function_handlerIFvvEZN4test4Test11get_lambda2EiEUlvE_E9_M_invokeERKSt9_Any_data+0x24) [0x5651c2e3aafb]
./a.out(_ZNKSt8functionIFvvEEclEv+0x36) [0x5651c2e3aa7a]
./a.out(main+0x87) [0x5651c2e3a3f7]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f2165e29d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f2165e29e40]

we can see four blank stacks in get_lambda1's stack frames, compared to get_lambda2's.

0

There are 0 best solutions below