https://github.com/JDevlieghere created 
https://github.com/llvm/llvm-project/pull/205913

Reading a global or static variable on a Wasm target produced a wrong value (or 
none at all). Two Wasm-only bugs combined to break it, both of which need to be 
fixed to support `target variable` / `frame var`.

1. DWARFExpression::Evaluate special-cased DW_OP_addr and 
DW_OP_addrx/DW_OP_GNU_addr_index on Wasm to push a LoadAddress, based on the 
theory that "Wasm file sections aren't mapped into memory". But a DW_OP_addr 
operand denotes a location in the module's address space, i.e. a file address 
like on every other target. Forcing a load address breaks the static 
(no-process) read path, since a file section cannot be read as a load address.

2. ObjectFileWasm::SetLoadAddress mapped every section with `load_address | 
GetFileOffset()`. For an active data segment that Object-tags the address (top 
bit = code space) and uses the file offset instead of the segment's 
linear-memory address, so a live read of a data global resolved to a garbage 
address in the wrong space.

Address (1) by dropping the incorrect special casing. Address (2) by mapping 
data sections into the Memory space at their linear VM address while preserving 
the module id. Code and other sections keep their Object-space module-offset 
addressing.

>From 978dadc0f6df559586d58cf5779c5ef7f96f8be3 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <[email protected]>
Date: Thu, 25 Jun 2026 13:44:11 -0700
Subject: [PATCH] [lldb] Fix reading global variables on WebAssembly

Reading a global or static variable on a Wasm target produced a wrong
value (or none at all). Two Wasm-only bugs combined to break it, both of
which need to be fixed to support `target variable` / `frame var`.

1. DWARFExpression::Evaluate special-cased DW_OP_addr and
   DW_OP_addrx/DW_OP_GNU_addr_index on Wasm to push a LoadAddress, based on
   the theory that "Wasm file sections aren't mapped into memory". But a
   DW_OP_addr operand denotes a location in the module's address space,
   i.e. a file address like on every other target. Forcing a load
   address breaks the static (no-process) read path, since a file
   section cannot be read as a load address.

2. ObjectFileWasm::SetLoadAddress mapped every section with
   `load_address | GetFileOffset()`. For an active data segment that
   Object-tags the address (top bit = code space) and uses the file
   offset instead of the segment's linear-memory address, so a live read
   of a data global resolved to a garbage address in the wrong space.

Address (1) by dropping the incorrect special casing. Address (2) by
mapping data sections into the Memory space at their linear VM address
while preserving the module id. Code and other sections keep their
Object-space module-offset addressing.
---
 lldb/source/Expression/DWARFExpression.cpp    | 18 +----
 .../ObjectFile/wasm/ObjectFileWasm.cpp        | 18 ++++-
 .../wasm/wasm-data-section-load-address.yaml  | 66 +++++++++++++++++++
 .../Expression/DWARFExpressionTest.cpp        |  9 +--
 4 files changed, 88 insertions(+), 23 deletions(-)
 create mode 100644 
lldb/test/Shell/ObjectFile/wasm/wasm-data-section-load-address.yaml

diff --git a/lldb/source/Expression/DWARFExpression.cpp 
b/lldb/source/Expression/DWARFExpression.cpp
index 7966673bb65ed..66f33971fd220 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -1359,14 +1359,7 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
     switch (opcode) {
     case DW_OP_addr:
       stack.push_back(Scalar(op->getRawOperand(0)));
-      if (eval_ctx.target && eval_ctx.target->GetArchitecture().GetCore() ==
-                                 ArchSpec::eCore_wasm32) {
-        // wasm file sections aren't mapped into memory, therefore addresses 
can
-        // never point into a file section and are always LoadAddresses.
-        stack.back().SetValueType(Value::ValueType::LoadAddress);
-      } else {
-        stack.back().SetValueType(Value::ValueType::FileAddress);
-      }
+      stack.back().SetValueType(Value::ValueType::FileAddress);
       break;
 
     case DW_OP_deref: {
@@ -1902,14 +1895,7 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
       lldb::addr_t value =
           eval_ctx.dwarf_cu->ReadAddressFromDebugAddrSection(index);
       stack.push_back(Scalar(value));
-      if (eval_ctx.target && eval_ctx.target->GetArchitecture().GetCore() ==
-                                 ArchSpec::eCore_wasm32) {
-        // wasm file sections aren't mapped into memory, therefore addresses 
can
-        // never point into a file section and are always LoadAddresses.
-        stack.back().SetValueType(Value::ValueType::LoadAddress);
-      } else {
-        stack.back().SetValueType(Value::ValueType::FileAddress);
-      }
+      stack.back().SetValueType(Value::ValueType::FileAddress);
     } break;
 
     case DW_OP_GNU_const_index: {
diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp 
b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
index 205e549b66a22..9895044cd29bc 100644
--- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
+++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
@@ -803,10 +803,22 @@ bool ObjectFileWasm::SetLoadAddress(Target &target, 
lldb::addr_t load_address,
   const size_t num_sections = section_list->GetSize();
   for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
     SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
-    if (target.SetSectionLoadAddress(
-            section_sp, load_address | section_sp->GetFileOffset())) {
-      ++num_loaded_sections;
+    lldb::addr_t section_load_addr;
+    if (section_sp->GetType() == eSectionTypeData) {
+      // Active data segments are mapped into the module's linear memory at
+      // their virtual address. Linear memory is a separate address space from
+      // code (the top two bits of the 64-bit address encode the space: 0 for
+      // Memory, 1 for Object/code), so place the segment at its linear VM
+      // address in the Memory space while preserving the module id.
+      section_load_addr = (load_address & ~(uint64_t(0b11) << 62)) |
+                          section_sp->GetFileAddress();
+    } else {
+      // Code (and other) sections are addressed by their offset within the
+      // module in the Object address space.
+      section_load_addr = load_address | section_sp->GetFileOffset();
     }
+    if (target.SetSectionLoadAddress(section_sp, section_load_addr))
+      ++num_loaded_sections;
   }
 
   return num_loaded_sections > 0;
diff --git 
a/lldb/test/Shell/ObjectFile/wasm/wasm-data-section-load-address.yaml 
b/lldb/test/Shell/ObjectFile/wasm/wasm-data-section-load-address.yaml
new file mode 100644
index 0000000000000..0681344de2cb7
--- /dev/null
+++ b/lldb/test/Shell/ObjectFile/wasm/wasm-data-section-load-address.yaml
@@ -0,0 +1,66 @@
+# REQUIRES: webassembly
+
+# An active data segment is mapped into the module's linear memory at the
+# virtual address given by its init expression. Linear memory is a separate
+# address space from code: a wasm 64-bit address encodes the space in its top
+# two bits (0 = Memory, 1 = Object/code). Code is addressed in the Object space
+# by its module offset; data must be addressed in the Memory space at its
+# linear address.
+#
+# Here the data segment's init expr places it at linear address 0x10000.
+
+# RUN: yaml2obj %s -o %t.wasm
+# RUN: %lldb %t.wasm \
+# RUN:   -o "target modules dump sections" \
+# RUN:   -o "target modules load --file %t.wasm --slide 0x4000000000000000" \
+# RUN:   -o "target modules dump sections" \
+# RUN:   -o exit 2>&1 | FileCheck %s
+
+# Before loading, sections carry their file addresses. The data segment's file
+# address is its linear-memory address (from the init expr), not its offset in
+# the wasm file.
+# CHECK-LABEL: File Address
+# CHECK:       code  {{.*}}[0x0000000000000000-
+# CHECK:       data  {{.*}}[0x0000000000010000-0x0000000000010004)
+
+# After loading the module in the Object space (top bit set), the code section
+# is placed in the Object space at its module offset, while the data segment is
+# placed in the Memory space at its linear address. It is *not* Object-tagged.
+# CHECK-LABEL: Load Address
+# CHECK:       code  {{.*}}[0x40000000{{[0-9a-f]+}}-
+# CHECK:       data  {{.*}}[0x0000000000010000-0x0000000000010004)
+
+--- !WASM
+FileHeader:
+  Version: 0x1
+Sections:
+  - Type: TYPE
+    Signatures:
+      - Index: 0
+        ParamTypes: []
+        ReturnTypes:
+          - I32
+  - Type: FUNCTION
+    FunctionTypes: [ 0 ]
+  - Type: MEMORY
+    Memories:
+      - Minimum: 0x2
+  - Type: CODE
+    Functions:
+      - Index: 0
+        Locals: []
+        Body: 412A0F0B
+  - Type: DATA
+    Segments:
+      - SectionOffset:   6
+        InitFlags:       0
+        Offset:
+          Opcode:          I32_CONST
+          Value:           65536
+        Content:          'DEADBEEF'
+  - Type: CUSTOM
+    Name: name
+    FunctionNames:
+      - Index: 0
+        Name: get_42
+...
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp 
b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index 7feacb2b9da24..130f2090a6865 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -1179,10 +1179,11 @@ TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr) 
{
   ASSERT_TRUE(CreateTestContext(&test_ctx, "wasm32-unknown-unknown-wasm"));
 
   ExecutionContext exe_ctx(test_ctx.target_sp, false);
-  // DW_OP_addr takes a single operand of address size width:
+  // DW_OP_addr takes a single operand of address size width. It denotes a
+  // location in the module's address space, i.e. a file address.
   EXPECT_THAT_EXPECTED(
       Evaluate({DW_OP_addr, 0x40, 0x0, 0x0, 0x0}, {}, {}, &exe_ctx),
-      ExpectLoadAddress(0x40));
+      ExpectFileAddress(0x40));
 }
 
 TEST_F(DWARFExpressionMockProcessTest, WASM_DW_OP_addr_index) {
@@ -1255,11 +1256,11 @@ TEST_F(DWARFExpressionMockProcessTest, 
WASM_DW_OP_addr_index) {
   DWARFExpression expr(extractor);
 
   llvm::Expected<Value> result = evaluate(expr);
-  EXPECT_THAT_EXPECTED(result, ExpectLoadAddress(0x5678u));
+  EXPECT_THAT_EXPECTED(result, ExpectFileAddress(0x5678u));
 
   ASSERT_TRUE(expr.Update_DW_OP_addr(dwarf_cu, 0xdeadbeef));
   result = evaluate(expr);
-  EXPECT_THAT_EXPECTED(result, ExpectLoadAddress(0xdeadbeefu));
+  EXPECT_THAT_EXPECTED(result, ExpectFileAddress(0xdeadbeefu));
 }
 
 class CustomSymbolFileDWARF : public SymbolFileDWARF {

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to