I’m hacking on an assembly project, and wanted to document some of the tricks I was using for figuring out what was going on. This post might seem a little basic for folks who spend all day heads down in gdb or who do this stuff professionally, but I just wanted to share a quick intro to some tools that others may find useful. (oh god, I’m doing it)

If your coming from gdb to lldb, there’s a few differences in commands. LLDB has great documentation on some of the differences. Everything in this post about LLDB is pretty much there.

The bread and butter commands when working with gdb or lldb are:

r (run the program)

s (step in)

n (step over)

finish (step out)

c (continue)

q (quit the program)

You can hit enter if you want to run the last command again, which is really useful if you want to keep stepping over statements repeatedly.

I’ve been using LLDB on OSX. Let’s say I want to debug a program I can build, but is crashing or something:

1 $ sudo lldb ./asmttpd web_root

Setting a breakpoint on jump to label:

1 2 ( lldb ) b sys_write Breakpoint 3: where = asmttpd ` sys_write, address = 0x00000000000029ae

Running the program until breakpoint hit:

1 2 3 4 5 6 7 8 9 10 ( lldb ) r Process 32236 launched: './asmttpd' ( x86_64 ) Process 32236 stopped * thread #1: tid = 0xe69b9, 0x00000000000029ae asmttpd`sys_write, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1 frame #0: 0x00000000000029ae asmttpd`sys_write asmttpd ` sys_write: -> 0x29ae <+0>: pushq %rdi 0x29af <+1>: pushq %rsi 0x29b0 <+2>: pushq %rdx 0x29b1 <+3>: pushq %r10

Seeing more of the current stack frame:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ( lldb ) d asmttpd ` sys_write: -> 0x29ae <+0>: pushq %rdi 0x29af <+1>: pushq %rsi 0x29b0 <+2>: pushq %rdx 0x29b1 <+3>: pushq %r10 0x29b3 <+5>: pushq %r8 0x29b5 <+7>: pushq %r9 0x29b7 <+9>: pushq %rbx 0x29b8 <+10>: pushq %rcx 0x29b9 <+11>: movq %rsi, %rdx 0x29bc <+14>: movq %rdi, %rsi 0x29bf <+17>: movq $0x1 , %rdi 0x29c6 <+24>: movq $0x2000004 , %rax 0x29cd <+31>: syscall 0x29cf <+33>: popq %rcx 0x29d0 <+34>: popq %rbx 0x29d1 <+35>: popq %r9 0x29d3 <+37>: popq %r8 0x29 <+39>: popq %r10 0x29d7 <+41>: popq %rdx 0x29d8 <+42>: popq %rsi 0x29d9 <+43>: popq %rdi 0x29da <+44>: retq

Getting a back trace (call stack):

1 2 3 4 5 6 7 ( lldb ) bt * thread #1: tid = 0xe69b9, 0x00000000000029ae asmttpd`sys_write, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1 * frame #0: 0x00000000000029ae asmttpd`sys_write frame #1: 0x00000000000021b6 asmttpd`print_line + 16 frame #2: 0x0000000000002ab3 asmttpd`start + 35 frame #3: 0x00007fff9900c5ad libdyld.dylib`start + 1 frame #4: 0x00007fff9900c5ad libdyld.dylib`start + 1

peeking at the upper stack frame:

1 2 3 4 5 6 7 ( lldb ) up frame #1: 0x00000000000021b6 asmttpd`print_line + 16 asmttpd ` print_line: 0x21b6 <+16>: movabsq $0x30cb , %rdi 0x21c0 <+26>: movq $0x1 , %rsi 0x21c7 <+33>: callq 0x29ae ; sys_write 0x21cc <+38>: popq %rcx

back down to the breakpoint-halted stack frame:

1 2 3 4 5 6 7 ( lldb ) down frame #0: 0x00000000000029ae asmttpd`sys_write asmttpd ` sys_write: -> 0x29ae <+0>: pushq %rdi 0x29af <+1>: pushq %rsi 0x29b0 <+2>: pushq %rdx 0x29b1 <+3>: pushq %r10

dumping the values of registers:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ( lldb ) register read General Purpose Registers: rax = 0x0000000000002a90 asmttpd ` start rbx = 0x0000000000000000 rcx = 0x00007fff5fbffaf8 rdx = 0x00007fff5fbffa40 rdi = 0x00000000000030cc start_text rsi = 0x000000000000000f rbp = 0x00007fff5fbffa18 rsp = 0x00007fff5fbff9b8 r8 = 0x0000000000000000 r9 = 0x00007fff7b1670c8 atexit_mutex + 24 r10 = 0x00000000ffffffff r11 = 0xffffffff00000000 r12 = 0x0000000000000000 r13 = 0x0000000000000000 r14 = 0x0000000000000000 r15 = 0x0000000000000000 rip = 0x00000000000029ae asmttpd ` sys_write rflags = 0x0000000000000246 cs = 0x000000000000002b fs = 0x0000000000000000 gs = 0x0000000000000000

read just one register:

1 2 ( lldb ) register read rdi rdi = 0x00000000000030cc start_text

When you’re trying to figure out what system calls are made by some C code, using dtruss is very helpful. dtruss is available on OSX and seems to be some kind of wrapper around DTrace.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ cat sleep.c #include <time.h> int main () { struct timespec rqtp = { 2, 0 } ; nanosleep ( &rqtp, NULL ) ; } $ clang sleep.c $ sudo dtruss ./a.out ...all kinds of fun stuff __semwait_signal ( 0xB03, 0x0, 0x1 ) = -1 Err#60

If you compile with -g to emit debug symbols, you can use lldb’s disassemble command to get the equivalent assembly:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 $ clang sleep.c -g $ lldb a.out ( lldb ) target create "a.out" Current executable set to 'a.out' ( x86_64 ) . ( lldb ) b main Breakpoint 1: where = a.out ` main + 16 at sleep.c:3, address = 0x0000000100000f40 ( lldb ) r Process 33213 launched: '/Users/Nicholas/code/assembly/asmttpd/a.out' ( x86_64 ) Process 33213 stopped * thread #1: tid = 0xeca04, 0x0000000100000f40 a.out`main + 16 at sleep.c:3, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x0000000100000f40 a.out`main + 16 at sleep.c:3 1 #include <time.h> 2 int main () { -> 3 struct timespec rqtp = { 4 2, 5 0 6 } ; 7 ( lldb ) disassemble a.out ` main: 0x100000f30 <+0>: pushq %rbp 0x100000f31 <+1>: movq %rsp, %rbp 0x100000f34 <+4>: subq $0x20 , %rsp 0x100000f38 <+8>: leaq -0x10 ( %rbp ) , %rdi 0x100000f3c <+12>: xorl %eax, %eax 0x100000f3e <+14>: movl %eax, %esi -> 0x100000f40 <+16>: movq 0x49 ( %rip ) , %rcx 0x100000f47 <+23>: movq %rcx, -0x10 ( %rbp ) 0x100000f4b <+27>: movq 0x46 ( %rip ) , %rcx 0x100000f52 <+34>: movq %rcx, -0x8 ( %rbp ) 0x100000f56 <+38>: callq 0x100000f68 ; symbol stub for : nanosleep 0x100000f5b <+43>: xorl %edx, %edx 0x100000f5d <+45>: movl %eax, -0x14 ( %rbp ) 0x100000f60 <+48>: movl %edx, %eax 0x100000f62 <+50>: addq $0x20 , %rsp 0x100000f66 <+54>: popq %rbp 0x100000f67 <+55>: retq

Anyways, I’ve been learning some interesting things about OSX that I’ll be sharing soon. If you’d like to learn more about x86-64 assembly programming, you should read my other posts about writing x86-64 and a toy JIT for Brainfuck (the creator of Brainfuck liked it).

I should also do a post on Mozilla’s rr, because it can do amazing things like step backwards. Another day…