From 2bf56743172c536b201a6f68b497694253236bd4 Mon Sep 17 00:00:00 2001 From: Georgii Rymar Date: Thu, 16 Apr 2020 16:41:28 +0300 Subject: [PATCH] [yaml2obj] - Program headers: add an additional check for `Offset` The `Offset` field is used to set the file offset of a program header. In a normal object it should not be greater than the minimal offset of sections included into segment. This patch adds a check for that and adds tests. Differential revision: https://reviews.llvm.org/D78304 --- llvm/lib/ObjectYAML/ELFEmitter.cpp | 16 +-- llvm/test/Object/invalid.test | 8 -- .../ELF/invalid-p_filesz-p_offset.test | 12 +-- llvm/test/tools/llvm-readobj/ELF/gnu-notes.test | 8 +- .../yaml2obj/ELF/program-header-size-offset.yaml | 120 +++++++++++++++++++-- 5 files changed, 128 insertions(+), 36 deletions(-) diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index 450856da..c32c42b 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -759,12 +759,16 @@ void ELFState::setProgramHeaderLayout(std::vector &PHeaders, reportError("sections in the program header with index " + Twine(PhdrIdx) + " are not sorted by their file offset"); - if (YamlPhdr.Offset) - PHeader.p_offset = *YamlPhdr.Offset; - else if (!Fragments.empty()) - PHeader.p_offset = Fragments.front().Offset; - else - PHeader.p_offset = 0; + uint64_t PhdrFileOffset = Fragments.empty() ? 0 : Fragments.front().Offset; + if (YamlPhdr.Offset) { + if (!Fragments.empty() && *YamlPhdr.Offset > PhdrFileOffset) + reportError("'Offset' for segment with index " + Twine(PhdrIdx) + + " must be less than or equal to the minimum file offset of " + "all included sections (0x" + + Twine::utohexstr(PhdrFileOffset) + ")"); + PhdrFileOffset = *YamlPhdr.Offset; + } + PHeader.p_offset = 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 diff --git a/llvm/test/Object/invalid.test b/llvm/test/Object/invalid.test index 3a1ef2b..a7363ab 100644 --- a/llvm/test/Object/invalid.test +++ b/llvm/test/Object/invalid.test @@ -485,17 +485,9 @@ FileHeader: Data: ELFDATA2LSB Type: ET_EXEC Machine: EM_X86_64 -Sections: - - Name: .dynamic - Type: SHT_DYNAMIC - Entries: - - Tag: DT_NULL - Value: 0 ProgramHeaders: - Type: PT_DYNAMIC Offset: 0xffff0000 - Sections: - - Section: .dynamic ## PT_DYNAMIC's p_filesz field is so large that p_offset + p_filesz is larger ## than the object size. Check llvm-readobj reports it. diff --git a/llvm/test/tools/llvm-objcopy/ELF/invalid-p_filesz-p_offset.test b/llvm/test/tools/llvm-objcopy/ELF/invalid-p_filesz-p_offset.test index b2f746e..db6b591 100644 --- a/llvm/test/tools/llvm-objcopy/ELF/invalid-p_filesz-p_offset.test +++ b/llvm/test/tools/llvm-objcopy/ELF/invalid-p_filesz-p_offset.test @@ -33,13 +33,7 @@ FileHeader: Data: ELFDATA2LSB Type: ET_EXEC Machine: EM_X86_64 -Sections: - - Name: .foo - Type: SHT_PROGBITS - Size: 1 ProgramHeaders: - - Type: PT_LOAD - Offset: 0x100000 - FileSize: 1 - Sections: - - Section: .foo + - Type: PT_LOAD + Offset: 0x100000 + FileSize: 1 diff --git a/llvm/test/tools/llvm-readobj/ELF/gnu-notes.test b/llvm/test/tools/llvm-readobj/ELF/gnu-notes.test index cb4ea1d..366d435 100644 --- a/llvm/test/tools/llvm-readobj/ELF/gnu-notes.test +++ b/llvm/test/tools/llvm-readobj/ELF/gnu-notes.test @@ -165,15 +165,9 @@ FileHeader: Data: ELFDATA2LSB Type: ET_CORE Machine: EM_X86_64 -Sections: - - Name: .note - Type: SHT_NOTE - Notes: [] ProgramHeaders: - - Type: PT_NOTE + - Type: PT_NOTE Offset: 0xffff0000 - Sections: - - Section: .note ## Test tools report an error if a note program header has an invalid size that ## goes past the end of file. diff --git a/llvm/test/tools/yaml2obj/ELF/program-header-size-offset.yaml b/llvm/test/tools/yaml2obj/ELF/program-header-size-offset.yaml index a2f727dab..9b16b32 100644 --- a/llvm/test/tools/yaml2obj/ELF/program-header-size-offset.yaml +++ b/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 @@ -92,13 +92,11 @@ ProgramHeaders: MemSize: 9 Sections: - Section: .text - # Program header with sections, invalid properties. + # Program header with invalid properties. - Type: 0x6abcdef0 Offset: 0x3000 FileSize: 3 MemSize: 2 - Sections: - - Section: .data # Program header with 2 SHT_NOBITS sections. - Type: 0x6abcdef0 Offset: 0x2004 @@ -106,3 +104,113 @@ ProgramHeaders: - Section: .data - Section: .nobits1 - Section: .nobits2 + +## Test the "Offset" property. + +## Check that by default the p_offset field of a segment is set to the +## offset of the section with the minimum offset. +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: llvm-readelf %t2 --sections --program-headers | \ +# RUN: FileCheck %s --check-prefixes=DEFAULT-OFFSET + +# DEFAULT-OFFSET: [Nr] Name Type Address Off +# DEFAULT-OFFSET: [ 1] .foo PROGBITS 0000000000001000 0000b0 +# DEFAULT-OFFSET-NEXT: [ 2] .bar PROGBITS 0000000000001001 0000b1 + +# DEFAULT-OFFSET: Type Offset +# DEFAULT-OFFSET-NEXT: LOAD 0x0000b0 +# DEFAULT-OFFSET-NEXT: LOAD 0x0000b1 + +--- !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 + Sections: + - Section: .foo + - Section: .bar + - Type: PT_LOAD + Sections: + - Section: .bar + +## Check we can set the "Offset" value explicitly to be less than or equal to +## the offset of a section in the segment. +# RUN: yaml2obj --docnum=3 -DOFFSET=0x77 %s -o %t3 +# RUN: llvm-readelf %t3 --sections --program-headers | \ +# RUN: FileCheck %s --check-prefixes=VALID-OFFSET,VALID-OFFSET-LESS +# RUN: yaml2obj --docnum=3 -DOFFSET=0x78 %s -o %t4 +# RUN: llvm-readelf %t4 --sections --program-headers | \ +# RUN: FileCheck %s --check-prefixes=VALID-OFFSET,VALID-OFFSET-EQ + +# VALID-OFFSET: [Nr] Name Type Address Off +# VALID-OFFSET: [ 1] .foo PROGBITS 0000000000000000 000078 + +# VALID-OFFSET: Type Offset +# VALID-OFFSET-EQ: LOAD 0x000078 +# VALID-OFFSET-LESS: LOAD 0x000077 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Size: 0x1 +ProgramHeaders: + - Type: PT_LOAD + Offset: [[OFFSET]] + Sections: + - Section: .foo + +## 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=0x79 %s -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=INVALID-OFFSET + +# INVALID-OFFSET: yaml2obj: error: 'Offset' for segment with index 1 must be less than or equal to the minimum file offset of all included sections (0x78) + +## Document that the "Offset" value is checked after the section offset is overriden using "ShOffset". +# RUN: yaml2obj --docnum=4 %s -o %t5 +# RUN: llvm-readelf %t5 --sections --program-headers | FileCheck %s --check-prefix=SHOFFSET + +# SHOFFSET: [Nr] Name Type Address Off +# SHOFFSET: [ 1] .foo PROGBITS 0000000000000000 ffffffff + +# SHOFFSET: Type Offset +# SHOFFSET-NEXT: LOAD 0xffffff00 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Size: 0x1 +## Note: the real .foo offset is much less than 0xFFFFFFFF or +## 0xFFFFFF00, but no error is reported. + ShOffset: 0xFFFFFFFF +ProgramHeaders: + - Type: PT_LOAD + Offset: 0xFFFFFF00 + Sections: + - Section: .foo -- 2.7.4