Index: llvm/include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- llvm/include/llvm/ObjectYAML/ELFYAML.h +++ llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -99,6 +99,9 @@ Optional MemSize; Optional Offset; std::vector Sections; + + // This can be used to override the offset stored in the p_offset field. + Optional POffset; }; struct Symbol { @@ -672,6 +675,7 @@ template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::ProgramHeader &FileHdr); + static StringRef validate(IO &IO, ELFYAML::ProgramHeader &FileHdr); }; template <> Index: llvm/lib/ObjectYAML/ELFEmitter.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFEmitter.cpp +++ llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -754,23 +754,27 @@ Elf_Phdr &PHeader = PHeaders[PhdrIdx++]; std::vector Fragments = getPhdrFragments(YamlPhdr, SHeaders); - if (YamlPhdr.Offset) { - PHeader.p_offset = *YamlPhdr.Offset; - } else { - if (YamlPhdr.Sections.size()) - PHeader.p_offset = UINT32_MAX; - else - PHeader.p_offset = 0; + uint64_t PhdrFileOffset = Fragments.empty() ? 0 : UINT64_MAX; + // Find the minimum section file offset. + for (const Fragment &F : Fragments) + PhdrFileOffset = std::min(PhdrFileOffset, F.Offset); - // Find the minimum offset for the program header. - for (const Fragment &F : Fragments) - PHeader.p_offset = std::min((uint64_t)PHeader.p_offset, F.Offset); + if (YamlPhdr.Offset) { + if (!Fragments.empty() && *YamlPhdr.Offset > PhdrFileOffset) + reportError("the 'Offset' specified for the segment with index " + + Twine(PhdrIdx) + + " must be less or equal than the minimal file offset of a " + "section included into it (0x" + + Twine::utohexstr(PhdrFileOffset) + ")"); + PhdrFileOffset = *YamlPhdr.Offset; } + PHeader.p_offset = + YamlPhdr.POffset ? uint64_t(*YamlPhdr.POffset) : PhdrFileOffset; // Find the maximum offset of the end of a section in order to set p_filesz // and p_memsz. When setting p_filesz, trailing SHT_NOBITS sections are not // counted. - uint64_t FileOffset = PHeader.p_offset, MemOffset = PHeader.p_offset; + uint64_t FileOffset = PhdrFileOffset, MemOffset = PhdrFileOffset; for (const Fragment &F : Fragments) { uint64_t End = F.Offset + F.Size; MemOffset = std::max(MemOffset, End); @@ -781,9 +785,9 @@ // Set the file size and the memory size if not set explicitly. PHeader.p_filesz = YamlPhdr.FileSize ? uint64_t(*YamlPhdr.FileSize) - : FileOffset - PHeader.p_offset; + : FileOffset - PhdrFileOffset; PHeader.p_memsz = YamlPhdr.MemSize ? uint64_t(*YamlPhdr.MemSize) - : MemOffset - PHeader.p_offset; + : MemOffset - PhdrFileOffset; if (YamlPhdr.Align) { PHeader.p_align = *YamlPhdr.Align; Index: llvm/lib/ObjectYAML/ELFYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFYAML.cpp +++ llvm/lib/ObjectYAML/ELFYAML.cpp @@ -856,6 +856,20 @@ IO.mapOptional("FileSize", Phdr.FileSize); IO.mapOptional("MemSize", Phdr.MemSize); IO.mapOptional("Offset", Phdr.Offset); + + // obj2yaml does not dump these fields. They are expected to be empty when we + // are producing YAML, because yaml2obj sets appropriate values for them + // automatically when they are not explicitly defined. + assert(!IO.outputting() || !Phdr.POffset.hasValue()); + IO.mapOptional("POffset", Phdr.POffset); +} + +StringRef +MappingTraits::validate(IO &IO, + ELFYAML::ProgramHeader &Phdr) { + if (Phdr.POffset && Phdr.Offset) + return "POffset and Offset cannot both be specified for ProgramHeader"; + return StringRef(); } LLVM_YAML_STRONG_TYPEDEF(StringRef, StOtherPiece) Index: llvm/test/Object/invalid.test =================================================================== --- llvm/test/Object/invalid.test +++ llvm/test/Object/invalid.test @@ -492,8 +492,8 @@ - Tag: DT_NULL Value: 0 ProgramHeaders: - - Type: PT_DYNAMIC - Offset: 0xffff0000 + - Type: PT_DYNAMIC + POffset: 0xffff0000 Sections: - Section: .dynamic Index: llvm/test/tools/llvm-objcopy/ELF/invalid-p_filesz-p_offset.test =================================================================== --- llvm/test/tools/llvm-objcopy/ELF/invalid-p_filesz-p_offset.test +++ llvm/test/tools/llvm-objcopy/ELF/invalid-p_filesz-p_offset.test @@ -39,7 +39,7 @@ Size: 1 ProgramHeaders: - Type: PT_LOAD - Offset: 0x100000 + POffset: 0x100000 FileSize: 1 Sections: - Section: .foo Index: llvm/test/tools/llvm-readobj/ELF/gnu-notes.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/gnu-notes.test +++ llvm/test/tools/llvm-readobj/ELF/gnu-notes.test @@ -170,8 +170,8 @@ Type: SHT_NOTE Notes: [] ProgramHeaders: - - Type: PT_NOTE - Offset: 0xffff0000 + - Type: PT_NOTE + POffset: 0xffff0000 Sections: - Section: .note Index: llvm/test/tools/yaml2obj/ELF/program-header-size-offset.yaml =================================================================== --- llvm/test/tools/yaml2obj/ELF/program-header-size-offset.yaml +++ llvm/test/tools/yaml2obj/ELF/program-header-size-offset.yaml @@ -1,8 +1,8 @@ ## Show that yaml2obj properly emits program headers with explicit file size, ## memory size and offset parameters. -# RUN: yaml2obj %s -o %t -# RUN: llvm-readobj %t --program-headers | FileCheck %s +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-readobj %t1 --program-headers | FileCheck %s # CHECK: ProgramHeaders [ # CHECK: Offset: 0x1234 @@ -34,7 +34,7 @@ # CHECK: MemSize: 6 # CHECK: ] -!ELF +--- !ELF FileHeader: Class: ELFCLASS64 Data: ELFDATA2LSB @@ -94,7 +94,7 @@ - Section: .text # Program header with sections, invalid properties. - Type: 0x6abcdef0 - Offset: 0x3000 + POffset: 0x3000 FileSize: 3 MemSize: 2 Sections: @@ -106,3 +106,85 @@ - Section: .data - Section: .nobits1 - Section: .nobits2 + +## Test "Offset" and "POffset" properties. + +## Check we report an error when the "Offset" and "POffset" are used together. +# RUN: not yaml2obj --docnum=2 %s -o %t3 2>&1 | FileCheck %s --check-prefix=BOTH + +# BOTH: error: POffset and Offset cannot both be specified for ProgramHeader + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +ProgramHeaders: + - Type: PT_LOAD + POffset: 0x0 + Offset: 0x0 + Sections: [] + +## Check that by default the segments p_offset field is set to the +## offset of the section with the minimal section offset. + +# RUN: yaml2obj --docnum=3 -DOFFSET_KEY="" %s -o %t2 +# RUN: llvm-readelf %t2 --sections --program-headers | \ +# RUN: FileCheck %s --check-prefixes=SEC,DEFAULT + +# SEC: [Nr] Name Type Address Off +# SEC: [ 1] .foo PROGBITS 0000000000001000 000078 + +# DEFAULT: Type Offset +# DEFAULT: LOAD 0x000078 + +## Check we can set the "Offset" value explicitly when it is less or equal to +## the offset of a section in the segment. +# RUN: yaml2obj --docnum=3 -DOFFSET_KEY="Offset: 0x78" %s -o %t3 +# RUN: llvm-readelf %t3 --sections --program-headers | \ +# RUN: FileCheck %s --check-prefixes=SEC,DEFAULT + +## Check we report an error when the "Offset" value is larger than the offset of a section in the segment. +# RUN: not yaml2obj --docnum=3 -DOFFSET_KEY="Offset: 0x79" %s -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=INVALID-OFFSET + +# INVALID-OFFSET: yaml2obj: error: the 'Offset' specified for the segment with index 1 must be less or equal than the minimal file offset of a section included into it (0x78) + +## We can use "POffset" to set an arbitrary offset for a segment. +# RUN: yaml2obj --docnum=3 -DOFFSET_KEY="POffset: 0x79" %s -o %t4 +# RUN: llvm-readelf %t4 --sections --program-headers | \ +# RUN: FileCheck %s --check-prefixes=SEC,POFFSET1 + +# POFFSET1: Type Offset +# POFFSET1: LOAD 0x000079 + +# RUN: yaml2obj --docnum=3 -DOFFSET_KEY="POffset: 0xFF112233" %s -o %t5 +# RUN: llvm-readelf %t5 --sections --program-headers | \ +# RUN: FileCheck %s --check-prefixes=SEC,POFFSET2 + +# POFFSET2: Type Offset +# POFFSET2: LOAD 0xff112233 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Size: 0x1 + Address: 0x1000 + - Name: .bar + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Size: 0x1 +ProgramHeaders: + - Type: PT_LOAD + [[OFFSET_KEY]] + Sections: + - Section: .foo + - Section: .bar