[Lldb-commits] [lldb] r168018 - in /lldb/trunk: include/lldb/Expression/ include/lldb/Target/ source/Expression/ source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/ test/lang/objc/objc-ivar-stripped/

Sean Callanan scallanan at apple.com
Wed Nov 14 18:02:04 PST 2012


Author: spyffe
Date: Wed Nov 14 20:02:04 2012
New Revision: 168018

URL: http://llvm.org/viewvc/llvm-project?rev=168018&view=rev
Log:
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>

Added:
    lldb/trunk/test/lang/objc/objc-ivar-stripped/
    lldb/trunk/test/lang/objc/objc-ivar-stripped/Makefile
    lldb/trunk/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py
    lldb/trunk/test/lang/objc/objc-ivar-stripped/main.m
Modified:
    lldb/trunk/include/lldb/Expression/ClangExpressionDeclMap.h
    lldb/trunk/include/lldb/Target/ObjCLanguageRuntime.h
    lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp
    lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
    lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
    lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp

Modified: lldb/trunk/include/lldb/Expression/ClangExpressionDeclMap.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Expression/ClangExpressionDeclMap.h?rev=168018&r1=168017&r2=168018&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Expression/ClangExpressionDeclMap.h (original)
+++ lldb/trunk/include/lldb/Expression/ClangExpressionDeclMap.h Wed Nov 14 20:02:04 2012
@@ -328,6 +328,11 @@
     ///     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 @@
     //------------------------------------------------------------------
     lldb::addr_t 
     GetSymbolAddress (Target &target,
+                      Process *process,
                       const ConstString &name,
                       lldb::SymbolType symbol_type);
     

Modified: lldb/trunk/include/lldb/Target/ObjCLanguageRuntime.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/ObjCLanguageRuntime.h?rev=168018&r1=168017&r2=168018&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/ObjCLanguageRuntime.h (original)
+++ lldb/trunk/include/lldb/Target/ObjCLanguageRuntime.h Wed Nov 14 20:02:04 2012
@@ -114,8 +114,9 @@
         // 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 @@
     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.
     ///

Modified: lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp?rev=168018&r1=168017&r2=168018&view=diff
==============================================================================
--- lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp (original)
+++ lldb/trunk/source/Expression/ClangExpressionDeclMap.cpp Wed Nov 14 20:02:04 2012
@@ -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 @@
 }
 
 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 @@
         }
     }
     
+    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 @@
     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 @@
     }
     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)
         {

Modified: lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp?rev=168018&r1=168017&r2=168018&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp (original)
+++ lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp Wed Nov 14 20:02:04 2012
@@ -1047,8 +1047,9 @@
     
     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 @@
             {
                 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 @@
             
             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 @@
         }
     };
     
+    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 @@
     
     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;
+}

Modified: lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h?rev=168018&r1=168017&r2=168018&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h (original)
+++ lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h Wed Nov 14 20:02:04 2012
@@ -99,6 +99,9 @@
     virtual TypeVendor *
     GetTypeVendor();
     
+    virtual lldb::addr_t
+    LookupRuntimeSymbol (const ConstString &name);
+    
 protected:
     virtual lldb::BreakpointResolverSP
     CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp);

Modified: lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp?rev=168018&r1=168017&r2=168018&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp (original)
+++ lldb/trunk/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTypeVendor.cpp Wed Nov 14 20:02:04 2012
@@ -529,6 +529,8 @@
         
         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 @@
         
         if (method_decl)
             interface_decl->addDecl(method_decl);
+        
+        return false;
     };
     
     if (log)
@@ -552,7 +556,10 @@
     }
     
     
-    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)

Added: lldb/trunk/test/lang/objc/objc-ivar-stripped/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/objc/objc-ivar-stripped/Makefile?rev=168018&view=auto
==============================================================================
--- lldb/trunk/test/lang/objc/objc-ivar-stripped/Makefile (added)
+++ lldb/trunk/test/lang/objc/objc-ivar-stripped/Makefile Wed Nov 14 20:02:04 2012
@@ -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

Added: lldb/trunk/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py?rev=168018&view=auto
==============================================================================
--- lldb/trunk/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py (added)
+++ lldb/trunk/test/lang/objc/objc-ivar-stripped/TestObjCIvarStripped.py Wed Nov 14 20:02:04 2012
@@ -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()

Added: lldb/trunk/test/lang/objc/objc-ivar-stripped/main.m
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/objc/objc-ivar-stripped/main.m?rev=168018&view=auto
==============================================================================
--- lldb/trunk/test/lang/objc/objc-ivar-stripped/main.m (added)
+++ lldb/trunk/test/lang/objc/objc-ivar-stripped/main.m Wed Nov 14 20:02:04 2012
@@ -0,0 +1,33 @@
+#import <Foundation/Foundation.h>
+
+ at interface MyClass : NSObject {
+ at public
+  int _foo;
+};
+
+-(id)init;
+ at end
+
+ at implementation MyClass
+
+-(id)init
+{
+  if ([super init])
+  {
+    _foo = 3;
+  }
+
+  return self;
+}
+
+ at end
+
+int main ()
+{
+  @autoreleasepool
+  {
+    MyClass *mc = [[MyClass alloc] init];
+
+    NSLog(@"%d", mc->_foo); // Set breakpoint here.
+  }
+}





More information about the lldb-commits mailing list