Skip to content

Commit 0225308

Browse files
authored
Merge pull request #8374 from jimingham/valobj-cast-swift
Make ValueObject::Cast work for casts from smaller to larger structs …
2 parents 999064a + eb078e0 commit 0225308

File tree

3 files changed

+97
-18
lines changed

3 files changed

+97
-18
lines changed

lldb/source/Core/ValueObject.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,18 +2855,30 @@ ValueObjectSP ValueObject::DoCast(const CompilerType &compiler_type) {
28552855

28562856
ValueObjectSP ValueObject::Cast(const CompilerType &compiler_type) {
28572857
// Only allow casts if the original type is equal or larger than the cast
2858-
// type. We don't know how to fetch more data for all the ConstResult types,
2859-
// so we can't guarantee this will work:
2858+
// type, unless we know this is a load address. Getting the size wrong for
2859+
// a host side storage could leak lldb memory, so we absolutely want to
2860+
// prevent that. We may not always get the right value, for instance if we
2861+
// have an expression result value that's copied into a storage location in
2862+
// the target may not have copied enough memory. I'm not trying to fix that
2863+
// here, I'm just making Cast from a smaller to a larger possible in all the
2864+
// cases where that doesn't risk making a Value out of random lldb memory.
2865+
// You have to check the ValueObject's Value for the address types, since
2866+
// ValueObjects that use live addresses will tell you they fetch data from the
2867+
// live address, but once they are made, they actually don't.
2868+
// FIXME: Can we make ValueObject's with a live address fetch "more data" from
2869+
// the live address if it is still valid?
2870+
28602871
Status error;
28612872
CompilerType my_type = GetCompilerType();
28622873

28632874
ExecutionContextScope *exe_scope
28642875
= ExecutionContext(GetExecutionContextRef())
28652876
.GetBestExecutionContextScope();
2866-
if (compiler_type.GetByteSize(exe_scope)
2867-
<= GetCompilerType().GetByteSize(exe_scope)) {
2877+
if (compiler_type.GetByteSize(exe_scope)
2878+
<= GetCompilerType().GetByteSize(exe_scope)
2879+
|| m_value.GetValueType() == Value::ValueType::LoadAddress)
28682880
return DoCast(compiler_type);
2869-
}
2881+
28702882
error.SetErrorString("Can only cast to a type that is equal to or smaller "
28712883
"than the orignal type.");
28722884

lldb/test/API/python_api/value/TestValueAPI.py

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,72 @@ def test(self):
148148

149149
# Test some other cases of the Cast API. We allow casts from one struct type
150150
# to another, which is a little weird, but we don't support casting from a
151-
# smaller type to a larger as we often wouldn't know how to get the extra data:
152-
val_f = target.EvaluateExpression("f")
153-
bad_cast = val_s.Cast(val_f.GetType())
154-
self.assertFailure(bad_cast.GetError(),
155-
"Can only cast to a type that is equal to or smaller than the orignal type.")
156-
weird_cast = val_f.Cast(val_s.GetType())
157-
self.assertSuccess(weird_cast.GetError(),
158-
"Can cast from a larger to a smaller")
159-
self.assertEqual(weird_cast.GetChildMemberWithName("a").GetValueAsSigned(0), 33,
160-
"Got the right value")
151+
# smaller type to a larger when the underlying data is not in the inferior,
152+
# since then we have no way to fetch the out-of-bounds values.
153+
# For an expression that references a variable, or a FindVariable result,
154+
# or an SBValue made from an address and a type, we can get back to the target,
155+
# so those will work. Make sure they do and get the right extra values as well.
156+
157+
# We're casting everything to the type of "f", so get that first:
158+
f_var = frame0.FindVariable("f")
159+
self.assertSuccess(f_var.error, "Got f")
160+
bigger_type = f_var.GetType()
161+
162+
# First try a value that we got from FindVariable
163+
container = frame0.FindVariable("my_container")
164+
self.assertSuccess(container.error, "Found my_container")
165+
fv_small = container.GetValueForExpressionPath(".data.small")
166+
self.assertSuccess(fv_small.error, "Found small in my_container")
167+
fv_cast = fv_small.Cast(bigger_type)
168+
self.assertSuccess(fv_cast.error, "Can cast up from FindVariable")
169+
child_checks = [
170+
ValueCheck(name="a", value="33", type="int"),
171+
ValueCheck(name="b", value="44", type="int"),
172+
ValueCheck(name="c", value="55", type="int"),
173+
]
174+
cast_check = ValueCheck(type=bigger_type.name, children=child_checks)
175+
176+
# Now try one we made with expr. This one should fail, because expr
177+
# stores the "canonical value" in host memory, and doesn't know how
178+
# to augment that from the live address.
179+
expr_cont = frame0.EvaluateExpression("my_container")
180+
self.assertSuccess(expr_cont.error, "Got my_container by expr")
181+
expr_small = expr_cont.GetValueForExpressionPath(".data.small")
182+
self.assertSuccess(expr_small.error, "Got small by expr")
183+
expr_cast = expr_small.Cast(bigger_type)
184+
self.assertFailure(expr_cast.error, msg="Cannot cast expr result")
185+
186+
# Now try one we made with CreateValueFromAddress. That will succeed
187+
# because this directly tracks the inferior memory.
188+
small_addr = fv_small.addr
189+
self.assertTrue(small_addr.IsValid())
190+
small_type = fv_small.GetType()
191+
vfa_small = target.CreateValueFromAddress(
192+
"small_from_addr", small_addr, small_type
193+
)
194+
self.assertSuccess(vfa_small.error, "Made small from address")
195+
vfa_cast = vfa_small.Cast(bigger_type)
196+
self.assertSuccess(vfa_cast.error, "Made a cast from vfa_small")
197+
cast_check.check_value(self, vfa_cast, "Cast of ValueFromAddress succeeds")
198+
199+
# Next try ValueObject created from data. They should fail as there's no
200+
# way to grow the data:
201+
data_small = target.CreateValueFromData(
202+
"small_from_data", fv_small.data, fv_small.type
203+
)
204+
self.assertSuccess(data_small.error, "Made a valid object from data")
205+
data_cast = data_small.Cast(bigger_type)
206+
self.assertFailure(data_cast.error, msg="Cannot cast data backed SBValue")
207+
208+
# Now check casting from a larger type to a smaller, we can always do this,
209+
# so just test one case:
210+
weird_cast = f_var.Cast(val_s.GetType())
211+
self.assertSuccess(weird_cast.GetError(), "Can cast from a larger to a smaller")
212+
self.assertEqual(
213+
weird_cast.GetChildMemberWithName("a").GetValueAsSigned(0),
214+
33,
215+
"Got the right value",
216+
)
161217

162218
# Check that lldb.value implements truth testing.
163219
self.assertFalse(lldb.value(frame0.FindVariable("bogus")))

lldb/test/API/python_api/value/main.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const char *weekdays[5] = { "Monday",
2222
const char **g_table[2] = { days_of_week, weekdays };
2323

2424
typedef int MyInt;
25-
25+
2626
struct MyStruct
2727
{
2828
int a;
@@ -36,15 +36,26 @@ struct MyBiggerStruct
3636
int c;
3737
};
3838

39+
struct Container
40+
{
41+
int discriminator;
42+
union Data {
43+
struct MyStruct small;
44+
struct MyBiggerStruct big;
45+
} data;
46+
};
47+
3948
int main (int argc, char const *argv[])
4049
{
4150
uint32_t uinthex = 0xE0A35F10;
4251
int32_t sinthex = 0xE0A35F10;
4352

4453
int i;
4554
MyInt a = 12345;
46-
struct MyStruct s = { 11, 22 };
47-
struct MyBiggerStruct f = { 33, 44, 55 };
55+
struct MyStruct s = {11, 22};
56+
struct MyBiggerStruct f = { 33, 44, 55 };
57+
struct Container my_container;
58+
my_container.data.big = f;
4859
int *my_int_ptr = &g_my_int;
4960
printf("my_int_ptr points to location %p\n", my_int_ptr);
5061
const char **str_ptr = days_of_week;

0 commit comments

Comments
 (0)