summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean Callanan <scallanan@apple.com>2012-11-15 02:02:04 +0000
committerSean Callanan <scallanan@apple.com>2012-11-15 02:02:04 +0000
commite0b23b5198baa1499edaf0209c164e81007c4dfe (patch)
tree84dc4e81c828133899b75fb134f8f9541eb7008f
parenta660ae4421ed77fdbfe7fb461f78b9a13c5fc12b (diff)
downloadbcm5719-llvm-e0b23b5198baa1499edaf0209c164e81007c4dfe.tar.gz
bcm5719-llvm-e0b23b5198baa1499edaf0209c164e81007c4dfe.zip
In cases where the Objective-C ivar symbols are stripped out,
expressions that refer to ivars will not work because Clang emits IR that refers to them to get the ivar offsets. However, it is possible to search the runtime for these values. I have added support for reading the relevant tables to the Objective-C runtime, and extended ClangExpressionDeclMap to query that information if and only if it doesn't find the symbols in the binary. Also added a testcase. <rdar://problem/12628122> llvm-svn: 168018
-rw-r--r--lldb/include/lldb/Expression/ClangExpressionDeclMap.h6
-rw-r--r--lldb/include/lldb/Target/ObjCLanguageRuntime.h14
-rw-r--r--lldb/source/Expression/ClangExpressionDeclMap.cpp17
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp177
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h3
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp9
-rw-r--r--lldb/test/lang/objc/objc-ivar-stripped/Makefile15
-rw-r--r--lldb/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py63
-rw-r--r--lldb/test/lang/objc/objc-ivar-stripped/main.m33
9 files changed, 326 insertions, 11 deletions
diff --git a/lldb/include/lldb/Expression/ClangExpressionDeclMap.h b/lldb/include/lldb/Expression/ClangExpressionDeclMap.h
index a300053de33..a32ac3310a0 100644
--- a/lldb/include/lldb/Expression/ClangExpressionDeclMap.h
+++ b/lldb/include/lldb/Expression/ClangExpressionDeclMap.h
@@ -328,6 +328,11 @@ public:
/// The target to find the symbol in. If not provided,
/// then the current parsing context's Target.
///
+ /// @param[in] process
+ /// The process to use. For Objective-C symbols, the process's
+ /// Objective-C language runtime may be queried if the process
+ /// is non-NULL.
+ ///
/// @param[in] name
/// The name of the symbol.
///
@@ -336,6 +341,7 @@ public:
//------------------------------------------------------------------
lldb::addr_t
GetSymbolAddress (Target &target,
+ Process *process,
const ConstString &name,
lldb::SymbolType symbol_type);
diff --git a/lldb/include/lldb/Target/ObjCLanguageRuntime.h b/lldb/include/lldb/Target/ObjCLanguageRuntime.h
index cbfd4a19555..140e5f6fc45 100644
--- a/lldb/include/lldb/Target/ObjCLanguageRuntime.h
+++ b/lldb/include/lldb/Target/ObjCLanguageRuntime.h
@@ -114,8 +114,9 @@ public:
// This should return true iff the interface could be completed
virtual bool
Describe (std::function <void (ObjCISA)> const &superclass_func,
- std::function <void (const char*, const char*)> const &instance_method_func,
- std::function <void (const char*, const char*)> const &class_method_func)
+ std::function <bool (const char*, const char*)> const &instance_method_func,
+ std::function <bool (const char*, const char*)> const &class_method_func,
+ std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> const &ivar_func)
{
return false;
}
@@ -285,6 +286,15 @@ public:
virtual size_t
GetByteOffsetForIvar (ClangASTType &parent_qual_type, const char *ivar_name);
+ // Given the name of an Objective-C runtime symbol (e.g., ivar offset symbol),
+ // try to determine from the runtime what the value of that symbol would be.
+ // Useful when the underlying binary is stripped.
+ virtual lldb::addr_t
+ LookupRuntimeSymbol (const ConstString &name)
+ {
+ return LLDB_INVALID_ADDRESS;
+ }
+
//------------------------------------------------------------------
/// Chop up an objective C function prototype.
///
diff --git a/lldb/source/Expression/ClangExpressionDeclMap.cpp b/lldb/source/Expression/ClangExpressionDeclMap.cpp
index f54d53e7f7e..4cd74a86b34 100644
--- a/lldb/source/Expression/ClangExpressionDeclMap.cpp
+++ b/lldb/source/Expression/ClangExpressionDeclMap.cpp
@@ -40,6 +40,7 @@
#include "lldb/Symbol/Variable.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
@@ -749,7 +750,7 @@ ClangExpressionDeclMap::GetFunctionAddress
}
addr_t
-ClangExpressionDeclMap::GetSymbolAddress (Target &target, const ConstString &name, lldb::SymbolType symbol_type)
+ClangExpressionDeclMap::GetSymbolAddress (Target &target, Process *process, const ConstString &name, lldb::SymbolType symbol_type)
{
SymbolContextList sc_list;
@@ -808,6 +809,16 @@ ClangExpressionDeclMap::GetSymbolAddress (Target &target, const ConstString &nam
}
}
+ if (symbol_load_addr == LLDB_INVALID_ADDRESS && process)
+ {
+ ObjCLanguageRuntime *runtime = process->GetObjCLanguageRuntime();
+
+ if (runtime)
+ {
+ symbol_load_addr = runtime->LookupRuntimeSymbol(name);
+ }
+ }
+
return symbol_load_addr;
}
@@ -819,7 +830,7 @@ ClangExpressionDeclMap::GetSymbolAddress (const ConstString &name, lldb::SymbolT
if (!m_parser_vars->m_exe_ctx.GetTargetPtr())
return false;
- return GetSymbolAddress(m_parser_vars->m_exe_ctx.GetTargetRef(), name, symbol_type);
+ return GetSymbolAddress(m_parser_vars->m_exe_ctx.GetTargetRef(), m_parser_vars->m_exe_ctx.GetProcessPtr(), name, symbol_type);
}
// Interface for IRInterpreter
@@ -1840,7 +1851,7 @@ ClangExpressionDeclMap::DoMaterializeOneVariable
}
else if (sym)
{
- addr_t location_load_addr = GetSymbolAddress(*target, name, lldb::eSymbolTypeAny);
+ addr_t location_load_addr = GetSymbolAddress(*target, process, name, lldb::eSymbolTypeAny);
if (location_load_addr == LLDB_INVALID_ADDRESS)
{
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
index 5b758c86ca5..9fb6f4e7ab5 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -1047,8 +1047,9 @@ public:
virtual bool
Describe (std::function <void (ObjCLanguageRuntime::ObjCISA)> const &superclass_func,
- std::function <void (const char *, const char *)> const &instance_method_func,
- std::function <void (const char *, const char *)> const &class_method_func)
+ std::function <bool (const char *, const char *)> const &instance_method_func,
+ std::function <bool (const char *, const char *)> const &class_method_func,
+ std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> const &ivar_func)
{
lldb_private::Process *process = m_runtime.GetProcess();
@@ -1084,7 +1085,8 @@ public:
{
method->Read(process, base_method_list->m_first_ptr + (i * base_method_list->m_entsize));
- instance_method_func(method->m_name.c_str(), method->m_types.c_str());
+ if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
+ break;
}
}
@@ -1097,9 +1099,32 @@ public:
metaclass.Describe(std::function <void (ObjCLanguageRuntime::ObjCISA)> (nullptr),
class_method_func,
- std::function <void (const char *, const char *)> (nullptr));
+ std::function <bool (const char *, const char *)> (nullptr),
+ std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> (nullptr));
+ }
+
+ if (ivar_func)
+ {
+ std::auto_ptr <ivar_list_t> ivar_list;
+
+ ivar_list.reset(new ivar_list_t);
+ if (!ivar_list->Read(process, class_ro->m_ivars_ptr))
+ return false;
+
+ if (ivar_list->m_entsize != ivar_t::GetSize(process))
+ return false;
+
+ std::auto_ptr <ivar_t> ivar;
+ ivar.reset(new ivar_t);
+
+ for (uint32_t i = 0, e = ivar_list->m_count; i < e; ++i)
+ {
+ ivar->Read(process, ivar_list->m_first_ptr + (i * ivar_list->m_entsize));
+
+ if (ivar_func(ivar->m_name.c_str(), ivar->m_type.c_str(), ivar->m_offset_ptr, ivar->m_size))
+ break;
+ }
}
- while (0);
return true;
}
@@ -1392,6 +1417,98 @@ private:
}
};
+ struct ivar_list_t
+ {
+ uint32_t m_entsize;
+ uint32_t m_count;
+ lldb::addr_t m_first_ptr;
+
+ bool Read(Process *process, lldb::addr_t addr)
+ {
+ size_t size = sizeof(uint32_t) // uint32_t entsize;
+ + sizeof(uint32_t); // uint32_t count;
+
+ DataBufferHeap buffer (size, '\0');
+ Error error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail())
+ {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), process->GetAddressByteSize());
+
+ uint32_t cursor = 0;
+
+ m_entsize = extractor.GetU32_unchecked(&cursor);
+ m_count = extractor.GetU32_unchecked(&cursor);
+ m_first_ptr = addr + cursor;
+
+ return true;
+ }
+ };
+
+ struct ivar_t
+ {
+ lldb::addr_t m_offset_ptr;
+ lldb::addr_t m_name_ptr;
+ lldb::addr_t m_type_ptr;
+ uint32_t m_alignment;
+ uint32_t m_size;
+
+ std::string m_name;
+ std::string m_type;
+
+ static size_t GetSize(Process *process)
+ {
+ size_t ptr_size = process->GetAddressByteSize();
+
+ return ptr_size // uintptr_t *offset;
+ + ptr_size // const char *name;
+ + ptr_size // const char *type;
+ + sizeof(uint32_t) // uint32_t alignment;
+ + sizeof(uint32_t); // uint32_t size;
+ }
+
+ bool Read(Process *process, lldb::addr_t addr)
+ {
+ size_t size = GetSize(process);
+
+ DataBufferHeap buffer (size, '\0');
+ Error error;
+
+ process->ReadMemory(addr, buffer.GetBytes(), size, error);
+ if (error.Fail())
+ {
+ return false;
+ }
+
+ DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(), process->GetAddressByteSize());
+
+ uint32_t cursor = 0;
+
+ m_offset_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_name_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_type_ptr = extractor.GetAddress_unchecked(&cursor);
+ m_alignment = extractor.GetU32_unchecked(&cursor);
+ m_size = extractor.GetU32_unchecked(&cursor);
+
+ const size_t buffer_size = 1024;
+ size_t count;
+
+ DataBufferHeap string_buf(buffer_size, 0);
+
+ count = process->ReadCStringFromMemory(m_name_ptr, (char*)string_buf.GetBytes(), buffer_size, error);
+ m_name.assign((char*)string_buf.GetBytes(), count);
+
+ count = process->ReadCStringFromMemory(m_type_ptr, (char*)string_buf.GetBytes(), buffer_size, error);
+ m_type.assign((char*)string_buf.GetBytes(), count);
+
+ return true;
+ }
+ };
+
bool Read_objc_class (Process* process, std::auto_ptr<objc_class_t> &objc_class)
{
objc_class.reset(new objc_class_t);
@@ -1864,3 +1981,53 @@ AppleObjCRuntimeV2::GetTypeVendor()
return m_type_vendor_ap.get();
}
+
+lldb::addr_t
+AppleObjCRuntimeV2::LookupRuntimeSymbol (const ConstString &name)
+{
+ lldb::addr_t ret = LLDB_INVALID_ADDRESS;
+
+ const char *name_cstr = name.AsCString();
+
+ if (name_cstr)
+ {
+ llvm::StringRef name_strref(name_cstr);
+
+ static const llvm::StringRef ivar_prefix("OBJC_IVAR_$_");
+
+ if (name_strref.startswith(ivar_prefix))
+ {
+ llvm::StringRef ivar_skipped_prefix = name_strref.substr(ivar_prefix.size());
+ std::pair<llvm::StringRef, llvm::StringRef> class_and_ivar = ivar_skipped_prefix.split('.');
+
+ if (class_and_ivar.first.size() && class_and_ivar.second.size())
+ {
+ const ConstString class_name_cs(class_and_ivar.first);
+ ClassDescriptorSP descriptor = ObjCLanguageRuntime::GetClassDescriptor(class_name_cs);
+
+ if (descriptor)
+ {
+ const ConstString ivar_name_cs(class_and_ivar.second);
+ const char *ivar_name_cstr = ivar_name_cs.AsCString();
+
+ auto ivar_func = [&ret, ivar_name_cstr](const char *name, const char *type, lldb::addr_t offset_addr, uint64_t size)
+ {
+ if (!strcmp(name, ivar_name_cstr))
+ {
+ ret = offset_addr;
+ return true;
+ }
+ return false;
+ };
+
+ descriptor->Describe(std::function<void (ObjCISA)>(nullptr),
+ std::function<bool (const char *, const char *)>(nullptr),
+ std::function<bool (const char *, const char *)>(nullptr),
+ ivar_func);
+ }
+ }
+ }
+ }
+
+ return ret;
+}
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
index 8dafd0f3e4c..59401825a3a 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
@@ -99,6 +99,9 @@ public:
virtual TypeVendor *
GetTypeVendor();
+ virtual lldb::addr_t
+ LookupRuntimeSymbol (const ConstString &name);
+
protected:
virtual lldb::BreakpointResolverSP
CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp);
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp
index 1ab1063f4f4..69f4209f13e 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp
@@ -529,6 +529,8 @@ AppleObjCTypeVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl)
if (method_decl)
interface_decl->addDecl(method_decl);
+
+ return false;
};
auto class_method_func = [log, interface_decl, this](const char *name, const char *types)
@@ -542,6 +544,8 @@ AppleObjCTypeVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl)
if (method_decl)
interface_decl->addDecl(method_decl);
+
+ return false;
};
if (log)
@@ -552,7 +556,10 @@ AppleObjCTypeVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl)
}
- if (!descriptor->Describe(superclass_func, instance_method_func, class_method_func))
+ if (!descriptor->Describe(superclass_func,
+ instance_method_func,
+ class_method_func,
+ std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> (nullptr)))
return false;
if (log)
diff --git a/lldb/test/lang/objc/objc-ivar-stripped/Makefile b/lldb/test/lang/objc/objc-ivar-stripped/Makefile
new file mode 100644
index 00000000000..4365ed9ae93
--- /dev/null
+++ b/lldb/test/lang/objc/objc-ivar-stripped/Makefile
@@ -0,0 +1,15 @@
+LEVEL = ../../../make
+
+OBJC_SOURCES := main.m
+LDFLAGS = $(CFLAGS) -lobjc -framework Foundation
+
+default: a.out.stripped
+
+a.out.stripped: a.out.dSYM
+ strip -o a.out.stripped a.out
+
+clean::
+ rm -f a.out.stripped
+ rm -rf a.out.stripped.dSYM
+
+include $(LEVEL)/Makefile.rules
diff --git a/lldb/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py b/lldb/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py
new file mode 100644
index 00000000000..fef0921d896
--- /dev/null
+++ b/lldb/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py
@@ -0,0 +1,63 @@
+"""Test printing ObjC objects that use unbacked properties - so that the static ivar offsets are incorrect."""
+
+import os, time
+import unittest2
+import lldb
+from lldbtest import *
+import lldbutil
+
+class TestObjCIvarStripped(TestBase):
+
+ mydir = os.path.join("lang", "objc", "objc-ivar-stripped")
+
+ @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
+ @python_api_test
+ @dsym_test
+ def test_with_dsym_and_python_api(self):
+ """Test that we can find stripped Objective-C ivars in the runtime"""
+ self.buildDsym()
+ self.objc_ivar_offsets()
+
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+ # Find the line numbers to break inside main().
+ self.main_source = "main.m"
+ self.stop_line = line_number(self.main_source, '// Set breakpoint here.')
+
+ def objc_ivar_offsets(self):
+ """Test that we can find stripped Objective-C ivars in the runtime"""
+ exe = os.path.join(os.getcwd(), "a.out.stripped")
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, VALID_TARGET)
+
+ breakpoint = target.BreakpointCreateByLocation(self.main_source, self.stop_line)
+ self.assertTrue(breakpoint, VALID_BREAKPOINT)
+
+ process = target.LaunchSimple (None, None, os.getcwd())
+ self.assertTrue (process, "Created a process.")
+ self.assertTrue (process.GetState() == lldb.eStateStopped, "Stopped it too.")
+
+ thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, breakpoint)
+ self.assertTrue (len(thread_list) == 1)
+ thread = thread_list[0]
+
+ frame = thread.GetFrameAtIndex(0)
+ self.assertTrue (frame, "frame 0 is valid")
+
+ # Test the expression for mc->_foo
+
+ error = lldb.SBError()
+
+ ivar = frame.EvaluateExpression ("(mc->_foo)")
+ self.assertTrue(ivar, "Got result for mc->_foo")
+ ivar_value = ivar.GetValueAsSigned (error)
+ self.assertTrue (error.Success())
+ self.assertTrue (ivar_value == 3)
+
+if __name__ == '__main__':
+ import atexit
+ lldb.SBDebugger.Initialize()
+ atexit.register(lambda: lldb.SBDebugger.Terminate())
+ unittest2.main()
diff --git a/lldb/test/lang/objc/objc-ivar-stripped/main.m b/lldb/test/lang/objc/objc-ivar-stripped/main.m
new file mode 100644
index 00000000000..ed9c1d9ec42
--- /dev/null
+++ b/lldb/test/lang/objc/objc-ivar-stripped/main.m
@@ -0,0 +1,33 @@
+#import <Foundation/Foundation.h>
+
+@interface MyClass : NSObject {
+@public
+ int _foo;
+};
+
+-(id)init;
+@end
+
+@implementation MyClass
+
+-(id)init
+{
+ if ([super init])
+ {
+ _foo = 3;
+ }
+
+ return self;
+}
+
+@end
+
+int main ()
+{
+ @autoreleasepool
+ {
+ MyClass *mc = [[MyClass alloc] init];
+
+ NSLog(@"%d", mc->_foo); // Set breakpoint here.
+ }
+}
OpenPOWER on IntegriCloud