[Lldb-commits] [lldb] [lldb] Skip local variable declarations at start of Wasm function (PR #190093)
Maarten Steevens via lldb-commits
lldb-commits at lists.llvm.org
Thu Apr 2 01:50:39 PDT 2026
MaartenS11 wrote:
> For just disassembly it appears to work (although the addresses are shifted):
>
> ```
> llvm-project/build/bin/lldb test-dbg.wasm
> (lldb) target create "test-dbg.wasm"
> Current executable set to '/Users/maarten/Projects/tinygo-warduino-test/tmp/test-dbg.wasm' (wasm32).
> (lldb) disassemble -n _main
> test-dbg.wasm`_main:
> test-dbg.wasm[0x81] <+3>: global.get 0
> test-dbg.wasm[0x87] <+9>: i32.const 16
> test-dbg.wasm[0x89] <+11>: i32.sub
> test-dbg.wasm[0x8a] <+12>: local.set 0
> test-dbg.wasm[0x8c] <+14>: local.get 0
> test-dbg.wasm[0x8e] <+16>: global.set 0
> test-dbg.wasm[0x94] <+22>: local.get 0
> test-dbg.wasm[0x96] <+24>: i32.const 0
> test-dbg.wasm[0x98] <+26>: i32.store 12
> test-dbg.wasm[0x9b] <+29>: block
> test-dbg.wasm[0x9d] <+31>: loop ; label2:
> test-dbg.wasm[0x9f] <+33>: local.get 0
> test-dbg.wasm[0xa1] <+35>: i32.load 12
> test-dbg.wasm[0xa4] <+38>: i32.const 50
> test-dbg.wasm[0xa6] <+40>: i32.lt_s
> test-dbg.wasm[0xa7] <+41>: i32.const 1
> test-dbg.wasm[0xa9] <+43>: i32.and
> test-dbg.wasm[0xaa] <+44>: i32.eqz
> test-dbg.wasm[0xab] <+45>: br_if 1 ; 1: up to label2
> test-dbg.wasm[0xad] <+47>: local.get 0
> test-dbg.wasm[0xaf] <+49>: i32.load 12
> test-dbg.wasm[0xb2] <+52>: call 0
> test-dbg.wasm[0xb8] <+58>: local.get 0
> test-dbg.wasm[0xba] <+60>: i32.load 12
> test-dbg.wasm[0xbd] <+63>: call 1
> test-dbg.wasm[0xc3] <+69>: call 0
> test-dbg.wasm[0xc9] <+75>: local.get 0
> test-dbg.wasm[0xcb] <+77>: local.get 0
> test-dbg.wasm[0xcd] <+79>: i32.load 12
> test-dbg.wasm[0xd0] <+82>: i32.const 1
> test-dbg.wasm[0xd2] <+84>: i32.add
> test-dbg.wasm[0xd3] <+85>: i32.store 12
> test-dbg.wasm[0xd6] <+88>: br 0 ; 0: up to label3
> test-dbg.wasm[0xd8] <+90>: end
> test-dbg.wasm[0xd9] <+91>: end
> test-dbg.wasm[0xda] <+92>: local.get 0
> test-dbg.wasm[0xdc] <+94>: i32.const 16
> test-dbg.wasm[0xde] <+96>: i32.add
> test-dbg.wasm[0xdf] <+97>: global.set 0
> test-dbg.wasm[0xe5] <+103>: return
> test-dbg.wasm[0xe6] <+104>: end
> (lldb)
> ```
>
> Which matches with the instructions seen in objdump:
>
> ```
> 0000e3 func[2] <_main>:
> 0000e4: 01 7f | local[0] type=i32
> 0000e6: 23 80 80 80 80 00 | global.get 0 <__stack_pointer>
> 0000ec: 41 10 | i32.const 16
> 0000ee: 6b | i32.sub
> 0000ef: 21 00 | local.set 0
> 0000f1: 20 00 | local.get 0
> 0000f3: 24 80 80 80 80 00 | global.set 0 <__stack_pointer>
> 0000f9: 20 00 | local.get 0
> 0000fb: 41 00 | i32.const 0
> 0000fd: 36 02 0c | i32.store 2 12
> 000100: 02 40 | block
> 000102: 03 40 | loop
> 000104: 20 00 | local.get 0
> 000106: 28 02 0c | i32.load 2 12
> 000109: 41 32 | i32.const 50
> 00010b: 48 | i32.lt_s
> 00010c: 41 01 | i32.const 1
> 00010e: 71 | i32.and
> 00010f: 45 | i32.eqz
> 000110: 0d 01 | br_if 1
> 000112: 20 00 | local.get 0
> 000114: 28 02 0c | i32.load 2 12
> 000117: 10 80 80 80 80 00 | call 0 <print_int>
> 00011d: 20 00 | local.get 0
> 00011f: 28 02 0c | i32.load 2 12
> 000122: 10 81 80 80 80 00 | call 1 <fib>
> 000128: 10 80 80 80 80 00 | call 0 <print_int>
> 00012e: 20 00 | local.get 0
> 000130: 20 00 | local.get 0
> 000132: 28 02 0c | i32.load 2 12
> 000135: 41 01 | i32.const 1
> 000137: 6a | i32.add
> 000138: 36 02 0c | i32.store 2 12
> 00013b: 0c 00 | br 0
> 00013d: 0b | end
> 00013e: 0b | end
> 00013f: 20 00 | local.get 0
> 000141: 41 10 | i32.const 16
> 000143: 6a | i32.add
> 000144: 24 80 80 80 80 00 | global.set 0 <__stack_pointer>
> 00014a: 0f | return
> 00014b: 0b | end
> ```
>
> On the other hand the moment I start debugging, the instructions suddenly look a bit strange:
>
> ```
> (lldb) process connect --plugin wasm connect://localhost:1234
> Process 1 stopped
> * thread #1, stop reason = signal SIGTRAP
> frame #0: 0x000000e6 test-dbg.wasm`_main at test-dbg.c:16
> 13 }
> 14
> 15 __attribute__((export_name("main")))
> -> 16 void _main() {
> 17 for (int i = 0; i < 50; i++) {
> 18 print_int(i);
> 19 print_int(fib(i));
> (lldb) disassemble -n _main
> test-dbg.wasm`_main:
> -> 0xe6 <+3>: end
> 0xe7 <+4>: loop unknown_type ; label0:
> 0xe9 <+6>: return
> 0xea <+7>: end
> ```
>
> It seems that the addresses are now shifted regardless of when I'm debugging or not. At `0xe6` there is an `end` according to lldb (see above). But according to objdump it's supposed to be the first instruction of the function `global.get 0`.
>
> Looking at `disassemble -s 0x81 -c 20`, with `0x81` being the address I got from the first disassembly when not debugging I get the expected instructions. So the shift to skip the locals appears to work but the adresses are all shifted by a certain amount.
>
> ```
> (lldb) disassemble -s 0x81 -c 20
> test-dbg.wasm`fib:
> 0x81 <+26>: global.get 0
> 0x87 <+32>: i32.const 16
> 0x89 <+34>: i32.sub
> 0x8a <+35>: local.set 0
> 0x8c <+37>: local.get 0
> 0x8e <+39>: global.set 0
> 0x94 <+45>: local.get 0
> 0x96 <+47>: i32.const 0
> 0x98 <+49>: i32.store 12
> 0x9b <+52>: block
> 0x9d <+54>: loop ; label2:
> 0x9f <+56>: local.get 0
> 0xa1 <+58>: i32.load 12
> 0xa4 <+61>: i32.const 50
> 0xa6 <+63>: i32.lt_s
> 0xa7 <+64>: i32.const 1
> 0xa9 <+66>: i32.and
> 0xaa <+67>: i32.eqz
> 0xab <+68>: br_if 1 ; 1: up to label2
> 0xad <+70>: local.get 0
> ```
I think this is actually my fault, LLDB is using an offset of 0 which is what my stub said it should use, but my stub appears to be using addresses relative to the start of the module so offset by the start of the code section. This previously was not an issue but now that the disassembly is actually correct my mistake is clear.
https://github.com/llvm/llvm-project/pull/190093
More information about the lldb-commits
mailing list