[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