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:
Greg Clayton 2014-12-16 01:33:17 +00:00
parent bba821b5b1
commit a2162b3166
3 changed files with 144 additions and 58 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}