paint-brush
How to Create Executable Files and Produce Debug Informationby@mvuksano
449 reads
449 reads

How to Create Executable Files and Produce Debug Information

by Marko VuksanovicNovember 19th, 2020
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

A number of times when I wanted to understand what went wrong with a program but debug information was not available. Without debug information it is nearly impossible to make sense of what happened. In this short article I will show you how to create an executable file as well as produce debug information but in a completely separate file. In a future post, I'll talk more about how to do understand failures in production. To truly understand failure of a system you will need to inspect that system to understand failure.

Company Mentioned

Mention Thumbnail
featured image - How to Create Executable Files and Produce Debug Information
Marko Vuksanovic HackerNoon profile picture

In this short article I will show you how to create an executable file as well as produce debug information but in a completely separate file.

There has been a number of times when I wanted to understand what went wrong with a program but debug information was not available. Without debug information it is nearly impossible to make sense of what happened. This holds true especially for larger programs.

Today we will work with something super simple:

int factorial(int n) {
  if(n==1) {
    return 1;
  }
  else {
    return n * factorial(n-1);
  }
}

int main(int argc, char **argv) {
  int x = factorial(5);
  switch(x) {
    case 120:
      return 0;
    default:
      return -1;
  }

  return -1;
}

Save this into a file called

main.c
.

We can now use c compiler to produce an executable:

gcc -o fact main.c

This gives us a binary file, but unfortunatelly there's no debug information in it. If we run a program like this in production and for whatever reason need to debug this program to understand what's wrong we're out of luck. I understand that some of you may say - "hey, you should never debug programs in production" but there will be situations where that's the only way.

Ideally, programs should be so good by the time they go to production that they don't fail. In a future post, I'll talk more about how to do understand failures in production. For now, you will need to trust me that to truly understand failure of a system you will need to inspect that system.

Now let's compile this program with debug info:

gcc -g -Og -o fact-debug main

Now we can use

eu-readelf 
(or
readelf
) to look at section headers in two binaries . With a bit of
sed
and
readelf
wizardry you will notice that debug version of the binary has a few more sections:
debug_aranges
,
debug_info
,
debug_abbrev
,
debug_line
and
debug_str
.

If you try using

gdb
to step through
fact
binary (one that doesn't include debug info) you will notice that there's no source code that you can use to step through. You can step through the program and understand what's happening from disassembly but it will be hard to make sense. On the other hand, if you use gdb to run fact-debug binary (one that includes debug information) you will notice that you can see source code while stepping through.

Now let's extract debug information from fact-debug binary and put it in another file. That can be done using

obj-copy
and
eu-strip
command:

objcopy --only-keep-debug fact-debug fact-debug-info
eu-strip -g fact-debug

Now you have the best of both worlds - an executable file that does not include debug information as well as access to debug information if the need arises.

As a teaser to my next article, I encourage you to check out

build-id
associated with the executable. It can be retrieved using
eu-readelf -n fact-debug
.

I'll show you how to put it to good use using the latest

gdb
.