Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What kind of stack unwinding libraries do exist and what's the difference? [closed]

Trying to build my own non-GNU cross-platform C++ environment, I have faced the fact that I don't really understand the basics of the stack unwinding. The environment I build is as follows:

libc++libc++abilibunwind (or some other unwinder).

I've found that libc++abi already contains some kind of libunwind, but doesn't use it on Linux. From the comments I understood, that it's special libunwind: LLVM Stack Unwinder that supports only Darwin and ARM but not x86_64 - and it's confusing. How does it come that the CPU architecture affects the stack unwinding process?

Also I know about following stack unwinders:

  1. glibc built-in.
  2. libc++abi LLVM libunwind.
  3. GNU libunwind (from savanna).

Questions:

  1. How does the platform or CPU architecture affects the stack unwinding process?
  2. Why to have many stack unwinders - and not just a single one?
  3. What kind of unwinders do exist and what is a difference between them?

Expectations from an answer:

I expect to get the answer that covers this topic as whole, not just separate points on each question.

like image 649
abyss.7 Avatar asked Aug 18 '14 05:08

abyss.7


People also ask

What is stack unwinding?

When an exception is thrown and control passes from a try block to a handler, the C++ run time calls destructors for all automatic objects constructed since the beginning of the try block. This process is called stack unwinding. The automatic objects are destroyed in reverse order of their construction.

Which convention is used to stack unwind in caller?

JSC's Calling Convention This convention is used by the interpreter, the baseline JIT and its optimizing compilers DFG and FTL, such that JS-functions emitted by different compilers are able to interoperate with each other. All arguments are passed on the stack, there are also some additional values passed as argument.


1 Answers

Fundamentally, the stack layout is up to the compiler. It can lay out the stack in almost whatever way it thinks is best. The language standard says nothing about how the stack is laid out.

In practice, different compilers lay the stack out differently and the same compiler can also lay it out differently when run with different options. The stack layout will depend on the size of types on the target platform (especially the size of pointer types), compiler options such as GCC's -fomit-frame-pointer, ABI requirements of the platform (eg x64 has a defined ABI where x86 does not). How to interpret the stack will also depend on how the compiler stores the relevant information. This in turn partly depends on the executable format (probably either ELF or COFF these days, but actually, as long as the OS can load the executable and find the entry point, everything else is pretty much up for grabs) and partly on the debug information format - which is again specific to the compiler/debugger combination in use. In the end, it is perfectly possible to write inline assembler that manipulates the stack and program flow in a way that no unwinder will be able to follow. Some compilers also allow you to customise the function prologue and epilogue, giving you another opportunity to confuse any unwinding algorithm.

The net effect of all this is that it is not possible to write a single stack-unwinding algorithm that will work everywhere. The unwinding algorithm has to match the compiler, OS and, for more than the most basic information, the debugger. The best you can do is to write a simple stack-unwinding interface and implement it differently for each compiler/OS/debugger combination you support.

like image 87
Tom Avatar answered Oct 03 '22 20:10

Tom