Index: source/Plugins/ObjectFile/ELF/ObjectFileELF.h =================================================================== --- source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -17,6 +17,7 @@ #include "lldb/Host/FileSpec.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Core/UUID.h" +#include "lldb/Core/ArchSpec.h" #include "ELFHeader.h" @@ -48,6 +49,29 @@ Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset); }; +// ELF note owner definitions +#define LLDB_NT_OWNER_FREEBSD "FreeBSD" +#define LLDB_NT_OWNER_FREEBSD_LENGTH 8 + +#define LLDB_NT_OWNER_GNU "GNU" +#define LLDB_NT_OWNER_GNU_LENGTH 4 + +#define LLDB_NT_OWNER_NETBSD "NetBSD" +#define LLDB_NT_OWNER_NETBSD_LENGTH 7 + +// Name lengths: include trailing null + +// ELF note type definitions +#define LLDB_NT_FREEBSD_ABI_TAG 0x01 +#define LLDB_NT_GNU_ABI_TAG 0x01 +#define LLDB_NT_GNU_BUILD_ID_TAG 0x03 +#define LLDB_NT_NETBSD_ABI_TAG 0x01 + +// GNU ABI note OS constants +#define LLDB_NT_GNU_ABI_OS_LINUX 0x00 +#define LLDB_NT_GNU_ABI_OS_HURD 0x01 +#define LLDB_NT_GNU_ABI_OS_SOLARIS 0x02 + //------------------------------------------------------------------------------ /// @class ObjectFileELF /// @brief Generic ELF object file reader. @@ -242,6 +266,9 @@ /// Cached value of the entry point for this module. lldb_private::Address m_entry_point_address; + /// The architecture detected from parsing elf file contents. + lldb_private::ArchSpec m_arch_spec; + /// Returns a 1 based index of the given section header. size_t SectionIndex(const SectionHeaderCollIter &I); @@ -273,14 +300,15 @@ size_t ParseSectionHeaders(); - /// Parses the elf section headers and returns the uuid, debug link name, crc. + /// Parses the elf section headers and returns the uuid, debug link name, crc, archspec. static size_t GetSectionHeaderInfo(SectionHeaderColl §ion_headers, lldb_private::DataExtractor &data, const elf::ELFHeader &header, lldb_private::UUID &uuid, std::string &gnu_debuglink_file, - uint32_t &gnu_debuglink_crc); + uint32_t &gnu_debuglink_crc, + lldb_private::ArchSpec &arch_spec); /// Scans the dynamic section and locates all dependent modules (shared /// libraries) populating m_filespec_ap. This method will compute the @@ -407,6 +435,9 @@ unsigned PLTRelocationType(); + + static lldb_private::Error + RefineModuleDetailsFromNote (lldb_private::DataExtractor &data, lldb_private::ArchSpec &arch_spec, lldb_private::UUID &uuid); }; #endif // #ifndef liblldb_ObjectFileELF_h_ Index: source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp =================================================================== --- source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -448,6 +448,55 @@ return core_notes_crc; } +static const char* +OSABIAsCString (unsigned char osabi_byte) +{ +#define _MAKE_OSABI_CASE(x) case x: return #x + switch (osabi_byte) + { + _MAKE_OSABI_CASE(ELFOSABI_NONE); + _MAKE_OSABI_CASE(ELFOSABI_HPUX); + _MAKE_OSABI_CASE(ELFOSABI_NETBSD); + _MAKE_OSABI_CASE(ELFOSABI_GNU); + _MAKE_OSABI_CASE(ELFOSABI_HURD); + _MAKE_OSABI_CASE(ELFOSABI_SOLARIS); + _MAKE_OSABI_CASE(ELFOSABI_AIX); + _MAKE_OSABI_CASE(ELFOSABI_IRIX); + _MAKE_OSABI_CASE(ELFOSABI_FREEBSD); + _MAKE_OSABI_CASE(ELFOSABI_TRU64); + _MAKE_OSABI_CASE(ELFOSABI_MODESTO); + _MAKE_OSABI_CASE(ELFOSABI_OPENBSD); + _MAKE_OSABI_CASE(ELFOSABI_OPENVMS); + _MAKE_OSABI_CASE(ELFOSABI_NSK); + _MAKE_OSABI_CASE(ELFOSABI_AROS); + _MAKE_OSABI_CASE(ELFOSABI_FENIXOS); + _MAKE_OSABI_CASE(ELFOSABI_C6000_ELFABI); + _MAKE_OSABI_CASE(ELFOSABI_C6000_LINUX); + _MAKE_OSABI_CASE(ELFOSABI_ARM); + _MAKE_OSABI_CASE(ELFOSABI_STANDALONE); + default: + return ""; + } +#undef _MAKE_OSABI_CASE +} + +static bool +GetOsFromOSABI (unsigned char osabi_byte, llvm::Triple::OSType &ostype) +{ + switch (osabi_byte) + { + case ELFOSABI_AIX: ostype = llvm::Triple::OSType::AIX; break; + case ELFOSABI_FREEBSD: ostype = llvm::Triple::OSType::FreeBSD; break; + case ELFOSABI_GNU: ostype = llvm::Triple::OSType::Linux; break; + case ELFOSABI_NETBSD: ostype = llvm::Triple::OSType::NetBSD; break; + case ELFOSABI_OPENBSD: ostype = llvm::Triple::OSType::OpenBSD; break; + case ELFOSABI_SOLARIS: ostype = llvm::Triple::OSType::Solaris; break; + default: + ostype = llvm::Triple::OSType::UnknownOS; + } + return ostype != llvm::Triple::OSType::UnknownOS; +} + size_t ObjectFileELF::GetModuleSpecifications (const lldb_private::FileSpec& file, lldb::DataBufferSP& data_sp, @@ -456,6 +505,8 @@ lldb::offset_t length, lldb_private::ModuleSpecList &specs) { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES)); + const size_t initial_count = specs.GetSize(); if (ObjectFileELF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) @@ -474,13 +525,22 @@ LLDB_INVALID_CPUTYPE); if (spec.GetArchitecture().IsValid()) { - // We could parse the ABI tag information (in .note, .notes, or .note.ABI-tag) to get the - // machine information. However, this info isn't guaranteed to exist or be correct. Details: - // http://refspecs.linuxfoundation.org/LSB_1.2.0/gLSB/noteabitag.html - // Instead of passing potentially incorrect information down the pipeline, grab - // the host information and use it. - spec.GetArchitecture().GetTriple().setOSName (Host::GetOSString().GetCString()); - spec.GetArchitecture().GetTriple().setVendorName(Host::GetVendorString().GetCString()); + llvm::Triple::OSType ostype; + // First try to determine the OS type from the OSABI field in the elf header. + + if (log) + log->Printf ("ObjectFileELF::%s file '%s' module OSABI: %s", __FUNCTION__, file.GetPath ().c_str (), OSABIAsCString (header.e_ident[EI_OSABI])); + if (GetOsFromOSABI (header.e_ident[EI_OSABI], ostype) && ostype != llvm::Triple::OSType::UnknownOS) + { + spec.GetArchitecture ().GetTriple ().setOS (ostype); + + // Also clear the vendor so we don't end up with situations like + // x86_64-apple-FreeBSD. + spec.GetArchitecture ().GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); + + if (log) + log->Printf ("ObjectFileELF::%s file '%s' set ELF module OS type from ELF header OSABI.", __FUNCTION__, file.GetPath ().c_str ()); + } // Try to get the UUID from the section list. Usually that's at the end, so // map the file in if we don't have it already. @@ -495,8 +555,20 @@ std::string gnu_debuglink_file; SectionHeaderColl section_headers; lldb_private::UUID &uuid = spec.GetUUID(); - GetSectionHeaderInfo(section_headers, data, header, uuid, gnu_debuglink_file, gnu_debuglink_crc); + GetSectionHeaderInfo(section_headers, data, header, uuid, gnu_debuglink_file, gnu_debuglink_crc, spec.GetArchitecture ()); + + // If the module vendor is not set and the module OS matches this host OS, set the module vendor to the host vendor. + llvm::Triple &spec_triple = spec.GetArchitecture ().GetTriple (); + if (spec_triple.getVendor () == llvm::Triple::VendorType::UnknownVendor) + { + const llvm::Triple &host_triple = Host::GetArchitecture ().GetTriple (); + if (spec_triple.getOS () == host_triple.getOS ()) + spec_triple.setVendor (host_triple.getVendor ()); + } + + if (log) + log->Printf ("ObjectFileELF::%s file '%s' module set to triple: %s (architecture %s)", __FUNCTION__, file.GetPath ().c_str (), spec_triple.getTriple ().c_str (), spec.GetArchitecture ().GetArchitectureName ()); if (!uuid.IsValid()) { @@ -596,15 +668,19 @@ lldb::offset_t length) : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset), m_header(), + m_uuid(), + m_gnu_debuglink_file(), + m_gnu_debuglink_crc(0), m_program_headers(), m_section_headers(), - m_filespec_ap() + m_dynamic_symbols(), + m_filespec_ap(), + m_entry_point_address(), + m_arch_spec() { if (file) m_file = *file; ::memset(&m_header, 0, sizeof(m_header)); - m_gnu_debuglink_crc = 0; - m_gnu_debuglink_file.clear(); } ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp, @@ -613,9 +689,15 @@ addr_t header_addr) : ObjectFile(module_sp, process_sp, LLDB_INVALID_ADDRESS, data_sp), m_header(), + m_uuid(), + m_gnu_debuglink_file(), + m_gnu_debuglink_crc(0), m_program_headers(), m_section_headers(), - m_filespec_ap() + m_dynamic_symbols(), + m_filespec_ap(), + m_entry_point_address(), + m_arch_spec() { ::memset(&m_header, 0, sizeof(m_header)); } @@ -963,35 +1045,171 @@ return GetProgramHeaderInfo(m_program_headers, m_data, m_header); } -static bool -ParseNoteGNUBuildID(DataExtractor &data, lldb_private::UUID &uuid) +lldb_private::Error +ObjectFileELF::RefineModuleDetailsFromNote (lldb_private::DataExtractor &data, lldb_private::ArchSpec &arch_spec, lldb_private::UUID &uuid) { - // Try to parse the note section (ie .note.gnu.build-id|.notes|.note|...) and get the build id. - // BuildID documentation: https://fedoraproject.org/wiki/Releases/FeatureBuildId + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES)); + Error error; + lldb::offset_t offset = 0; - static const uint32_t g_gnu_build_id = 3; // NT_GNU_BUILD_ID from elf.h while (true) { + // Parse the note header. If this fails, bail out. ELFNote note = ELFNote(); if (!note.Parse(data, &offset)) - return false; + { + // We're done. + return error; + } + + // If a tag processor handles the tag, it should set processed to true, and + // the loop will assume the tag processing has moved entirely past the note's payload. + // Otherwise, leave it false and the end of the loop will handle the offset properly. + bool processed = false; - // 16 bytes is UUID|MD5, 20 bytes is SHA1 - if (note.n_name == "GNU" && (note.n_type == g_gnu_build_id) && - (note.n_descsz == 16 || note.n_descsz == 20)) + if (log) + log->Printf ("ObjectFileELF::%s parsing note name='%s', type=%" PRIu32, __FUNCTION__, note.n_name.c_str (), note.n_type); + + // Process FreeBSD ELF notes. + if (note.n_namesz == LLDB_NT_OWNER_FREEBSD_LENGTH && note.n_name == LLDB_NT_OWNER_FREEBSD) { - uint8_t uuidbuf[20]; - if (data.GetU8 (&offset, &uuidbuf, note.n_descsz) == NULL) - return false; - uuid.SetBytes (uuidbuf, note.n_descsz); - return true; + switch (note.n_type) + { + case LLDB_NT_FREEBSD_ABI_TAG: + { + // We'll consume the payload below. + processed = true; + + // Pull out the min version info. + uint32_t version_info; + if (data.GetU32 (&offset, &version_info, 1) == nullptr) + { + error.SetErrorString ("failed to read FreeBSD ABI note payload"); + return error; + } + + // Set the elf OS version to FreeBSD. Also clear the vendor. + arch_spec.GetTriple ().setOS (llvm::Triple::OSType::FreeBSD); + arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); + + if (log) + log->Printf ("ObjectFileELF::%s detected FreeBSD, min version constant %" PRIu32, __FUNCTION__, version_info); + } + break; + } } - offset += llvm::RoundUpToAlignment(note.n_descsz, 4); + // Process GNU ELF notes. + else if (note.n_namesz == LLDB_NT_OWNER_GNU_LENGTH && note.n_name == LLDB_NT_OWNER_GNU) + { + switch (note.n_type) + { + case LLDB_NT_GNU_ABI_TAG: + { + // We'll consume the payload below. + processed = true; + + // Pull out the min OS version supporting the ABI. + uint32_t version_info[4]; + if (data.GetU32 (&offset, &version_info[0], note.n_descsz / 4) == nullptr) + { + error.SetErrorString ("failed to read GNU ABI note payload"); + return error; + } + + // Set the OS per the OS field. + switch (version_info[0]) + { + case LLDB_NT_GNU_ABI_OS_LINUX: + arch_spec.GetTriple ().setOS (llvm::Triple::OSType::Linux); + arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); + if (log) + log->Printf ("ObjectFileELF::%s detected Linux, min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[1], version_info[2], version_info[3]); + // FIXME we have the minimal version number, we could be propagating that. version_info[1] = OS Major, version_info[2] = OS Minor, version_info[3] = Revision. + break; + case LLDB_NT_GNU_ABI_OS_HURD: + arch_spec.GetTriple ().setOS (llvm::Triple::OSType::UnknownOS); + arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); + if (log) + log->Printf ("ObjectFileELF::%s detected Hurd (unsupported), min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[1], version_info[2], version_info[3]); + break; + case LLDB_NT_GNU_ABI_OS_SOLARIS: + arch_spec.GetTriple ().setOS (llvm::Triple::OSType::Solaris); + arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); + if (log) + log->Printf ("ObjectFileELF::%s detected Solaris, min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[1], version_info[2], version_info[3]); + break; + default: + if (log) + log->Printf ("ObjectFileELF::%s unrecognized OS in note, id %" PRIu32 ", min version %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_info[0], version_info[1], version_info[2], version_info[3]); + break; + } + } + break; + + case LLDB_NT_GNU_BUILD_ID_TAG: + // Only bother processing this if we don't already have the uuid set. + if (!uuid.IsValid()) + { + // We'll consume the payload below. + processed = true; + + // 16 bytes is UUID|MD5, 20 bytes is SHA1 + if ((note.n_descsz == 16 || note.n_descsz == 20)) + { + uint8_t uuidbuf[20]; + if (data.GetU8 (&offset, &uuidbuf, note.n_descsz) == nullptr) + { + error.SetErrorString ("failed to read GNU_BUILD_ID note payload"); + return error; + } + + // Save the build id as the UUID for the module. + uuid.SetBytes (uuidbuf, note.n_descsz); + } + } + break; + } + } + // Process NetBSD ELF notes. + else if (note.n_namesz == LLDB_NT_OWNER_NETBSD_LENGTH && note.n_name == LLDB_NT_OWNER_NETBSD) + { + switch (note.n_type) + { + case LLDB_NT_NETBSD_ABI_TAG: + { + + // We'll consume the payload below. + processed = true; + + // Pull out the min version info. + uint32_t version_info; + if (data.GetU32 (&offset, &version_info, 1) == nullptr) + { + error.SetErrorString ("failed to read NetBSD ABI note payload"); + return error; + } + + // Set the elf OS version to NetBSD. Also clear the vendor. + arch_spec.GetTriple ().setOS (llvm::Triple::OSType::NetBSD); + arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); + + if (log) + log->Printf ("ObjectFileELF::%s detected NetBSD, min version constant %" PRIu32, __FUNCTION__, version_info); + } + break; + } + } + // Add other OS version notes here. + + if (!processed) + offset += llvm::RoundUpToAlignment(note.n_descsz, 4); } - return false; + + return error; } + //---------------------------------------------------------------------- // GetSectionHeaderInfo //---------------------------------------------------------------------- @@ -1001,8 +1219,18 @@ const elf::ELFHeader &header, lldb_private::UUID &uuid, std::string &gnu_debuglink_file, - uint32_t &gnu_debuglink_crc) + uint32_t &gnu_debuglink_crc, + ArchSpec &arch_spec) { + // Only intialize the arch_spec to okay defaults if they're not already set. + // We'll refine this with note data as we parse the notes. + if (arch_spec.GetTriple ().getOS () == llvm::Triple::OSType::UnknownOS) + { + arch_spec.SetArchitecture (eArchTypeELF, header.e_machine, LLDB_INVALID_CPUTYPE); + arch_spec.GetTriple().setOSName (Host::GetOSString().GetCString()); + arch_spec.GetTriple().setVendorName(Host::GetVendorString().GetCString()); + } + // We have already parsed the section headers if (!section_headers.empty()) return section_headers.size(); @@ -1011,6 +1239,8 @@ if (header.e_shnum == 0) return 0; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MODULES)); + section_headers.resize(header.e_shnum); if (section_headers.size() != header.e_shnum) return 0; @@ -1063,12 +1293,19 @@ } } - if (header.sh_type == SHT_NOTE && !uuid.IsValid()) + // Process ELF note section entries. + if (header.sh_type == SHT_NOTE) { + // Allow notes to refine module info. DataExtractor data; if (section_size && (data.SetData (object_data, header.sh_offset, section_size) == section_size)) { - ParseNoteGNUBuildID (data, uuid); + Error error = RefineModuleDetailsFromNote (data, arch_spec, uuid); + if (error.Fail ()) + { + if (log) + log->Printf ("ObjectFileELF::%s ELF note processing failed: %s", __FUNCTION__, error.AsCString ()); + } } } } @@ -1114,7 +1351,7 @@ size_t ObjectFileELF::ParseSectionHeaders() { - return GetSectionHeaderInfo(m_section_headers, m_data, m_header, m_uuid, m_gnu_debuglink_file, m_gnu_debuglink_crc); + return GetSectionHeaderInfo(m_section_headers, m_data, m_header, m_uuid, m_gnu_debuglink_file, m_gnu_debuglink_crc, m_arch_spec); } const ObjectFileELF::ELFSectionHeaderInfo * @@ -2309,12 +2546,10 @@ bool ObjectFileELF::GetArchitecture (ArchSpec &arch) { - if (!ParseHeader()) + if (!ParseHeader() || !ParseSectionHeaders()) return false; - arch.SetArchitecture (eArchTypeELF, m_header.e_machine, LLDB_INVALID_CPUTYPE); - arch.GetTriple().setOSName (Host::GetOSString().GetCString()); - arch.GetTriple().setVendorName(Host::GetVendorString().GetCString()); + arch = m_arch_spec; return true; }