https://github.com/sga-sc created https://github.com/llvm/llvm-project/pull/210918
In C, an array name in an expression "decays" into a pointer to its first element. LLDB did not honor this: commands like `memory read my_array` did not work correctly, because an aggregate type has no scalar value, so trying to obtain one (ResolveValue/GetValueAsUnsigned) failed. This MR adds explicit array decay: for array-typed expressions, the address of the array object itself is used instead of its (non-existent) scalar value. >From 13836af9cef991065b9006f74a68192450fd20e8 Mon Sep 17 00:00:00 2001 From: Georgiy Samoylov <[email protected]> Date: Tue, 14 Jul 2026 17:52:43 +0300 Subject: [PATCH 1/2] Add decaying of arrays name --- .../source/Interpreter/CommandInterpreter.cpp | 19 ++++++++++++++++++- lldb/source/Interpreter/OptionArgParser.cpp | 15 +++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 9887d24112c20..08de320041153 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -51,6 +51,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Core/Telemetry.h" #include "lldb/Host/StreamFile.h" +#include "lldb/Symbol/CompilerType.h" #include "lldb/Utility/ErrorMessages.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBLog.h" @@ -59,6 +60,7 @@ #include "lldb/Utility/Stream.h" #include "lldb/Utility/StructuredData.h" #include "lldb/Utility/Timer.h" +#include "lldb/ValueObject/ValueObject.h" #include "lldb/Host/Config.h" #include "lldb/lldb-forward.h" @@ -2001,7 +2003,22 @@ Status CommandInterpreter::PreprocessToken(std::string &expr_str) { expr_result_valobj_sp = expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable( expr_result_valobj_sp->GetDynamicValueType(), true); - if (expr_result_valobj_sp->ResolveValue(scalar)) { + // For array-typed results, C decays the array to a pointer to its first + // element. ResolveValue() can't produce a scalar for an aggregate, and the + // expression evaluator materializes arrays into a temporary result buffer + // whose address is not the array's real location. So decay explicitly here: + // use the address of the array object instead of its (non-existent) scalar + // value. + if (expr_result_valobj_sp && + expr_result_valobj_sp->GetCompilerType().IsArrayType()) { + lldb::addr_t addr = + expr_result_valobj_sp->GetAddressOf(/*scalar_is_load_address=*/true) + .address; + if (addr != LLDB_INVALID_ADDRESS) + scalar = addr; + } + + if (scalar.IsValid() || expr_result_valobj_sp->ResolveValue(scalar)) { StreamString value_strm; const bool show_type = false; diff --git a/lldb/source/Interpreter/OptionArgParser.cpp b/lldb/source/Interpreter/OptionArgParser.cpp index 170f65ad80a74..bdbf9859faa09 100644 --- a/lldb/source/Interpreter/OptionArgParser.cpp +++ b/lldb/source/Interpreter/OptionArgParser.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Symbol/CompilerType.h" #include "lldb/Target/ABI.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" @@ -231,8 +232,18 @@ OptionArgParser::DoToAddress(const ExecutionContext *exe_ctx, llvm::StringRef s, valobj_sp = valobj_sp->GetQualifiedRepresentationIfAvailable( valobj_sp->GetDynamicValueType(), true); // Get the address to watch. - if (valobj_sp) - addr = valobj_sp->GetValueAsUnsigned(0, &success); + if (valobj_sp) { + // In C an array decays to a pointer to its first element, whose value is + // the address of the array object itself. An aggregate has no scalar + // value, so GetValueAsUnsigned() would fail here; use the array's own + // load address instead. + if (valobj_sp->GetCompilerType().IsArrayType()) { + addr = valobj_sp->GetAddressOf(/*scalar_is_load_address=*/true).address; + success = addr != LLDB_INVALID_ADDRESS; + } else { + addr = valobj_sp->GetValueAsUnsigned(0, &success); + } + } if (success) { if (error_ptr) error_ptr->Clear(); >From dcc983b68dbd5cc135c705bf2186befdd320d208 Mon Sep 17 00:00:00 2001 From: Georgiy Samoylov <[email protected]> Date: Tue, 14 Jul 2026 17:42:43 +0300 Subject: [PATCH 2/2] Add case of decaying to TestMemoryRead.py --- .../commands/memory/read/TestMemoryRead.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lldb/test/API/commands/memory/read/TestMemoryRead.py b/lldb/test/API/commands/memory/read/TestMemoryRead.py index bc8c45a402423..2a3268f38e600 100644 --- a/lldb/test/API/commands/memory/read/TestMemoryRead.py +++ b/lldb/test/API/commands/memory/read/TestMemoryRead.py @@ -144,6 +144,44 @@ def test_memory_read(self): self.assertEqual(len(o), expected_object_length) self.assertEqual(len(objects_read), 4) + def test_memory_read_array_name_decays_to_address(self): + """An array expression decays to a pointer to its first element, so + `memory read <array>` and `memory read `<array>`` must behave like + `memory read &<array>`, rather than failing or reading a bogus address.""" + self.build_run_stop() + + fmt = "memory read --format uint32_t[] --size 4 --count 3 " + + def read_addr_and_values(suffix): + self.runCmd(fmt + suffix) + lines = self.res.GetOutput().splitlines() + addr = int(lines[0].split(":")[0], 0) + values = [] + for line in lines: + values.extend(v.strip(" {}") for v in line.split(":")[1].split()) + return addr, values + + # Taking the address explicitly yields a pointer whose value is the + # array's address; this is the reference every other form must match. + ref_addr, ref_values = read_addr_and_values("&my_ints") + self.assertEqual( + ref_values, ["0x00000002", "0x00000004", "0x00000006"] + ) + + # Every one of these forms must resolve to the same address and data: + # &my_ints pointer, via OptionArgParser::ToAddress + # `&my_ints` pointer, via CommandInterpreter::PreprocessToken + # my_ints array decay, via OptionArgParser::ToAddress + # `my_ints` array decay, via CommandInterpreter::PreprocessToken + for suffix in ["&my_ints", "`&my_ints`", "my_ints", "`my_ints`"]: + addr, values = read_addr_and_values(suffix) + self.assertEqual( + addr, ref_addr, "wrong address for 'memory read %s'" % suffix + ) + self.assertEqual( + values, ref_values, "wrong data for 'memory read %s'" % suffix + ) + def test_memory_read_file(self): self.build_run_stop() res = lldb.SBCommandReturnObject() _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
