Index: include/lldb/Expression/DWARFExpression.h =================================================================== --- include/lldb/Expression/DWARFExpression.h +++ include/lldb/Expression/DWARFExpression.h @@ -58,7 +58,8 @@ /// @param[in] data_length /// The byte length of the location expression. //------------------------------------------------------------------ - DWARFExpression(const DataExtractor& data, + DWARFExpression(lldb::ModuleSP module, + const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length); @@ -172,6 +173,9 @@ /// Make the expression parser read its location information from a /// given data source /// + /// @param[in] module_sp + /// The module that defines the DWARF expression. + /// /// @param[in] data /// A data extractor configured to read the DWARF location expression's /// bytecode. @@ -183,7 +187,7 @@ /// The byte length of the location expression. //------------------------------------------------------------------ void - SetOpcodeData(const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length); + SetOpcodeData(lldb::ModuleSP module_sp, const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length); //------------------------------------------------------------------ /// Copy the DWARF location expression into a local buffer. @@ -199,6 +203,9 @@ /// the data, it shouldn't amount to that much for the variables we /// end up parsing. /// + /// @param[in] module_sp + /// The module that defines the DWARF expression. + /// /// @param[in] data /// A data extractor configured to read and copy the DWARF /// location expression's bytecode. @@ -210,7 +217,8 @@ /// The byte length of the location expression. //------------------------------------------------------------------ void - CopyOpcodeData (const DataExtractor& data, + CopyOpcodeData (lldb::ModuleSP module_sp, + const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length); @@ -279,6 +287,9 @@ /// expression. The location expression may access the target's /// memory, especially if it comes from the expression parser. /// + /// @param[in] opcode_ctx + /// The module which defined the expression. + /// /// @param[in] opcodes /// This is a static method so the opcodes need to be provided /// explicitly. @@ -332,6 +343,7 @@ ClangExpressionVariableList *expr_locals, ClangExpressionDeclMap *decl_map, RegisterContext *reg_ctx, + lldb::ModuleSP opcode_ctx, const DataExtractor& opcodes, const lldb::offset_t offset, const lldb::offset_t length, @@ -410,7 +422,8 @@ //------------------------------------------------------------------ /// Classes that inherit from DWARFExpression can see and modify these //------------------------------------------------------------------ - + + lldb::ModuleWP m_module_wp; ///< Module which defined this expression. DataExtractor m_data; ///< A data extractor capable of reading opcode bytes lldb::RegisterKind m_reg_kind; ///< One of the defines that starts with LLDB_REGKIND_ lldb::addr_t m_loclist_slide; ///< A value used to slide the location list offsets so that Index: include/lldb/Target/DynamicLoader.h =================================================================== --- include/lldb/Target/DynamicLoader.h +++ include/lldb/Target/DynamicLoader.h @@ -224,6 +224,26 @@ return false; } + //------------------------------------------------------------------ + /// Retrieves the per-module TLS block for a given thread. + /// + /// @param[in] module + /// The module to query TLS data for. + /// + /// @param[in] thread + /// The specific thread to query TLS data for. + /// + /// @return + /// If the given thread has TLS data allocated for the + /// module, the address of the TLS block. Otherwise + /// LLDB_INVALID_ADDRESS is returned. + //------------------------------------------------------------------ + virtual lldb::addr_t + GetThreadLocalData (const lldb::ModuleSP module, const lldb::ThreadSP thread) + { + return LLDB_INVALID_ADDRESS; + } + protected: //------------------------------------------------------------------ // Member variables. Index: include/lldb/Target/Process.h =================================================================== --- include/lldb/Target/Process.h +++ include/lldb/Target/Process.h @@ -1838,6 +1838,38 @@ GetImageInfoAddress (); //------------------------------------------------------------------ + /// Retrieves the per-thread data area. + /// Most OSs maintain a per-thread pointer (e.g. the FS register on + /// x64), which we return the value of here. + /// + /// @param[in] thread + /// The specific thread to query data for. + /// + /// @return + /// LLDB_INVALID_ADDRESS if not supported, otherwise the thread + /// pointer value. + //------------------------------------------------------------------ + virtual lldb::addr_t + GetThreadPointer (const lldb::ThreadSP thread); + + //------------------------------------------------------------------ + /// Retrieves the per-module TLS block for a given thread. + /// + /// @param[in] module + /// The module to query TLS data for. + /// + /// @param[in] thread + /// The specific thread to query TLS data for. + /// + /// @return + /// If the given thread has TLS data allocated for the + /// module, the address of the TLS block. Otherwise + /// LLDB_INVALID_ADDRESS is returned. + //------------------------------------------------------------------ + virtual lldb::addr_t + GetThreadLocalData (const lldb::ModuleSP module, const lldb::ThreadSP thread); + + //------------------------------------------------------------------ /// Load a shared library into this process. /// /// Try and load a shared library into the current process. This Index: source/Expression/DWARFExpression.cpp =================================================================== --- source/Expression/DWARFExpression.cpp +++ source/Expression/DWARFExpression.cpp @@ -42,6 +42,7 @@ using namespace lldb; using namespace lldb_private; +// TODO- why is this also defined (in a better way) in DWARFDefines.cpp? const char * DW_OP_value_to_name (uint32_t val) { @@ -220,6 +221,7 @@ // DWARFExpression constructor //---------------------------------------------------------------------- DWARFExpression::DWARFExpression() : + m_module_wp(), m_data(), m_reg_kind (eRegisterKindDWARF), m_loclist_slide (LLDB_INVALID_ADDRESS) @@ -227,6 +229,7 @@ } DWARFExpression::DWARFExpression(const DWARFExpression& rhs) : + m_module_wp(rhs.m_module_wp), m_data(rhs.m_data), m_reg_kind (rhs.m_reg_kind), m_loclist_slide(rhs.m_loclist_slide) @@ -234,7 +237,8 @@ } -DWARFExpression::DWARFExpression(const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) : +DWARFExpression::DWARFExpression(lldb::ModuleSP module_sp, const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) : + m_module_wp(module_sp), m_data(data, data_offset, data_length), m_reg_kind (eRegisterKindDWARF), m_loclist_slide(LLDB_INVALID_ADDRESS) @@ -262,11 +266,12 @@ } void -DWARFExpression::CopyOpcodeData (const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) +DWARFExpression::CopyOpcodeData (lldb::ModuleSP module_sp, const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) { const uint8_t *bytes = data.PeekData(data_offset, data_length); if (bytes) { + m_module_wp = module_sp; m_data.SetData(DataBufferSP(new DataBufferHeap(bytes, data_length))); m_data.SetByteOrder(data.GetByteOrder()); m_data.SetAddressByteSize(data.GetAddressByteSize()); @@ -274,8 +279,9 @@ } void -DWARFExpression::SetOpcodeData (const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) +DWARFExpression::SetOpcodeData (lldb::ModuleSP module_sp, const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) { + m_module_wp = module_sp; m_data.SetData(data, data_offset, data_length); } @@ -588,6 +594,9 @@ // case DW_OP_APPLE_array_ref: // s->PutCString("DW_OP_APPLE_array_ref"); // break; + case DW_OP_GNU_push_tls_address: + s->PutCString("DW_OP_GNU_push_tls_address"); // 0xe0 + break; case DW_OP_APPLE_uninit: s->PutCString("DW_OP_APPLE_uninit"); // 0xF0 break; @@ -919,7 +928,8 @@ case DW_OP_form_tls_address: // 0x9b DWARF3 case DW_OP_call_frame_cfa: // 0x9c DWARF3 case DW_OP_stack_value: // 0x9f DWARF4 - return 0; + case DW_OP_GNU_push_tls_address: // 0xe0 GNU extension + return 0; // Opcodes with a single 1 byte arguments case DW_OP_const1u: // 0x08 1 1-byte constant @@ -1221,6 +1231,14 @@ Error *error_ptr ) const { + ModuleSP module_sp = m_module_wp.lock(); + if (!module_sp.get()) + { + if (error_ptr) + error_ptr->SetErrorString("Module was unloaded."); + return false; + } + if (IsLocationList()) { lldb::offset_t offset = 0; @@ -1268,7 +1286,7 @@ if (length > 0 && lo_pc <= pc && pc < hi_pc) { - return DWARFExpression::Evaluate (exe_ctx, expr_locals, decl_map, reg_ctx, m_data, offset, length, m_reg_kind, initial_value_ptr, result, error_ptr); + return DWARFExpression::Evaluate (exe_ctx, expr_locals, decl_map, reg_ctx, module_sp, m_data, offset, length, m_reg_kind, initial_value_ptr, result, error_ptr); } offset += length; } @@ -1280,7 +1298,7 @@ } // Not a location list, just a single expression. - return DWARFExpression::Evaluate (exe_ctx, expr_locals, decl_map, reg_ctx, m_data, 0, m_data.GetByteSize(), m_reg_kind, initial_value_ptr, result, error_ptr); + return DWARFExpression::Evaluate (exe_ctx, expr_locals, decl_map, reg_ctx, module_sp, m_data, 0, m_data.GetByteSize(), m_reg_kind, initial_value_ptr, result, error_ptr); } @@ -1292,6 +1310,7 @@ ClangExpressionVariableList *expr_locals, ClangExpressionDeclMap *decl_map, RegisterContext *reg_ctx, + lldb::ModuleSP opcode_ctx, const DataExtractor& opcodes, const lldb::offset_t opcodes_offset, const lldb::offset_t opcodes_length, @@ -2659,6 +2678,48 @@ return false; } break; + + //---------------------------------------------------------------------- + // OPCODE: DW_OP_GNU_push_tls_address + // OPERANDS: none + // DESCRIPTION: Pops a TLS offset from the stack, converts it to + // an absolute value, and pushes it back on. + //---------------------------------------------------------------------- + case DW_OP_GNU_push_tls_address: + { + if (stack.size() < 1) + { + if (error_ptr) + error_ptr->SetErrorString("DW_OP_GNU_push_tls_address needs an argument."); + return false; + } + + if (!exe_ctx) + { + if (error_ptr) + error_ptr->SetErrorString("No thread context to evaluate TLS within."); + return false; + } + + ThreadSP thread_sp = exe_ctx->GetThreadSP(); + + // Lookup the TLS block address for this thread and module. + addr_t tls_addr = process->GetThreadLocalData (opcode_ctx, thread_sp); + + if (tls_addr == LLDB_INVALID_ADDRESS) + { + if (error_ptr) + error_ptr->SetErrorString ("No TLS data currently exists for this thread."); + return false; + } + + // Convert the TLS offset into the absolute address. + Scalar tmp = stack.back().ResolveValue(exe_ctx); + stack.back() = tmp + tls_addr; + stack.back().SetValueType (Value::eValueTypeLoadAddress); + } + break; + default: if (log) log->Printf("Unhandled opcode %s in DWARFExpression.", DW_OP_value_to_name(op)); Index: source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h =================================================================== --- source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h +++ source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h @@ -48,6 +48,16 @@ }; public: + // Various metadata supplied by the inferior's threading library to describe + // the per-thread state. + struct ThreadInfo { + bool valid; // whether we read valid metadata + uint32_t dtv_offset; // offset of DTV pointer within pthread + uint32_t dtv_slot_size; // size of one DTV slot + uint32_t modid_offset; // offset of module ID within link_map + uint32_t tls_offset; // offset of TLS pointer within DTV slot + }; + DYLDRendezvous(lldb_private::Process *process); /// Update the internal snapshot of runtime linker rendezvous and recompute @@ -100,6 +110,10 @@ lldb::addr_t GetLDBase() const { return m_current.ldbase; } + /// @returns the thread layout metadata from the inferiors thread library. + const ThreadInfo& + GetThreadInfo(); + /// @returns true if modules have been loaded into the inferior since the /// last call to Resolve(). bool @@ -128,6 +142,7 @@ /// This object is a rough analogue to the struct link_map object which /// actually lives in the inferiors memory. struct SOEntry { + lldb::addr_t link_addr; ///< Address of this link_map. lldb::addr_t base_addr; ///< Base address of the loaded object. lldb::addr_t path_addr; ///< String naming the shared object. lldb::addr_t dyn_addr; ///< Dynamic section of shared object. @@ -142,6 +157,7 @@ } void clear() { + link_addr = 0; base_addr = 0; path_addr = 0; dyn_addr = 0; @@ -194,6 +210,9 @@ /// Resolve(). SOEntryList m_removed_soentries; + /// Threading metadata read from the inferior. + ThreadInfo m_thread_info; + /// Reads an unsigned integer of @p size bytes from the inferior's address /// space starting at @p addr. /// @@ -232,6 +251,10 @@ /// supplied by the runtime linker. bool TakeSnapshot(SOEntryList &entry_list); + + enum PThreadField { eSize, eNElem, eOffset }; + + bool FindMetadata(const char *name, PThreadField field, uint32_t& value); }; #endif Index: source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp =================================================================== --- source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp +++ source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp @@ -14,6 +14,7 @@ #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" +#include "lldb/Symbol/Symbol.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" @@ -55,6 +56,8 @@ m_added_soentries(), m_removed_soentries() { + m_thread_info.valid = false; + // Cache a copy of the executable path if (m_process) { @@ -284,6 +287,8 @@ DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) { entry.clear(); + + entry.link_addr = addr; if (!(addr = ReadPointer(addr, &entry.base_addr))) return false; @@ -320,6 +325,51 @@ return true; } + +bool +DYLDRendezvous::FindMetadata(const char *name, PThreadField field, uint32_t& value) +{ + Target& target = m_process->GetTarget(); + + SymbolContextList list; + if (!target.GetImages().FindSymbolsWithNameAndType (ConstString(name), eSymbolTypeAny, list)) + return false; + + Address address = list[0].symbol->GetAddress(); + addr_t addr = address.GetLoadAddress (&target); + if (addr == LLDB_INVALID_ADDRESS) + return false; + + Error error; + value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(addr + field*sizeof(uint32_t), sizeof(uint32_t), 0, error); + if (error.Fail()) + return false; + + if (field == eSize) + value /= 8; // convert bits to bytes + + return true; +} + +const DYLDRendezvous::ThreadInfo& +DYLDRendezvous::GetThreadInfo() +{ + if (!m_thread_info.valid) + { + bool ok = true; + + ok &= FindMetadata ("_thread_db_pthread_dtvp", eOffset, m_thread_info.dtv_offset); + ok &= FindMetadata ("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size); + ok &= FindMetadata ("_thread_db_link_map_l_tls_modid", eOffset, m_thread_info.modid_offset); + ok &= FindMetadata ("_thread_db_dtv_t_pointer_val", eOffset, m_thread_info.tls_offset); + + if (ok) + m_thread_info.valid = true; + } + + return m_thread_info; +} + void DYLDRendezvous::DumpToLog(Log *log) const { Index: source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h =================================================================== --- source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h +++ source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -61,6 +61,9 @@ virtual lldb_private::Error CanLoadImage(); + virtual lldb::addr_t + GetThreadLocalData (const lldb::ModuleSP module, const lldb::ThreadSP thread); + //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ @@ -95,6 +98,9 @@ /// Rendezvous breakpoint. lldb::break_id_t m_dyld_bid; + /// Loaded module list. (link map for each module) + std::map> m_loaded_modules; + /// Enables a breakpoint on a function called by the runtime /// linker each time a module is loaded or unloaded. void @@ -117,10 +123,13 @@ /// /// @param module The module to traverse. /// + /// @param link_map_addr The virtual address of the link map for the @p module. + /// /// @param base_addr The virtual base address @p module is loaded at. void - UpdateLoadedSections(lldb::ModuleSP module, - lldb::addr_t base_addr = 0); + UpdateLoadedSections(lldb::ModuleSP module, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr); /// Removes the loaded sections from the target in @p module. /// @@ -131,7 +140,7 @@ /// Locates or creates a module given by @p file and updates/loads the /// resulting module at the virtual base address @p base_addr. lldb::ModuleSP - LoadModuleAtAddress(const lldb_private::FileSpec &file, lldb::addr_t base_addr); + LoadModuleAtAddress(const lldb_private::FileSpec &file, lldb::addr_t link_map_addr, lldb::addr_t base_addr); /// Resolves the entry point for the current inferior process and sets a /// breakpoint at that address. Index: source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp =================================================================== --- source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -123,7 +123,7 @@ { ModuleList module_list; module_list.Append(executable); - UpdateLoadedSections(executable, load_offset); + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset); LoadAllCurrentModules(); m_process->GetTarget().ModulesDidLoad(module_list); } @@ -144,7 +144,7 @@ { ModuleList module_list; module_list.Append(executable); - UpdateLoadedSections(executable, load_offset); + UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset); ProbeEntry(); m_process->GetTarget().ModulesDidLoad(module_list); } @@ -209,13 +209,15 @@ } void -DynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module, addr_t base_addr) +DynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module, addr_t link_map_addr, addr_t base_addr) { SectionLoadList &load_list = m_process->GetTarget().GetSectionLoadList(); const SectionList *sections = GetSectionListFromModule(module); assert(sections && "SectionList missing from loaded module."); + m_loaded_modules[module] = link_map_addr; + const size_t num_sections = sections->GetSize(); for (unsigned i = 0; i < num_sections; ++i) @@ -243,6 +245,8 @@ assert(sections && "SectionList missing from unloaded module."); + m_loaded_modules.erase(module); + const size_t num_sections = sections->GetSize(); for (size_t i = 0; i < num_sections; ++i) { @@ -337,7 +341,7 @@ for (I = m_rendezvous.loaded_begin(); I != E; ++I) { FileSpec file(I->path.c_str(), true); - ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + ModuleSP module_sp = LoadModuleAtAddress(file, I->link_addr, I->base_addr); if (module_sp.get()) { loaded_modules.AppendIfNeeded(module_sp); @@ -439,11 +443,17 @@ return; } + // The rendezvous class doesn't enumerate the main module, so track + // that ourselves here. + ModuleSP executable = GetTargetExecutable(); + m_loaded_modules[executable] = m_rendezvous.GetLinkMapAddress(); + + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { const char *module_path = I->path.c_str(); FileSpec file(module_path, false); - ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + ModuleSP module_sp = LoadModuleAtAddress(file, I->link_addr, I->base_addr); if (module_sp.get()) { module_list.Append(module_sp); @@ -461,7 +471,7 @@ } ModuleSP -DynamicLoaderPOSIXDYLD::LoadModuleAtAddress(const FileSpec &file, addr_t base_addr) +DynamicLoaderPOSIXDYLD::LoadModuleAtAddress(const FileSpec &file, addr_t link_map_addr, addr_t base_addr) { Target &target = m_process->GetTarget(); ModuleList &modules = target.GetImages(); @@ -470,11 +480,11 @@ ModuleSpec module_spec (file, target.GetArchitecture()); if ((module_sp = modules.FindFirstModule (module_spec))) { - UpdateLoadedSections(module_sp, base_addr); + UpdateLoadedSections(module_sp, link_map_addr, base_addr); } else if ((module_sp = target.GetSharedModule(module_spec))) { - UpdateLoadedSections(module_sp, base_addr); + UpdateLoadedSections(module_sp, link_map_addr, base_addr); } return module_sp; @@ -537,3 +547,68 @@ } return sections; } + +static int ReadInt(Process *process, addr_t addr) +{ + Error error; + int value = (int)process->ReadUnsignedIntegerFromMemory(addr, sizeof(uint32_t), 0, error); + if (error.Fail()) + return -1; + else + return value; +} + +static addr_t ReadPointer(Process *process, addr_t addr) +{ + Error error; + addr_t value = process->ReadPointerFromMemory(addr, error); + if (error.Fail()) + return LLDB_INVALID_ADDRESS; + else + return value; +} + +lldb::addr_t +DynamicLoaderPOSIXDYLD::GetThreadLocalData (const lldb::ModuleSP module, const lldb::ThreadSP thread) +{ + std::map::const_iterator it = m_loaded_modules.find (module); + if (it == m_loaded_modules.end()) + return LLDB_INVALID_ADDRESS; + + addr_t link_map = it->second; + if (link_map == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + const DYLDRendezvous::ThreadInfo &metadata = m_rendezvous.GetThreadInfo(); + if (!metadata.valid) + return LLDB_INVALID_ADDRESS; + + // Get the thread pointer. + addr_t tp = m_process->GetThreadPointer (thread); + if (tp == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + // Find the module's modid. + int modid = ReadInt (m_process, link_map + metadata.modid_offset); + if (modid == -1) + return LLDB_INVALID_ADDRESS; + + // Lookup the DTV stucture for this thread. + addr_t dtv_ptr = tp + metadata.dtv_offset; + addr_t dtv = ReadPointer (m_process, dtv_ptr); + if (dtv == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + // Find the TLS block for this module. + addr_t dtv_slot = dtv + metadata.dtv_slot_size*modid; + addr_t tls_block = ReadPointer (m_process, dtv_slot + metadata.tls_offset); + + Module *mod = module.get(); + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + if (log) + log->Printf("DynamicLoaderPOSIXDYLD::Performed TLS lookup: " + "module=%s, link_map=0x%" PRIx64 ", tp=0x%" PRIx64 ", modid=%i, tls_block=0x%" PRIx64 "\n", + mod->GetObjectName().AsCString(""), link_map, tp, modid, tls_block); + + return tls_block; +} Index: source/Plugins/Process/Linux/ProcessMonitor.h =================================================================== --- source/Plugins/Process/Linux/ProcessMonitor.h +++ source/Plugins/Process/Linux/ProcessMonitor.h @@ -147,6 +147,10 @@ bool WriteRegisterSet(lldb::tid_t tid, void *buf, size_t buf_size, unsigned int regset); + /// Reads the value of the thread-specific pointer for a given thread ID. + bool + ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value); + /// Writes a siginfo_t structure corresponding to the given thread ID to the /// memory region pointed to by @p siginfo. bool Index: source/Plugins/Process/Linux/ProcessMonitor.cpp =================================================================== --- source/Plugins/Process/Linux/ProcessMonitor.cpp +++ source/Plugins/Process/Linux/ProcessMonitor.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include // C++ Includes @@ -46,6 +47,19 @@ #ifndef PTRACE_SETREGSET #define PTRACE_SETREGSET 0x4205 #endif +#ifndef PTRACE_GET_THREAD_AREA + #define PTRACE_GET_THREAD_AREA 25 +#endif +#ifndef PTRACE_ARCH_PRCTL + #define PTRACE_ARCH_PRCTL 30 +#endif +#ifndef ARCH_GET_FS + #define ARCH_SET_GS 0x1001 + #define ARCH_SET_FS 0x1002 + #define ARCH_GET_FS 0x1003 + #define ARCH_GET_GS 0x1004 +#endif + // Support hardware breakpoints in case it has not been defined #ifndef TRAP_HWBKPT @@ -703,6 +717,74 @@ } //------------------------------------------------------------------------------ +/// @class ReadThreadPointerOperation +/// @brief Implements ProcessMonitor::ReadThreadPointer. +class ReadThreadPointerOperation : public Operation +{ +public: + ReadThreadPointerOperation(lldb::tid_t tid, lldb::addr_t *addr, bool &result) + : m_tid(tid), m_addr(addr), m_result(result) + { } + + void Execute(ProcessMonitor *monitor); + +private: + lldb::tid_t m_tid; + lldb::addr_t *m_addr; + bool &m_result; +}; + +void +ReadThreadPointerOperation::Execute(ProcessMonitor *monitor) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_REGISTERS)); + if (log) + log->Printf ("ProcessMonitor::%s()", __FUNCTION__); + + // The process for getting the thread area on Linux is + // somewhat... obscure. There's several different ways depending on + // what arch you're on, and what kernel version you have. + + const ArchSpec& arch = monitor->GetProcess().GetTarget().GetArchitecture(); + switch(arch.GetMachine()) + { + case llvm::Triple::x86: + { + // Find the GS register location for our host architecture. + size_t gs_user_offset = offsetof(struct user, regs); +#ifdef __x86_64__ + gs_user_offset += offsetof(struct user_regs_struct, gs); +#endif +#ifdef __i386__ + gs_user_offset += offsetof(struct user_regs_struct, xgs); +#endif + + // Read the GS register value to get the selector. + errno = 0; + long gs = PTRACE(PTRACE_PEEKUSER, m_tid, (void*)gs_user_offset, NULL, 0); + if (errno) + { + m_result = false; + break; + } + + // Read the LDT base for that selector. + uint32_t tmp[4]; + m_result = (PTRACE(PTRACE_GET_THREAD_AREA, m_tid, (void *)(gs >> 3), &tmp, 0) == 0); + *m_addr = tmp[1]; + break; + } + case llvm::Triple::x86_64: + // Read the FS register base. + m_result = (PTRACE(PTRACE_ARCH_PRCTL, m_tid, m_addr, (void *)ARCH_GET_FS, 0) == 0); + break; + default: + m_result = false; + break; + } +} + +//------------------------------------------------------------------------------ /// @class ResumeOperation /// @brief Implements ProcessMonitor::Resume. class ResumeOperation : public Operation @@ -2106,6 +2188,15 @@ } bool +ProcessMonitor::ReadThreadPointer(lldb::tid_t tid, lldb::addr_t &value) +{ + bool result; + ReadThreadPointerOperation op(tid, &value, result); + DoOperation(&op); + return result; +} + +bool ProcessMonitor::Resume(lldb::tid_t tid, uint32_t signo) { bool result; Index: source/Plugins/Process/POSIX/ProcessPOSIX.h =================================================================== --- source/Plugins/Process/POSIX/ProcessPOSIX.h +++ source/Plugins/Process/POSIX/ProcessPOSIX.h @@ -141,6 +141,9 @@ virtual lldb::addr_t GetImageInfoAddress(); + virtual lldb::addr_t + GetThreadPointer (const lldb::ThreadSP thread); + virtual size_t PutSTDIN(const char *buf, size_t len, lldb_private::Error &error); Index: source/Plugins/Process/POSIX/ProcessPOSIX.cpp =================================================================== --- source/Plugins/Process/POSIX/ProcessPOSIX.cpp +++ source/Plugins/Process/POSIX/ProcessPOSIX.cpp @@ -296,6 +296,17 @@ return LLDB_INVALID_ADDRESS; } +lldb::addr_t +ProcessPOSIX::GetThreadPointer (const lldb::ThreadSP thread) +{ + assert(m_monitor); + addr_t addr; + if (m_monitor->ReadThreadPointer (thread->GetID(), addr)) + return addr; + else + return LLDB_INVALID_ADDRESS; +} + Error ProcessPOSIX::DoHalt(bool &caused_stop) { Index: source/Plugins/Process/Utility/RegisterContextLLDB.cpp =================================================================== --- source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -1196,7 +1196,8 @@ DataExtractor dwarfdata (unwindplan_regloc.GetDWARFExpressionBytes(), unwindplan_regloc.GetDWARFExpressionLength(), process->GetByteOrder(), process->GetAddressByteSize()); - DWARFExpression dwarfexpr (dwarfdata, 0, unwindplan_regloc.GetDWARFExpressionLength()); + ModuleSP opcode_ctx; + DWARFExpression dwarfexpr (opcode_ctx, dwarfdata, 0, unwindplan_regloc.GetDWARFExpressionLength()); dwarfexpr.SetRegisterKind (unwindplan_registerkind); Value result; Error error; Index: source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -763,6 +763,8 @@ lldb::offset_t offset; const DWARFAbbreviationDeclaration* abbrevDecl = GetAbbreviationDeclarationPtr(dwarf2Data, cu, offset); + lldb::ModuleSP module = dwarf2Data->GetObjectFile()->GetModule(); + if (abbrevDecl) { const DataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); @@ -874,7 +876,7 @@ { uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); - frame_base->SetOpcodeData(debug_info_data, block_offset, block_length); + frame_base->SetOpcodeData(module, debug_info_data, block_offset, block_length); } else { @@ -884,7 +886,7 @@ size_t loc_list_length = DWARFLocationList::Size(debug_loc_data, debug_loc_offset); if (loc_list_length > 0) { - frame_base->SetOpcodeData(debug_loc_data, debug_loc_offset, loc_list_length); + frame_base->SetOpcodeData(module, debug_loc_data, debug_loc_offset, loc_list_length); if (lo_pc != LLDB_INVALID_ADDRESS) { assert (lo_pc >= cu->GetBaseAddress()); Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -1706,6 +1706,7 @@ const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); uint32_t member_idx = 0; BitfieldInfo last_field_info; + ModuleSP module = GetObjectFile()->GetModule(); for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) { @@ -1769,6 +1770,7 @@ NULL, // ClangExpressionVariableList * NULL, // ClangExpressionDeclMap * NULL, // RegisterContext * + module, debug_info_data, block_offset, block_length, @@ -2160,6 +2162,7 @@ NULL, NULL, NULL, + module, debug_info_data, block_offset, block_length, @@ -7268,6 +7271,7 @@ return var_sp; // Already been parsed! const dw_tag_t tag = die->Tag(); + ModuleSP module = GetObjectFile()->GetModule(); if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || @@ -7317,7 +7321,7 @@ // Retrieve the value as a block expression. uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); - location.CopyOpcodeData(debug_info_data, block_offset, block_length); + location.CopyOpcodeData(module, debug_info_data, block_offset, block_length); } else if (DWARFFormValue::IsDataForm(form_value.Form())) { @@ -7325,7 +7329,7 @@ const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes[form_value.Form()]; - location.CopyOpcodeData(debug_info_data, data_offset, data_length); + location.CopyOpcodeData(module, debug_info_data, data_offset, data_length); } else { @@ -7335,14 +7339,14 @@ const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes[form_value.Form()]; - location.CopyOpcodeData(debug_info_data, data_offset, data_length); + location.CopyOpcodeData(module, debug_info_data, data_offset, data_length); } else { const char *str = form_value.AsCString(&debug_info_data); uint32_t string_offset = str - (const char *)debug_info_data.GetDataStart(); uint32_t string_length = strlen(str) + 1; - location.CopyOpcodeData(debug_info_data, string_offset, string_length); + location.CopyOpcodeData(module, debug_info_data, string_offset, string_length); } } } @@ -7357,7 +7361,7 @@ uint32_t block_offset = form_value.BlockData() - debug_info_data.GetDataStart(); uint32_t block_length = form_value.Unsigned(); - location.CopyOpcodeData(get_debug_info_data(), block_offset, block_length); + location.CopyOpcodeData(module, get_debug_info_data(), block_offset, block_length); } else { @@ -7367,7 +7371,7 @@ size_t loc_list_length = DWARFLocationList::Size(debug_loc_data, debug_loc_offset); if (loc_list_length > 0) { - location.CopyOpcodeData(debug_loc_data, debug_loc_offset, loc_list_length); + location.CopyOpcodeData(module, debug_loc_data, debug_loc_offset, loc_list_length); assert (func_low_pc != LLDB_INVALID_ADDRESS); location.SetLocationListSlide (func_low_pc - dwarf_cu->GetBaseAddress()); } Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -1780,6 +1780,23 @@ return LLDB_INVALID_ADDRESS; } +lldb::addr_t +Process::GetThreadPointer (const lldb::ThreadSP thread) +{ + return LLDB_INVALID_ADDRESS; +} + +addr_t +Process::GetThreadLocalData (const ModuleSP module, const ThreadSP thread) +{ + DynamicLoader *loader = GetDynamicLoader(); + if (loader) + return loader->GetThreadLocalData (module, thread); + else + return LLDB_INVALID_ADDRESS; +} + + //---------------------------------------------------------------------- // LoadImage // Index: test/lang/c/tls_globals/Makefile =================================================================== --- /dev/null +++ test/lang/c/tls_globals/Makefile @@ -0,0 +1,11 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +CFLAGS_EXTRAS += -fPIC + +DYLIB_NAME := a +DYLIB_C_SOURCES := a.c + +LD_EXTRAS += -lpthread + +include $(LEVEL)/Makefile.rules Index: test/lang/c/tls_globals/TestTlsGlobals.py =================================================================== --- /dev/null +++ test/lang/c/tls_globals/TestTlsGlobals.py @@ -0,0 +1,88 @@ +"""Test that thread-local storage can be read correctly.""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class TlsGlobalTestCase(TestBase): + + mydir = os.path.join("lang", "c", "tls_globals") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym(self): + """Test thread-local storage.""" + self.buildDsym() + self.tls_globals() + + @dwarf_test + def test_with_dwarf(self): + """Test thread-local storage.""" + self.buildDwarf() + self.tls_globals() + + def setUp(self): + TestBase.setUp(self) + + if sys.platform.startswith("freebsd") or sys.platform.startswith("linux"): + # LD_LIBRARY_PATH must be set so the shared libraries are found on startup + if "LD_LIBRARY_PATH" in os.environ: + self.runCmd("settings set target.env-vars " + self.dylibPath + "=" + os.environ["LD_LIBRARY_PATH"] + ":" + os.getcwd()) + else: + self.runCmd("settings set target.env-vars " + self.dylibPath + "=" + os.getcwd()) + self.addTearDownHook(lambda: self.runCmd("settings remove target.env-vars " + self.dylibPath)) + + def tls_globals(self): + """Test thread-local storage.""" + + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + line1 = line_number('main.c', '// thread breakpoint') + lldbutil.run_break_set_by_file_and_line (self, "main.c", line1, num_expected_locations=1, loc_exact=True) + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.runCmd("process status", "Get process status") + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # BUG: sometimes lldb doesn't change threads to the stopped thread. + # (unrelated to this test). + self.runCmd("thread select 2", "Change thread") + + # Check that TLS evaluates correctly within the thread. + self.expect("expr var_static", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\(int\) \$.* = 88"]) + self.expect("expr var_shared", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\(int\) \$.* = 66"]) + + # Continue on the main thread + line2 = line_number('main.c', '// main breakpoint') + lldbutil.run_break_set_by_file_and_line (self, "main.c", line2, num_expected_locations=1, loc_exact=True) + self.runCmd("continue", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.runCmd("process status", "Get process status") + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # BUG: sometimes lldb doesn't change threads to the stopped thread. + # (unrelated to this test). + self.runCmd("thread select 1", "Change thread") + + # Check that TLS evaluates correctly within the main thread. + self.expect("expr var_static", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\(int\) \$.* = 44"]) + self.expect("expr var_shared", VARIABLES_DISPLAYED_CORRECTLY, + patterns = ["\(int\) \$.* = 33"]) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() Index: test/lang/c/tls_globals/a.c =================================================================== --- /dev/null +++ test/lang/c/tls_globals/a.c @@ -0,0 +1,18 @@ +//===-- a.c -----------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +__thread int var_shared = 33; + +void shared_check() +{ + var_shared *= 2; + usleep(1); // shared thread breakpoint +} Index: test/lang/c/tls_globals/main.c =================================================================== --- /dev/null +++ test/lang/c/tls_globals/main.c @@ -0,0 +1,36 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include +#include +#include + +void shared_check(); + +// Create some TLS storage within the static executable. +__thread int var_static = 44; + +void *fn_static(void *param) +{ + var_static *= 2; + shared_check(); + usleep(1); // thread breakpoint + for(;;) + usleep(1); +} + +int main (int argc, char const *argv[]) +{ + pthread_t handle; + pthread_create(&handle, NULL, &fn_static, NULL); + + for(;;) + usleep(1); // main breakpoint + + return 0; +}