/// 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.
///
//------------------------------------------------------------------
lldb::addr_t
GetSymbolAddress (Target &target,
+ Process *process,
const ConstString &name,
lldb::SymbolType symbol_type);
// 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;
}
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.
///
#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"
}
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;
}
}
+ if (symbol_load_addr == LLDB_INVALID_ADDRESS && process)
+ {
+ ObjCLanguageRuntime *runtime = process->GetObjCLanguageRuntime();
+
+ if (runtime)
+ {
+ symbol_load_addr = runtime->LookupRuntimeSymbol(name);
+ }
+ }
+
return symbol_load_addr;
}
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
}
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)
{
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();
{
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;
}
}
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;
}
}
};
+ 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);
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;
+}
virtual TypeVendor *
GetTypeVendor();
+ virtual lldb::addr_t
+ LookupRuntimeSymbol (const ConstString &name);
+
protected:
virtual lldb::BreakpointResolverSP
CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp);
if (method_decl)
interface_decl->addDecl(method_decl);
+
+ return false;
};
auto class_method_func = [log, interface_decl, this](const char *name, const char *types)
if (method_decl)
interface_decl->addDecl(method_decl);
+
+ return false;
};
if (log)
}
- 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)
--- /dev/null
+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
--- /dev/null
+"""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()
--- /dev/null
+#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.
+ }
+}