If a binary was stripped we sometimes didn't show the ivars of an Objective C class correctly. Now we do as we consult the runtime data for the class so we don't have to have a symbol in the symbol table.
Fixed: 1 - try the symbol table symbol for an ObjC ivar and use it if available 2 - fall back to using the runtime data since it is slower to gather via memory read 3 - Fixed our hidden ivars test case to test this to ensure we don't regress 4 - split out a test case in the hidden ivars to cover only the part that was failing so we don't have an expected failure for all of the other content in the test. <rdar://problem/18882687> llvm-svn: 224306
This commit is contained in:
parent
bba821b5b1
commit
a2162b3166
|
@ -554,36 +554,52 @@ AppleObjCRuntimeV2::CreateObjectChecker(const char *name)
|
|||
size_t
|
||||
AppleObjCRuntimeV2::GetByteOffsetForIvar (ClangASTType &parent_ast_type, const char *ivar_name)
|
||||
{
|
||||
uint32_t ivar_offset = LLDB_INVALID_IVAR_OFFSET;
|
||||
|
||||
const char *class_name = parent_ast_type.GetConstTypeName().AsCString();
|
||||
|
||||
if (!class_name || *class_name == '\0' || !ivar_name || *ivar_name == '\0')
|
||||
return LLDB_INVALID_IVAR_OFFSET;
|
||||
|
||||
if (class_name && class_name[0] && ivar_name && ivar_name[0])
|
||||
{
|
||||
//----------------------------------------------------------------------
|
||||
// Make the objective C V2 mangled name for the ivar offset from the
|
||||
// class name and ivar name
|
||||
//----------------------------------------------------------------------
|
||||
std::string buffer("OBJC_IVAR_$_");
|
||||
buffer.append (class_name);
|
||||
buffer.push_back ('.');
|
||||
buffer.append (ivar_name);
|
||||
ConstString ivar_const_str (buffer.c_str());
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Try to get the ivar offset address from the symbol table first using
|
||||
// the name we created above
|
||||
//----------------------------------------------------------------------
|
||||
SymbolContextList sc_list;
|
||||
Target &target = m_process->GetTarget();
|
||||
|
||||
target.GetImages().FindSymbolsWithNameAndType(ivar_const_str, eSymbolTypeObjCIVar, sc_list);
|
||||
|
||||
SymbolContext ivar_offset_symbol;
|
||||
if (sc_list.GetSize() != 1
|
||||
|| !sc_list.GetContextAtIndex(0, ivar_offset_symbol)
|
||||
|| ivar_offset_symbol.symbol == NULL)
|
||||
return LLDB_INVALID_IVAR_OFFSET;
|
||||
|
||||
addr_t ivar_offset_address = ivar_offset_symbol.symbol->GetAddress().GetLoadAddress (&target);
|
||||
addr_t ivar_offset_address = LLDB_INVALID_ADDRESS;
|
||||
|
||||
Error error;
|
||||
SymbolContext ivar_offset_symbol;
|
||||
if (sc_list.GetSize() == 1 && sc_list.GetContextAtIndex(0, ivar_offset_symbol))
|
||||
{
|
||||
if (ivar_offset_symbol.symbol)
|
||||
ivar_offset_address = ivar_offset_symbol.symbol->GetAddress().GetLoadAddress (&target);
|
||||
}
|
||||
|
||||
uint32_t ivar_offset = m_process->ReadUnsignedIntegerFromMemory (ivar_offset_address,
|
||||
//----------------------------------------------------------------------
|
||||
// If we didn't get the ivar offset address from the symbol table, fall
|
||||
// back to getting it from the runtime
|
||||
//----------------------------------------------------------------------
|
||||
if (ivar_offset_address == LLDB_INVALID_ADDRESS)
|
||||
ivar_offset_address = LookupRuntimeSymbol(ivar_const_str);
|
||||
|
||||
if (ivar_offset_address != LLDB_INVALID_ADDRESS)
|
||||
ivar_offset = m_process->ReadUnsignedIntegerFromMemory (ivar_offset_address,
|
||||
4,
|
||||
LLDB_INVALID_IVAR_OFFSET,
|
||||
error);
|
||||
}
|
||||
return ivar_offset;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import unittest2
|
|||
import lldb
|
||||
from lldbtest import *
|
||||
import lldbutil
|
||||
import subprocess
|
||||
|
||||
class HiddenIvarsTestCase(TestBase):
|
||||
|
||||
|
@ -15,34 +16,74 @@ class HiddenIvarsTestCase(TestBase):
|
|||
def test_expr_with_dsym(self):
|
||||
if self.getArchitecture() == 'i386':
|
||||
self.skipTest("requires modern objc runtime")
|
||||
else:
|
||||
self.buildDsym()
|
||||
self.expr()
|
||||
self.expr(False)
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dsym_test
|
||||
def test_expr_stripped_with_dsym(self):
|
||||
if self.getArchitecture() == 'i386':
|
||||
self.skipTest("requires modern objc runtime")
|
||||
else:
|
||||
self.buildDsym()
|
||||
self.expr(True)
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dwarf_test
|
||||
def test_expr_with_dwarf(self):
|
||||
if self.getArchitecture() == 'i386':
|
||||
self.skipTest("requires modern objc runtime")
|
||||
else:
|
||||
self.buildDwarf()
|
||||
self.expr()
|
||||
self.expr(False)
|
||||
|
||||
@unittest2.expectedFailure("rdar://18683637")
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dsym_test
|
||||
def test_frame_variable_with_dsym(self):
|
||||
if self.getArchitecture() == 'i386':
|
||||
self.skipTest("requires modern objc runtime")
|
||||
else:
|
||||
self.buildDsym()
|
||||
self.frame_var()
|
||||
self.frame_var(False)
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dsym_test
|
||||
def test_frame_variable_stripped_with_dsym(self):
|
||||
if self.getArchitecture() == 'i386':
|
||||
self.skipTest("requires modern objc runtime")
|
||||
else:
|
||||
self.buildDsym()
|
||||
self.frame_var(True)
|
||||
|
||||
@unittest2.expectedFailure("rdar://18683637")
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dwarf_test
|
||||
def test_frame_variable_with_dwarf(self):
|
||||
if self.getArchitecture() == 'i386':
|
||||
self.skipTest("requires modern objc runtime")
|
||||
else:
|
||||
self.buildDwarf()
|
||||
self.frame_var()
|
||||
self.frame_var(False)
|
||||
|
||||
@unittest2.expectedFailure("rdar://18683637")
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dsym_test
|
||||
def test_frame_variable_across_modules_with_dsym(self):
|
||||
if self.getArchitecture() == 'i386':
|
||||
self.skipTest("requires modern objc runtime")
|
||||
else:
|
||||
self.buildDsym()
|
||||
self.frame_var_type_access_across_module()
|
||||
|
||||
@unittest2.expectedFailure("rdar://18683637")
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@dwarf_test
|
||||
def test_frame_variable_across_modules_with_dwarf(self):
|
||||
if self.getArchitecture() == 'i386':
|
||||
self.skipTest("requires modern objc runtime")
|
||||
else:
|
||||
self.buildDwarf()
|
||||
self.frame_var_type_access_across_module()
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
|
@ -54,8 +95,12 @@ class HiddenIvarsTestCase(TestBase):
|
|||
# The names should have no loading "lib" or extension as they will be localized
|
||||
self.shlib_names = ["InternalDefiner"]
|
||||
|
||||
def common_setup(self):
|
||||
def common_setup(self, strip):
|
||||
|
||||
if strip:
|
||||
self.assertTrue(subprocess.call(['/usr/bin/strip', '-Sx', 'libInternalDefiner.dylib']) == 0, 'stripping dylib succeeded')
|
||||
self.assertTrue(subprocess.call(['/bin/rm', '-rf', 'libInternalDefiner.dylib.dSYM']) == 0, 'remove dylib dSYM file succeeded')
|
||||
self.assertTrue(subprocess.call(['/usr/bin/strip', '-Sx', 'a.out']) == 0, 'stripping a.out succeeded')
|
||||
# Create a target by the debugger.
|
||||
target = self.dbg.CreateTarget("a.out")
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
|
@ -88,8 +133,8 @@ class HiddenIvarsTestCase(TestBase):
|
|||
self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
|
||||
substrs = [' resolved, hit count = 1'])
|
||||
|
||||
def expr(self):
|
||||
self.common_setup()
|
||||
def expr(self, strip):
|
||||
self.common_setup(strip)
|
||||
|
||||
# This should display correctly.
|
||||
self.expect("expression (j->_definer->foo)", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
|
@ -98,6 +143,10 @@ class HiddenIvarsTestCase(TestBase):
|
|||
self.expect("expression (j->_definer->bar)", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["= 5"])
|
||||
|
||||
if strip:
|
||||
self.expect("expression *(j->_definer)", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["foo = 4"])
|
||||
else:
|
||||
self.expect("expression *(j->_definer)", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["foo = 4", "bar = 5"])
|
||||
|
||||
|
@ -107,30 +156,52 @@ class HiddenIvarsTestCase(TestBase):
|
|||
self.expect("expression (k->bar)", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["= 3"])
|
||||
|
||||
self.expect("expression *(k)", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["foo = 2", "bar = 3"])
|
||||
self.expect("expression k.filteredDataSource", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = [' = 0x', '"2 objects"'])
|
||||
|
||||
def frame_var(self):
|
||||
self.common_setup()
|
||||
if strip:
|
||||
self.expect("expression *(k)", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["foo = 2", ' = 0x', '"2 objects"'])
|
||||
else:
|
||||
self.expect("expression *(k)", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["foo = 2", "bar = 3", '_filteredDataSource = 0x', '"2 objects"'])
|
||||
|
||||
def frame_var(self, strip):
|
||||
self.common_setup(strip)
|
||||
|
||||
# This should display correctly.
|
||||
self.expect("frame variable j->_definer->foo", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["= 4"])
|
||||
|
||||
if not strip:
|
||||
self.expect("frame variable j->_definer->bar", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["= 5"])
|
||||
|
||||
if strip:
|
||||
self.expect("frame variable *j->_definer", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["foo = 4"])
|
||||
else:
|
||||
self.expect("frame variable *j->_definer", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["foo = 4", "bar = 5"])
|
||||
|
||||
self.expect("frame variable k->foo", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["= 2"])
|
||||
|
||||
self.expect("frame variable k->bar", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["= 3"])
|
||||
self.expect("frame variable k->_filteredDataSource", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = [' = 0x', '"2 objects"'])
|
||||
|
||||
if strip:
|
||||
self.expect("frame variable *k", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["foo = 2", "bar = 3"])
|
||||
substrs = ["foo = 2", '_filteredDataSource = 0x', '"2 objects"'])
|
||||
else:
|
||||
self.expect("frame variable *k", VARIABLES_DISPLAYED_CORRECTLY,
|
||||
substrs = ["foo = 2", "bar = 3", '_filteredDataSource = 0x', '"2 objects"'])
|
||||
|
||||
def frame_var_type_access_across_module(self):
|
||||
self.common_setup(False)
|
||||
|
||||
self.expect("frame variable k->bar", VARIABLES_DISPLAYED_CORRECTLY, substrs = ["= 3"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
|
|
|
@ -23,9 +23,7 @@
|
|||
@end
|
||||
|
||||
@interface InheritContainer : InternalDefiner
|
||||
{
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *filteredDataSource;
|
||||
-(id)init;
|
||||
@end
|
||||
|
||||
|
@ -35,6 +33,7 @@
|
|||
{
|
||||
if (self = [super initWithFoo:2 andBar:3])
|
||||
{
|
||||
self.filteredDataSource = [NSMutableArray arrayWithObjects:@"hello", @"world", nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue