diff options
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. + } +} |

