Sunday, September 4, 2011

Unwind A Program's Stack

Memories

Recalling my fond days in taking CS101 more than a decade ago, one of the critical concepts i had to learn was to understand how to debug a program. As time went on, i became interested in compilers and the technologies used and took up Compiler Writing course. What a journey, i'll do it all over again and this time i'll do better ;)

In this post, i wanted to share with you how you can really unwind the program stack using a C library, libunwind. Really cool stuff - wish i knew this earlier. Anyways, here goes...

Question Why do i ever need to unwind the program stack programmatically?

Answer This shouldn't come as a surprise since in many programming languages, you would have added a catch exception clause that basically is unwinding the stack (when it encounters an error) but what this library does for you is that you can unwind the stack under more favourable circumstances other than receiving an exception ;)


libunwind - is exactly the library you need to build this sort of support into your programs. Imagine how cool it'll be if your friends can debug your program other than GDB - they'll love you!

Building libunwind & issues

Download libunwind or clone it thru Git.

When you first build/install the software, you'll likely encounter two problems

/usr/include/bits/setjmp2.h:26:13: error: ‘longjmp’ aliased to undefined symbol ‘_longjmp’

libtool: install: (cd /home/tayboonl/libunwind/src; /bin/bash /home/tayboonl/libunwind/libtool  --tag CC --mode=relink gcc -U_FORTIFY_SOURCE -fexceptions -Wall -Wsign-compare -XCClinker -nostartfiles -version-info 0:0:0 -o libunwind-setjmp.la -rpath /home/tayboonl/ray-opt/lib setjmp/longjmp.lo setjmp/siglongjmp.lo x86_64/longjmp.lo x86_64/siglongjmp.lo libunwind-elf64.la libunwind-x86_64.la libunwind.la -lc )
mv: cannot stat `libunwind-setjmp.so.0.0.0': No such file or directory
libtool: install: error: relink `libunwind-setjmp.la' with the above command before installing it
make[3]: *** [install-libLTLIBRARIES] Error 1

At first i was stumped, then after browsing through the net, i've discovered that the main cause was because the default optimization level applied (-O2) actually triggers the GCC on the Ubuntu 11 to conduct security checks and it thinks that libunwind has flouted the rules. Admittedly, i'm not too concern at this point in time over security since what i wanted to do is implement this library ASAP and use it. Using the Ubuntu's wiki on compiler flags, circumventing it was to apply the -U_FORTIFY_SOURCE (which stands for unknown fortify source) to the build.

One thing you'll immediately notice is that the optimization level (-O2) is no longer applied. Just be aware of this.

Second thing i've discovered is that a critical library cannot be linked and the hack i've provided below on LDFLAGS will mitigate the shared library creation error.


./configure CFLAGS=-U_FORTIFY_SOURCE LDFLAGS=-L<full pathname>/libunwind/src/.libs --prefix=<prefix directory>

Last thing is that building the program requires that config.h header file be present. Then i realize that this file has been relieved from the latest Ubuntu 11 kernel and then checking through a couple of forum posts, blogs i realized that it could be hacked by simply creating an empty file i.e. touch /usr/include/config.h - reason for double checking this is because config.h has been used in various linux releases to define variables (i guess that's why its called a configuration header file) and building any simple program that uses the library is something like this


gcc (prog name.c) -I(install dir of libunwind) -L(location of libraries of libunwind) -lunwind -o (prog name)

A note of caution here is that you really shouldn't leave the config.h file you just created lying around like that.

Here's a simple program that shows you how to create a show backtrace command in C.

#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <stdio.h>

void show_backtrace(void) {
	unw_cursor_t cursor; unw_context_t uc;
	unw_word_t ip, sp;

	unw_getcontext(&uc);
	unw_init_local(&cursor, &uc);
	while( unw_step(&cursor) > 0) {
		unw_get_reg(&cursor, UNW_REG_IP, &ip);
		unw_get_reg(&cursor, UNW_REG_SP, &sp);
		printf("ip = %lx, sp = %lx\n", (long) ip, (long) sp);
	}
}

void simple_func_call_no_args() {
	int i, j;
	print("value of i=%d, j=%d\n", i, j);
	show_backtrace();
}

int main(int argc, char** argv) {
	simple_func_call_no_args();
	return 0;
}


0 comments: