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" @@ -242,6 +243,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 +277,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 +412,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 @@ -41,6 +41,23 @@ using namespace llvm::ELF; namespace { + +// ELF note owner definitions +const char *const LLDB_NT_OWNER_FREEBSD = "FreeBSD"; +const char *const LLDB_NT_OWNER_GNU = "GNU"; +const char *const LLDB_NT_OWNER_NETBSD = "NetBSD"; + +// ELF note type definitions +const elf_word LLDB_NT_FREEBSD_ABI_TAG = 0x01; +const elf_word LLDB_NT_GNU_ABI_TAG = 0x01; +const elf_word LLDB_NT_GNU_BUILD_ID_TAG = 0x03; +const elf_word LLDB_NT_NETBSD_ABI_TAG = 0x01; + +// GNU ABI note OS constants +const elf_word LLDB_NT_GNU_ABI_OS_LINUX = 0x00; +const elf_word LLDB_NT_GNU_ABI_OS_HURD = 0x01; +const elf_word LLDB_NT_GNU_ABI_OS_SOLARIS = 0x02; + //===----------------------------------------------------------------------===// /// @class ELFRelocation /// @brief Generic wrapper for ELFRel and ELFRela. @@ -448,6 +465,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 +522,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,14 +542,23 @@ 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. size_t section_header_end = header.e_shoff + header.e_shnum * header.e_shentsize; @@ -495,9 +572,21 @@ 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()) { uint32_t core_notes_crc = 0; @@ -596,15 +685,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 +706,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 +1062,170 @@ 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; + } - // 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 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; + + 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_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; + if (note.n_type == 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; + } + + // Convert the version info into a major/minor number. + const uint32_t version_major = version_info / 100000; + const uint32_t version_minor = (version_info / 1000) % 100; + + char os_name[32]; + snprintf (os_name, sizeof (os_name), "freebsd%" PRIu32 ".%" PRIu32, version_major, version_minor); + + // Set the elf OS version to FreeBSD. Also clear the vendor. + arch_spec.GetTriple ().setOSName (os_name); + arch_spec.GetTriple ().setVendor (llvm::Triple::VendorType::UnknownVendor); + + if (log) + log->Printf ("ObjectFileELF::%s detected FreeBSD %" PRIu32 ".%" PRIu32 ".%" PRIu32, __FUNCTION__, version_major, version_minor, static_cast (version_info % 1000)); + } } - offset += llvm::RoundUpToAlignment(note.n_descsz, 4); + // Process GNU ELF notes. + else if (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_name == LLDB_NT_OWNER_NETBSD) + { + if (note.n_type == 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); + } + } + // Add other OS version notes here. + + if (!processed) + offset += llvm::RoundUpToAlignment(note.n_descsz, 4); } - return false; + + return error; } + //---------------------------------------------------------------------- // GetSectionHeaderInfo //---------------------------------------------------------------------- @@ -1001,8 +1235,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 +1255,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 +1309,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 +1367,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 * @@ -2312,9 +2565,10 @@ if (!ParseHeader()) return false; - arch.SetArchitecture (eArchTypeELF, m_header.e_machine, LLDB_INVALID_CPUTYPE); - arch.GetTriple().setOSName (Host::GetOSString().GetCString()); - arch.GetTriple().setVendorName(Host::GetVendorString().GetCString()); + // Allow elf notes to be parsed which may affect the detected architecture. + ParseSectionHeaders(); + + arch = m_arch_spec; return true; }