Fix ObjectFileELF to determine architectures independent of host.
authorTodd Fiala <todd.fiala@gmail.com>
Fri, 27 Jun 2014 16:52:49 +0000 (16:52 +0000)
committerTodd Fiala <todd.fiala@gmail.com>
Fri, 27 Jun 2014 16:52:49 +0000 (16:52 +0000)
Previously ObjectFileELF was simplifying and assuming the object file it was
looking at was the same as the host architecture/triple.  This would break
attempts to run, say, lldb on MacOSX against lldb-gdbserver on Linux since
the MacOSX lldb would say that the linux elf file was really an Apple MacOSX
architecture.  Chaos would ensue.

This change allows the elf file to parse ELF notes for Linux, FreeBSD and
NetBSD, and determine the OS appropriately from them.  It also initializes
the OS type from the ELF header OSABI if it is set (which it is for FreeBSD
but not for Linux).

Added a test with freebsd and linux images that verify that
'(lldb) image list -t -A' prints out the expected architecture for each.

llvm-svn: 211907

lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
lldb/test/functionalities/object-file/TestImageListMultiArchitecture.py [new file with mode: 0644]
lldb/test/functionalities/object-file/ls-freebsd-10.0-x86_64 [new file with mode: 0755]
lldb/test/functionalities/object-file/sleep-ubuntu-14.04-x86_64 [new file with mode: 0755]

index bd733c8..b2119e3 100644 (file)
@@ -41,6 +41,29 @@ using namespace elf;
 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_FREEBSD_ABI_SIZE = 4;
+
+const elf_word LLDB_NT_GNU_ABI_TAG      = 0x01;
+const elf_word LLDB_NT_GNU_ABI_SIZE     = 16;
+
+const elf_word LLDB_NT_GNU_BUILD_ID_TAG = 0x03;
+
+const elf_word LLDB_NT_NETBSD_ABI_TAG   = 0x01;
+const elf_word LLDB_NT_NETBSD_ABI_SIZE  = 4;
+
+// 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 +471,55 @@ ObjectFileELF::CalculateELFNotesSegmentsCRC32 (const ProgramHeaderColl& program_
     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 "<unknown-osabi>";
+    }
+#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 +528,8 @@ ObjectFileELF::GetModuleSpecifications (const lldb_private::FileSpec& file,
                                         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 +548,22 @@ ObjectFileELF::GetModuleSpecifications (const lldb_private::FileSpec& file,
                                                        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 +578,20 @@ ObjectFileELF::GetModuleSpecifications (const lldb_private::FileSpec& file,
                     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 +691,19 @@ ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp,
                               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 +712,15 @@ ObjectFileELF::ObjectFileELF (const lldb::ModuleSP &module_sp,
                               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 +1068,168 @@ ObjectFileELF::ParseProgramHeaders()
     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;
+
+        if (log)
+            log->Printf ("ObjectFileELF::%s parsing note name='%s', type=%" PRIu32, __FUNCTION__, note.n_name.c_str (), note.n_type);
 
-        // 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))
+        // Process FreeBSD ELF notes.
+        if ((note.n_name == LLDB_NT_OWNER_FREEBSD) &&
+            (note.n_type == LLDB_NT_FREEBSD_ABI_TAG) &&
+            (note.n_descsz == LLDB_NT_FREEBSD_ABI_SIZE))
         {
-            uint8_t uuidbuf[20]; 
-            if (data.GetU8 (&offset, &uuidbuf, note.n_descsz) == NULL)
-                return false;
-            uuid.SetBytes (uuidbuf, note.n_descsz);
-            return true;
+            // 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<uint32_t> (version_info % 1000));
+        }
+        // Process GNU ELF notes.
+        else if (note.n_name == LLDB_NT_OWNER_GNU)
+        {
+            switch (note.n_type)
+            {
+                case LLDB_NT_GNU_ABI_TAG:
+                    if (note.n_descsz == LLDB_NT_GNU_ABI_SIZE)
+                    {
+                        // 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) &&
+                 (note.n_type == LLDB_NT_NETBSD_ABI_TAG) &&
+                 (note.n_descsz == LLDB_NT_NETBSD_ABI_SIZE))
+        {
+
+            // 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);
         }
-        offset += llvm::RoundUpToAlignment(note.n_descsz, 4);
+
+        if (!processed)
+            offset += llvm::RoundUpToAlignment(note.n_descsz, 4);
     }
-    return false;
+
+    return error;
 }
 
+
 //----------------------------------------------------------------------
 // GetSectionHeaderInfo
 //----------------------------------------------------------------------
@@ -1001,8 +1239,18 @@ ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
                                     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 +1259,8 @@ ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
     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 +1313,19 @@ ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
                     }
                 }
 
-                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 +1371,7 @@ ObjectFileELF::GetSegmentDataByIndex(lldb::user_id_t id)
 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 +2569,10 @@ ObjectFileELF::GetArchitecture (ArchSpec &arch)
     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;
 }
 
index ac7d38c..6b036af 100644 (file)
@@ -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 @@ private:
     /// 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 @@ private:
     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 &section_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 @@ private:
         
     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_
diff --git a/lldb/test/functionalities/object-file/TestImageListMultiArchitecture.py b/lldb/test/functionalities/object-file/TestImageListMultiArchitecture.py
new file mode 100644 (file)
index 0000000..23a9f2b
--- /dev/null
@@ -0,0 +1,41 @@
+"""
+Test lldb 'image list' on object files across multiple architectures.
+This exercises classes like ObjectFileELF and their support for opening
+foreign-architecture object files.
+"""
+
+# import os
+import os.path
+import unittest2
+import lldb
+from lldbtest import *
+import lldbutil
+import re
+
+class TestImageListMultiArchitecture(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+
+    def test_image_list_shows_multiple_architectures(self):
+        """Test that image list properly shows the correct architecture for a set of different architecture object files."""
+        images = {
+            "ls-freebsd-10.0-x86_64": re.compile(r"x86_64-(unknown)?-freebsd10.0 x86_64"),
+            "sleep-ubuntu-14.04-x86_64": re.compile(r"x86_64-(unknown)?-linux x86_64"),
+        }
+
+        for image_name in images:
+            file_name = os.path.abspath(os.path.join(os.path.dirname(__file__), image_name))
+            expected_triple_and_arch_regex = images[image_name]
+
+            self.runCmd("file {}".format(file_name))
+            self.match("image list -t -A", [expected_triple_and_arch_regex])
+
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()
diff --git a/lldb/test/functionalities/object-file/ls-freebsd-10.0-x86_64 b/lldb/test/functionalities/object-file/ls-freebsd-10.0-x86_64
new file mode 100755 (executable)
index 0000000..81605fc
Binary files /dev/null and b/lldb/test/functionalities/object-file/ls-freebsd-10.0-x86_64 differ
diff --git a/lldb/test/functionalities/object-file/sleep-ubuntu-14.04-x86_64 b/lldb/test/functionalities/object-file/sleep-ubuntu-14.04-x86_64
new file mode 100755 (executable)
index 0000000..bdfa0f5
Binary files /dev/null and b/lldb/test/functionalities/object-file/sleep-ubuntu-14.04-x86_64 differ