Index: lldb/trunk/lit/Modules/ELF/PT_LOAD-empty.yaml =================================================================== --- lldb/trunk/lit/Modules/ELF/PT_LOAD-empty.yaml +++ lldb/trunk/lit/Modules/ELF/PT_LOAD-empty.yaml @@ -0,0 +1,26 @@ +# Test behavior on unusual (and probably corrupt) files. Test that we drop an +# empty PT_LOAD segment. + +# RUN: yaml2obj %s > %t +# RUN: lldb-test object-file %t | FileCheck %s +# +# CHECK-NOT: container + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + Align: 0x4 Index: lldb/trunk/lit/Modules/ELF/PT_LOAD-overlap-PT_INTERP.yaml =================================================================== --- lldb/trunk/lit/Modules/ELF/PT_LOAD-overlap-PT_INTERP.yaml +++ lldb/trunk/lit/Modules/ELF/PT_LOAD-overlap-PT_INTERP.yaml @@ -0,0 +1,50 @@ +# Test that an overlapping PT_INTERP segment does not cause us to drop the +# subsequent PT_LOAD segment. + +# RUN: yaml2obj %s > %t +# RUN: lldb-test object-file %t | FileCheck %s + +# CHECK: Index: 0 +# CHECK-NEXT: ID: 0xfffffffffffffffe +# CHECK-NEXT: Name: PT_LOAD[0] +# CHECK-NEXT: Type: container +# CHECK-NEXT: Permissions: rwx +# CHECK-NEXT: Thread specific: no +# CHECK-NEXT: VM address: 0x1000 +# CHECK-NEXT: VM size: 12 +# CHECK-NEXT: File size: 12 +# CHECK-NEXT: Showing 2 subsections + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_ARM +Sections: + - Name: .interp + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1000 + AddressAlign: 0x4 + Content: 3232 + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1004 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D +ProgramHeaders: + - Type: PT_INTERP + Flags: [ PF_R ] + VAddr: 0x1000 + Align: 0x4 + Sections: + - Section: .interp + - Type: PT_LOAD + Flags: [ PF_X, PF_W, PF_R ] + VAddr: 0x1000 + Align: 0x4 + Sections: + - Section: .interp + - Section: .text Index: lldb/trunk/lit/Modules/ELF/PT_LOAD-overlap-section.yaml =================================================================== --- lldb/trunk/lit/Modules/ELF/PT_LOAD-overlap-section.yaml +++ lldb/trunk/lit/Modules/ELF/PT_LOAD-overlap-section.yaml @@ -0,0 +1,66 @@ +# Test behavior on strange (invalid?) elf files where the sections cross segment +# boundary. The test input was generated from this yaml file, but the program +# header was modified by hand, so this input is here for reference only. +# +# Right now lldb shortens sections to make sure every section is fully contained +# within a segment, but other behaviors are possible too (including outright +# rejecting such files). + +# RUN: lldb-test object-file %S/Inputs/PT_LOAD-overlap-section.elf | FileCheck %s + +# CHECK: Index: 0 +# CHECK-NEXT: ID: 0xffffffffffffffff +# CHECK-NEXT: Name: PT_LOAD[0] +# CHECK-NEXT: Type: container +# CHECK-NEXT: Permissions: rwx +# CHECK-NEXT: Thread specific: no +# CHECK-NEXT: VM address: 0x1006 +# CHECK-NEXT: VM size: 8 +# CHECK-NEXT: File size: 8 +# CHECK-NEXT: Showing 1 subsections +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: ID: 0x2 +# CHECK-NEXT: Name: .text +# CHECK-NEXT: Type: code +# CHECK-NEXT: Permissions: r-x +# CHECK-NEXT: Thread specific: no +# CHECK-NEXT: VM address: 0x1008 +# CHECK-NEXT: VM size: 6 +# CHECK-NEXT: File size: 8 + +# CHECK: Index: 1 +# CHECK-NEXT: ID: 0x1 +# CHECK-NEXT: Name: .interp +# CHECK-NEXT: Type: regular +# CHECK-NEXT: Permissions: r-- +# CHECK-NEXT: Thread specific: no +# CHECK-NEXT: VM address: 0x1000 +# CHECK-NEXT: VM size: 6 +# CHECK-NEXT: File size: 8 + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_ARM +Sections: + - Name: .interp + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1000 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1008 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_W, PF_R ] + VAddr: 0x1008 # Modified by hand to start at 0x1006 + Align: 0x4 + Sections: + - Section: .text Index: lldb/trunk/lit/Modules/ELF/PT_LOAD-overlap.yaml =================================================================== --- lldb/trunk/lit/Modules/ELF/PT_LOAD-overlap.yaml +++ lldb/trunk/lit/Modules/ELF/PT_LOAD-overlap.yaml @@ -0,0 +1,42 @@ +# Test behavior on unusual (and probably corrupt) object files. Check that we +# drop the second PT_LOAD segment which overlaps with the first one. + +# RUN: yaml2obj %s > %t +# RUN: lldb-test object-file %t | FileCheck %s + +# CHECK: Type: container +# CHECK-NOT: Type: container + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1008 + AddressAlign: 0x4 + Content: 3232 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_W, PF_R ] + VAddr: 0x1000 + Align: 0x4 + Sections: + - Section: .text + - Section: .data + - Type: PT_LOAD + Flags: [ PF_R, PF_W ] + VAddr: 0x1008 + Align: 0x4 + Sections: + - Section: .data Index: lldb/trunk/lit/Modules/ELF/PT_LOAD.yaml =================================================================== --- lldb/trunk/lit/Modules/ELF/PT_LOAD.yaml +++ lldb/trunk/lit/Modules/ELF/PT_LOAD.yaml @@ -0,0 +1,79 @@ +# Test that sections are made child-sections of the PT_LOAD segments/sections. + +# RUN: yaml2obj %s > %t +# RUN: lldb-test object-file %t | FileCheck %s + +# CHECK: Index: 0 +# CHECK-NEXT: ID: 0xffffffffffffffff +# CHECK-NEXT: Name: PT_LOAD[0] +# CHECK-NEXT: Type: container +# CHECK-NEXT: Permissions: rwx +# CHECK-NEXT: Thread specific: no +# CHECK-NEXT: VM address: 0x1000 +# CHECK-NEXT: VM size: 10 +# CHECK-NEXT: File size: 10 +# CHECK-NEXT: Showing 2 subsections +# CHECK-NEXT: Index: 0 +# CHECK-NEXT: ID: 0x1 +# CHECK-NEXT: Name: .text +# CHECK-NEXT: Type: code +# CHECK-NEXT: Permissions: r-x +# CHECK-NEXT: Thread specific: no +# CHECK-NEXT: VM address: 0x1000 +# CHECK-NEXT: VM size: 8 +# CHECK-NEXT: File size: 8 +# CHECK-EMPTY: +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: ID: 0x2 +# CHECK-NEXT: Name: .data +# CHECK-NEXT: Type: data +# CHECK-NEXT: Permissions: r-- +# CHECK-NEXT: Thread specific: no +# CHECK-NEXT: VM address: 0x1008 +# CHECK-NEXT: VM size: 2 +# CHECK-NEXT: File size: 2 +# CHECK-EMPTY: +# CHECK-EMPTY: +# CHECK-NEXT: Index: 1 +# CHECK-NEXT: ID: 0x3 +# CHECK-NEXT: Name: .data_outside +# CHECK-NEXT: Type: regular +# CHECK-NEXT: Permissions: r-- +# CHECK-NEXT: Thread specific: no +# CHECK-NEXT: VM address: 0x2000 +# CHECK-NEXT: VM size: 2 +# CHECK-NEXT: File size: 2 + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1008 + AddressAlign: 0x4 + Content: 3232 + - Name: .data_outside + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x2000 + AddressAlign: 0x4 + Content: 3232 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_W, PF_R ] + VAddr: 0x1000 + Align: 0x4 + Sections: + - Section: .text + - Section: .data Index: lldb/trunk/lit/Modules/ELF/section-overlap.yaml =================================================================== --- lldb/trunk/lit/Modules/ELF/section-overlap.yaml +++ lldb/trunk/lit/Modules/ELF/section-overlap.yaml @@ -0,0 +1,51 @@ +# Test handling of (corrupt?) object files, which have sections with overlapping +# virtual addresses. +# +# Right now the overlapping sections get dropped, but other behaviors +# (including outright rejecting such files) are possible too. + +# RUN: yaml2obj %s > %t +# RUN: lldb-test object-file %t | FileCheck %s + +# CHECK-NOT: .overlap1 +# CHECK-NOT: .overlap2 + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_ARM +Sections: + - Name: .sect1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1000 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D + - Name: .overlap1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1004 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D + - Name: .sect2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x2000 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D + - Name: .overlap2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x2004 + AddressAlign: 0x4 + Content: DEADBEEFBAADF00D +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_W, PF_R ] + VAddr: 0x1000 + Align: 0x4 + Sections: + - Section: .sect1 + - Section: .overlap1 Index: lldb/trunk/lit/Modules/ELF/section-permissions.yaml =================================================================== --- lldb/trunk/lit/Modules/ELF/section-permissions.yaml +++ lldb/trunk/lit/Modules/ELF/section-permissions.yaml @@ -14,7 +14,7 @@ FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB - Type: ET_DYN + Type: ET_REL Machine: EM_X86_64 Entry: 0x00000000000007A0 Sections: Index: lldb/trunk/lit/Modules/ELF/section-types.yaml =================================================================== --- lldb/trunk/lit/Modules/ELF/section-types.yaml +++ lldb/trunk/lit/Modules/ELF/section-types.yaml @@ -35,7 +35,7 @@ FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB - Type: ET_DYN + Type: ET_REL Machine: EM_X86_64 Entry: 0x00000000000007A0 Sections: Index: lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp =================================================================== --- lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ lldb/trunk/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -17,6 +17,7 @@ #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/RangeMap.h" #include "lldb/Core/Section.h" #include "lldb/Host/FileSystem.h" #include "lldb/Symbol/DWARFCallFrameInfo.h" @@ -30,6 +31,7 @@ #include "lldb/Utility/Stream.h" #include "lldb/Utility/Timer.h" +#include "llvm/ADT/IntervalMap.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include "llvm/Object/Decompressor.h" @@ -236,6 +238,8 @@ } // end anonymous namespace +static user_id_t SegmentID(size_t PHdrIndex) { return ~PHdrIndex; } + bool ELFNote::Parse(const DataExtractor &data, lldb::offset_t *offset) { // Read all fields. if (data.GetU32(offset, &n_namesz, 3) == NULL) @@ -822,7 +826,8 @@ // Iterate through the object file sections to find all of the sections // that have SHF_ALLOC in their flag bits. SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); - if (section_sp && section_sp->Test(SHF_ALLOC)) { + if (section_sp->Test(SHF_ALLOC) || + section_sp->GetType() == eSectionTypeContainer) { lldb::addr_t load_addr = section_sp->GetFileAddress(); // We don't want to update the load address of a section with type // eSectionTypeAbsoluteAddress as they already have the absolute load @@ -1821,74 +1826,185 @@ return Perm; } +static Permissions GetPermissions(const ELFProgramHeader &H) { + Permissions Perm = Permissions(0); + if (H.p_flags & PF_R) + Perm |= ePermissionsReadable; + if (H.p_flags & PF_W) + Perm |= ePermissionsWritable; + if (H.p_flags & PF_X) + Perm |= ePermissionsExecutable; + return Perm; +} + namespace { + +using VMRange = lldb_private::Range; + +struct SectionAddressInfo { + SectionSP Segment; + VMRange Range; +}; + // (Unlinked) ELF object files usually have 0 for every section address, meaning // we need to compute synthetic addresses in order for "file addresses" from // different sections to not overlap. This class handles that logic. class VMAddressProvider { - bool m_synthesizing; - addr_t m_next; + using VMMap = llvm::IntervalMap>; + + ObjectFile::Type ObjectType; + addr_t NextVMAddress = 0; + VMMap::Allocator Alloc; + VMMap Segments = VMMap(Alloc); + VMMap Sections = VMMap(Alloc); + lldb_private::Log *Log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_MODULES); + + VMRange GetVMRange(const ELFSectionHeader &H) { + addr_t Address = H.sh_addr; + addr_t Size = H.sh_flags & SHF_ALLOC ? H.sh_size : 0; + if (ObjectType == ObjectFile::Type::eTypeObjectFile && Segments.empty() && (H.sh_flags & SHF_ALLOC)) { + NextVMAddress = + llvm::alignTo(NextVMAddress, std::max(H.sh_addralign, 1)); + Address = NextVMAddress; + NextVMAddress += Size; + } + return VMRange(Address, Size); + } public: - VMAddressProvider(ObjectFile::Type Type) - : m_synthesizing(Type == ObjectFile::Type::eTypeObjectFile), m_next(0) {} + VMAddressProvider(ObjectFile::Type Type) : ObjectType(Type) {} - std::pair GetAddressAndSize(const ELFSectionHeader &H) { - addr_t address = H.sh_addr; - addr_t size = H.sh_flags & SHF_ALLOC ? H.sh_size : 0; - if (m_synthesizing && (H.sh_flags & SHF_ALLOC)) { - m_next = llvm::alignTo(m_next, std::max(H.sh_addralign, 1)); - address = m_next; - m_next += size; - } - return {address, size}; + llvm::Optional GetAddressInfo(const ELFProgramHeader &H) { + if (H.p_memsz == 0) { + LLDB_LOG(Log, + "Ignoring zero-sized PT_LOAD segment. Corrupt object file?"); + return llvm::None; + } + + if (Segments.overlaps(H.p_vaddr, H.p_vaddr + H.p_memsz)) { + LLDB_LOG(Log, + "Ignoring overlapping PT_LOAD segment. Corrupt object file?"); + return llvm::None; + } + return VMRange(H.p_vaddr, H.p_memsz); + } + + llvm::Optional GetAddressInfo(const ELFSectionHeader &H) { + VMRange Range = GetVMRange(H); + SectionSP Segment; + auto It = Segments.find(Range.GetRangeBase()); + if ((H.sh_flags & SHF_ALLOC) && It.valid()) { + addr_t MaxSize; + if (It.start() <= Range.GetRangeBase()) { + MaxSize = It.stop() - Range.GetRangeBase(); + Segment = *It; + } else + MaxSize = It.start() - Range.GetRangeBase(); + if (Range.GetByteSize() > MaxSize) { + LLDB_LOG(Log, "Shortening section crossing segment boundaries. " + "Corrupt object file?"); + Range.SetByteSize(MaxSize); + } + } + if (Range.GetByteSize() > 0 && + Sections.overlaps(Range.GetRangeBase(), Range.GetRangeEnd())) { + LLDB_LOG(Log, "Ignoring overlapping section. Corrupt object file?"); + return llvm::None; + } + if (Segment) + Range.Slide(-Segment->GetFileAddress()); + return SectionAddressInfo{Segment, Range}; + } + + void AddSegment(const VMRange &Range, SectionSP Seg) { + Segments.insert(Range.GetRangeBase(), Range.GetRangeEnd(), std::move(Seg)); + } + + void AddSection(SectionAddressInfo Info, SectionSP Sect) { + if (Info.Range.GetByteSize() == 0) + return; + if (Info.Segment) + Info.Range.Slide(Info.Segment->GetFileAddress()); + Sections.insert(Info.Range.GetRangeBase(), Info.Range.GetRangeEnd(), + std::move(Sect)); } }; } void ObjectFileELF::CreateSections(SectionList &unified_section_list) { - if (!m_sections_ap.get() && ParseSectionHeaders()) { - m_sections_ap.reset(new SectionList()); + if (m_sections_ap) + return; - VMAddressProvider address_provider(CalculateType()); - for (SectionHeaderCollIter I = std::next(m_section_headers.begin()); - I != m_section_headers.end(); ++I) { - const ELFSectionHeaderInfo &header = *I; - - ConstString &name = I->section_name; - const uint64_t file_size = - header.sh_type == SHT_NOBITS ? 0 : header.sh_size; - - addr_t vm_addr, vm_size; - std::tie(vm_addr, vm_size) = address_provider.GetAddressAndSize(header); - - SectionType sect_type = GetSectionType(header); - - const uint32_t target_bytes_size = - GetTargetByteSize(sect_type, m_arch_spec); - - elf::elf_xword log2align = - (header.sh_addralign == 0) ? 0 : llvm::Log2_64(header.sh_addralign); - - SectionSP section_sp(new Section( - GetModule(), // Module to which this section belongs. - this, // ObjectFile to which this section belongs and should read - // section data from. - SectionIndex(I), // Section ID. - name, // Section name. - sect_type, // Section type. - vm_addr, // VM address. - vm_size, // VM size in bytes of this section. - header.sh_offset, // Offset of this section in the file. - file_size, // Size of the section as found in the file. - log2align, // Alignment of the section - header.sh_flags, // Flags for this section. - target_bytes_size)); // Number of host bytes per target byte - - section_sp->SetPermissions(GetPermissions(header)); - section_sp->SetIsThreadSpecific(header.sh_flags & SHF_TLS); - m_sections_ap->AddSection(section_sp); - } + m_sections_ap = llvm::make_unique(); + VMAddressProvider address_provider(CalculateType()); + + size_t LoadID = 0; + for (const auto &EnumPHdr : llvm::enumerate(ProgramHeaders())) { + const ELFProgramHeader &PHdr = EnumPHdr.value(); + if (PHdr.p_type != PT_LOAD) + continue; + + auto InfoOr = address_provider.GetAddressInfo(PHdr); + if (!InfoOr) + continue; + + ConstString Name(("PT_LOAD[" + llvm::Twine(LoadID++) + "]").str()); + uint32_t Log2Align = llvm::Log2_64(std::max(PHdr.p_align, 1)); + SectionSP Segment = std::make_shared
( + GetModule(), this, SegmentID(EnumPHdr.index()), Name, + eSectionTypeContainer, InfoOr->GetRangeBase(), InfoOr->GetByteSize(), + PHdr.p_offset, PHdr.p_filesz, Log2Align, /*flags*/ 0); + Segment->SetPermissions(GetPermissions(PHdr)); + m_sections_ap->AddSection(Segment); + + address_provider.AddSegment(*InfoOr, std::move(Segment)); + } + + ParseSectionHeaders(); + if (m_section_headers.empty()) + return; + + for (SectionHeaderCollIter I = std::next(m_section_headers.begin()); + I != m_section_headers.end(); ++I) { + const ELFSectionHeaderInfo &header = *I; + + ConstString &name = I->section_name; + const uint64_t file_size = + header.sh_type == SHT_NOBITS ? 0 : header.sh_size; + + auto InfoOr = address_provider.GetAddressInfo(header); + if (!InfoOr) + continue; + + SectionType sect_type = GetSectionType(header); + + const uint32_t target_bytes_size = + GetTargetByteSize(sect_type, m_arch_spec); + + elf::elf_xword log2align = + (header.sh_addralign == 0) ? 0 : llvm::Log2_64(header.sh_addralign); + + SectionSP section_sp(new Section( + InfoOr->Segment, GetModule(), // Module to which this section belongs. + this, // ObjectFile to which this section belongs and should + // read section data from. + SectionIndex(I), // Section ID. + name, // Section name. + sect_type, // Section type. + InfoOr->Range.GetRangeBase(), // VM address. + InfoOr->Range.GetByteSize(), // VM size in bytes of this section. + header.sh_offset, // Offset of this section in the file. + file_size, // Size of the section as found in the file. + log2align, // Alignment of the section + header.sh_flags, // Flags for this section. + target_bytes_size)); // Number of host bytes per target byte + + section_sp->SetPermissions(GetPermissions(header)); + section_sp->SetIsThreadSpecific(header.sh_flags & SHF_TLS); + (InfoOr->Segment ? InfoOr->Segment->GetChildren() : *m_sections_ap) + .AddSection(section_sp); + address_provider.AddSection(std::move(*InfoOr), std::move(section_sp)); } // For eTypeDebugInfo files, the Symbol Vendor will take care of updating the