Debugging with LLDB

By Lups

Introduction

LLDB is a high-performance CLI debugger, commonly used as an alternative to GDB. LLDB is maintained by Apple and it's an awesome debugger full of functionalities. And today we will go on through some of them.

Starting LLDB

The first thing we need to do after installing lldb is to run it (yeah, you definitely need to run it in order to use it). So, you can simply run lldb by typing lldb on you terminal prompt. It should appear a prompt like

(lldb) #

But it's normally better to initialize the debugger with the binary you want to debug it. In order to do so, you need to simply type lldb a.out, or lldb -- a.out xif you want to run the binary with the argument x. It should return someting like

(lldb) target create "a.out" Current executable set to '/Users/lups/tests/a.out' (arm64). (lldb)

Now that we have our binary loaded, we can now run it.

(lldb) r

Hands-on

In order to show the next examples, I'll be using an exemplary C code, i.e

#include <stdio.h> int addNums(int x, int y); int main(){ int a; int b = 15; scanf("%d", &a); printf("scanned: %d\n", a); printf("sum of a + b: %d\n", addNums(a, b)); return 0; } int addNums(int x, int y) { return x + y; }

In order to add the debugger information to the binary, we must compile it with clang -g inp.c

So, let's first start lldb and run the binary.

➜ tests lldb a.out (lldb) target create "a.out" Current executable set to '/Users/lups/tests/a.out' (arm64). (lldb) r Process 1364 launched: '/Users/lups/tests/a.out' (arm64) 5 scanned: 5 sum of a + b: 20 Process 1364 exited with status = 0 (0x00000000)

So, I've ran the binary and he asked for an input, I inputted it and it ran normally. Let's talk about setting breakpoints.

Breakpoints

let's set a break point on line 7, in order to do so, we must run:

b 5 or b inp.c:7

(lldb) b 5 Breakpoint 2: where = a.out`main + 28 at inp.c:7:6, address = 0x0000000100003ee4 (lldb) b inp.c:7 # yea, you must insert name of src file. Breakpoint 1: where = a.out`main + 28 at inp.c:7:6, address = 0x0000000100003ee4

We can also set a breakpoint under a function, for example, let's set for main.

(lldb) b main Breakpoint 2: where = a.out`main + 28 at inp.c:7:6, address = 0x0000000100003ee4

Managing breakpoints

We can manage the breakpoints we created. Let's first see them (lldb) b # notice you can also use "br list" Current breakpoints:

1: file = 'inp.c', line = 7, exact_match = 0, locations = 1 1.1: where = a.out`main + 28 at inp.c:7:6, address = a.out[0x0000000100003ee4], unresolved, hit count = 0 2: name = 'main', locations = 1 2.1: where = a.out`main + 28 at inp.c:7:6, address = a.out[0x0000000100003ee4], unresolved, hit count = 0

if we want to delete breakpoint number 1, we can do

(lldb) br del 1 1 breakpoints deleted; 0 breakpoint locations disabled.

if we want to delete all breakpoints, we can run

(lldb) br del About to delete all breakpoints, do you want to do that?: [Y/n] y All breakpoints removed. (1 breakpoint)

Handling variables

So, let's set a breakpoint and stop on it.

(lldb) b 8 Breakpoint 4: where = a.out`main + 40 at inp.c:8:2, address = 0x0000000100003ef0 (lldb) run Process 1960 launched: '/Users/lups/tests/a.out' (arm64) Process 1960 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1 frame #0: 0x0000000100003ef0 a.out`main at inp.c:8:2 5 int main(){ 6 int a; 7 int b = 15; -> 8 scanf("%d", &a); 9 printf("scanned: %d\n", a); 10 printf("sum of a + b: %d\n", addNums(a, b)); 11 return 0; Target 0: (a.out) stopped.

So, we stopped right into the scanf function. Let's manage variables a and b. we can print their values by running

(lldb) p a (int) $0 = 753760 (lldb) p b (int) $1 = 15

We can see a has no initial value setted by us, and b has the value of 15. If we want to see everything we need to know about all the variables, we can run

(lldb) frame variable (int) a = 753760 (int) b = 15

Let's change b value to 20.

(lldb) expr b = 20 (int) $2 = 20 (lldb) p b (int) $3 = 20

Now, let's run the next line of the binary using n

(lldb) n 5 Process 2010 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = step over frame #0: 0x0000000100003f00 a.out`main at inp.c:9:26 6 int a; 7 int b = 15; 8 scanf("%d", &a); -> 9 printf("scanned: %d\n", a); 10 printf("sum of a + b: %d\n", addNums(a, b)); 11 return 0; 12 } Target 0: (a.out) stopped.

We can check which line we are by typing

(lldb) f frame #0: 0x0000000100003f00 a.out`main at inp.c:9:26 6 int a; 7 int b = 15; 8 scanf("%d", &a); -> 9 printf("scanned: %d\n", a); 10 printf("sum of a + b: %d\n", addNums(a, b)); 11 return 0; 12 }

So, I moved onto the next line and it asked for me to insert a value for a, so I inserted 5. Let's now print a, and them conclude the binary.

(lldb) p a (int) $4 = 5

We can continue the binary using c

(lldb) c Process 2010 resuming scanned: 5 sum of a + b: 25 Process 2010 exited with status = 0 (0x00000000)

As you could see, a is now equal to 5 and b equal to 20.

If you think your program died into some internal function, you can check it by running bt (backtrace).

(lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = step over * frame #0: 0x0000000100003f00 a.out`main at inp.c:9:26 frame #1: 0x00000001000110f4 dyld`start + 520

Conclusion

I hope you liked the paper and could learn about debugging with LLDB. If you found any error here on this paper or you have any doubt/comment to make, feel free to contact me at: [email protected]