Disassembling with no function bounds

In my quest to get thread step-over working on Windows, I’m trying to disassemble some code in LLDB to see what’s there. It won’t let me though, because it doesn’t have function bounds:

(lldb) dis
0xb55040 : pushl %ebp
0xb55041 <main+1>: movl %esp, %ebp
0xb55043 <main+3>: subl $0x14, %esp
0xb55046 <main+6>: leal 0xb50040, %eax
0xb5504c <main+12>: movl $0x0, -0x4(%ebp)
→ 0xb55053 <main+19>: movl %eax, (%esp)
0xb55056 <main+22>: calll 0xb550a1
0xb5505b <main+27>: leal 0xb50050, %ecx
0xb55061 <main+33>: movl %ecx, (%esp)
0xb55064 <main+36>: movl %eax, -0x8(%ebp)
0xb55067 <main+39>: calll 0xb550a1
0xb5506c <main+44>: leal 0xb50060, %ecx
0xb55072 <main+50>: movl %ecx, (%esp)
0xb55075 <main+53>: movl %eax, -0xc(%ebp)
0xb55078 <main+56>: calll 0xb550a1
0xb5507d <main+61>: movl $0x1, %ecx
0xb55082 <main+66>: movl %eax, -0x10(%ebp)
0xb55085 <main+69>: movl %ecx, %eax
0xb55087 <main+71>: addl $0x14, %esp
0xb5508a <main+74>: popl %ebp
0xb5508b <main+75>: retl

(lldb) dis -a 0xb550a1
error: Could not find function bounds for address 0xb550a1

Is there any way to work around this restriction? It seems like it shouldn’t matter what the bounds of the function are, or if there’s even a function at this address at all. As long as there’s code.

You should be able to use a combination of -s start address / -e end
address / -c instruction count.

Perhaps we could disassemble a small number of instructions starting
from the provided address if -a is given an address outside of a
function.

Yeah, the help says:

       -a <address-expression> ( --address <address-expression> )
            Disassemble function containing this address.

so it is doing what is expected. You don't need to specify -c or -e, it will dump some default number of instructions. Having it do something reasonable when there is no function containing that address is not totally unreasonable, except then -a and -s overlap in function, which is a little odd.

Jim

I tried -c and that didn’t work at all (may be a Windows bug which I have to look into).

I guess what I’m wondering is: Why do I have to care about function bounds at all? Here’s some output from my favorite Windows debugger. “u main” means unassemble main, and “u” with no arguments means “keep unassembling from the address where the last unassemble stopped”.

0:000> u main
expr_test_cl!main [d:\testexe\expr_test.cpp @ 23]:
002e1050 55 push ebp
002e1051 8bec mov ebp,esp
002e1053 68883c3000 push offset expr_test_cl!__xt_z+0x144 (00303c88)
002e1058 e8d0000000 call expr_test_cl!printf (002e112d)
002e105d 83c404 add esp,4
002e1060 68983c3000 push offset expr_test_cl!__xt_z+0x154 (00303c98)
002e1065 e8c3000000 call expr_test_cl!printf (002e112d)
002e106a 83c404 add esp,4
0:000> u
expr_test_cl!main+0x1d [d:\testexe\expr_test.cpp @ 26]:
002e106d 68a83c3000 push offset expr_test_cl!__xt_z+0x164 (00303ca8)
002e1072 e8b6000000 call expr_test_cl!printf (002e112d)
002e1077 83c404 add esp,4
002e107a b801000000 mov eax,1
002e107f 5d pop ebp
002e1080 c3 ret
002e1081 cc int 3
002e1082 cc int 3
0:000> u
expr_test_cl!main+0x33:
002e1083 cc int 3
002e1084 cc int 3
002e1085 cc int 3
002e1086 cc int 3
002e1087 cc int 3
002e1088 cc int 3
002e1089 cc int 3
002e108a cc int 3
0:000> u
expr_test_cl!main+0x3b:
002e108b cc int 3
002e108c cc int 3
002e108d cc int 3
002e108e cc int 3
002e108f cc int 3
002e1090 cc int 3
002e1091 cc int 3
002e1092 cc int 3
0:000> u
expr_test_cl!main+0x43:
002e1093 cc int 3
002e1094 cc int 3
expr_test_cl!_get_printf_count_output [f:\dd\vctools\crt\crtw32\stdio\printf.c @ 166]:
002e1095 8b0d00b43000 mov ecx,dword ptr [expr_test_cl!__security_cookie (0030b400)]
002e109b 33c0 xor eax,eax
002e109d 83c901 or ecx,1
002e10a0 390d40c53000 cmp dword ptr [expr_test_cl!__enable_percent_n (0030c540)],ecx
002e10a6 0f94c0 sete al
002e10a9 c3 ret
0:000> u
expr_test_cl!_printf_l [f:\dd\vctools\crt\crtw32\stdio\printf.c @ 79]:
002e10aa 55 push ebp
002e10ab 8bec mov ebp,esp
002e10ad 8d4510 lea eax,[ebp+10h]
002e10b0 50 push eax
002e10b1 ff750c push dword ptr [ebp+0Ch]
002e10b4 ff7508 push dword ptr [ebp+8]
002e10b7 e879060000 call expr_test_cl!_vprintf_l (002e1735)
002e10bc 83c40c add esp,0Ch

Note that there’s no knowledge of function bounds. It just goes. Is it possible to do something like this in LLDB? Or if not, can I implement it?

It looks like I just overlooked -s.

So armed with that knowledge, is there any objection to adding, say, -x, which is more or less “continue disassembling at the last address”?

The way this is supposed to work in lldb is that the "repeat command" that disassemble registers when you run the disassemble command should do "disassemble from the last instruction disassembled". So you should just hit return on the empty line to keep disassembling. It is a general feature that any lldb command can tell the command interpreter what "do me again" - i.e. return on a blank line - should do. Most commands repeat themselves, some commands like "run" don't auto-repeat because that's generally not what you want, and "source list" does the same trick disassemble was SUPPOSED to do.

But with TOT lldb, something seems to have broken that:

(lldb) dis -s 0x0000000100019b07
-> 0x100019b07 <main+55>: movq %rax, %rdi
    0x100019b0a <main+58>: movb $0x0, %al
    0x100019b0c <main+60>: callq 0x10001e28a ; symbol stub for: NSLog
    0x100019b11 <main+65>: leaq 0xa790(%rip), %rcx ; @Sketch.__TEXT.__ustring + 0
    0x100019b18 <main+72>: movb $0x0, -0x101(%rbp)
    0x100019b1f <main+79>: movq %rcx, -0x110(%rbp)
(lldb)
-> 0x100019b07 <main+55>: movq %rax, %rdi
    0x100019b0a <main+58>: movb $0x0, %al
    0x100019b0c <main+60>: callq 0x10001e28a ; symbol stub for: NSLog
    0x100019b11 <main+65>: leaq 0xa790(%rip), %rcx ; @Sketch.__TEXT.__ustring + 0
    0x100019b18 <main+72>: movb $0x0, -0x101(%rbp)
    0x100019b1f <main+79>: movq %rcx, -0x110(%rbp)

Grrr....

Jim

It looks like it's been broken for quite some time: We have an LLDB
snapshot in the FreeBSD base system, and the version on my desktop is
from around r202189. It shows the same behaviour.

Yeah, my guess is the change to make "dis" mean disassemble the current function did it, and that was a while ago. Initially, dis didn't mean anything except as the repeat command.

Jim

This could be r179258 from April 2013 that broke it.

We should definitely get test coverage in place along with a fix to ensure that we’re not surprised by future regressions.

Kate Stone k8stone@apple.com
 Xcode Runtime Analysis Tools