Index: llvm/docs/CommandGuide/llvm-objcopy.rst =================================================================== --- llvm/docs/CommandGuide/llvm-objcopy.rst +++ llvm/docs/CommandGuide/llvm-objcopy.rst @@ -63,6 +63,10 @@ Enable deterministic mode when copying archives, i.e. use 0 for archive member header UIDs, GIDs and timestamp fields. On by default. +.. option:: --gap-fill + + Fill gaps between two adjacent loadable sections with the specified 8 bit value. + .. option:: --help, -h Print a summary of command line options. @@ -72,6 +76,11 @@ Remove all sections from the output, except for sections named ``
``. Can be specified multiple times to keep multiple sections. +.. option:: --pad-to
+ + Pad the last loadable section to the pad-to address using a value of zero or + the value specified by ``--gap-fill``. + .. option:: --regex If specified, symbol and section names specified by other switches are treated Index: llvm/include/llvm/Object/ELF.h =================================================================== --- llvm/include/llvm/Object/ELF.h +++ llvm/include/llvm/Object/ELF.h @@ -405,7 +405,7 @@ Offset + Size > Buf.size()) return createError("section " + getSecIndexForError(this, Sec) + " has a sh_offset (0x" + Twine::utohexstr(Offset) + - ") + sh_size (0x" + Twine(Size) + + ") + sh_size (0x" + Twine::utohexstr(Size) + ") that cannot be represented"); if (Offset % alignof(T)) Index: llvm/test/Object/invalid.test =================================================================== --- llvm/test/Object/invalid.test +++ llvm/test/Object/invalid.test @@ -41,7 +41,7 @@ # RUN: not llvm-objdump -s %p/Inputs/invalid-strtab-size.elf 2>&1 \ # RUN: | FileCheck %s -DFILE=%p/Inputs/invalid-strtab-size.elf --check-prefix=INVALID-STRTAB-SIZE -# INVALID-STRTAB-SIZE: error: '[[FILE]]': section [index 1] has a sh_offset (0x70) + sh_size (0x16777215) that cannot be represented +# INVALID-STRTAB-SIZE: error: '[[FILE]]': section [index 1] has a sh_offset (0x70) + sh_size (0xffffff) that cannot be represented ## Check that llvm-dwarfdump reports an error during relocation resolution ## when instead of expected SHT_RELA section it locates a section of a different type. @@ -280,7 +280,7 @@ # RUN: yaml2obj %s --docnum=14 -o %t14 # RUN: not llvm-readobj --symbols %t14 2>&1 | FileCheck -DFILE=%t14 --check-prefix=INVALID-SECTION-SIZE2 %s -# INVALID-SECTION-SIZE2: error: '[[FILE]]': section [index 1] has a sh_offset (0xffffffff) + sh_size (0x27) that cannot be represented +# INVALID-SECTION-SIZE2: error: '[[FILE]]': section [index 1] has a sh_offset (0xffffffff) + sh_size (0x1b) that cannot be represented --- !ELF FileHeader: Index: llvm/test/tools/llvm-objcopy/ELF/gap-fill.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/gap-fill.test @@ -0,0 +1,159 @@ +# REQUIRES: shell +# Tests for --gap-fill option. +# +# clang-7 gapfillpadto.cpp -o gapfillpadto.elf +# void swap(int *a, int *b) { +# int temp = *b; +# *b = *a; +# *a = temp; +#} +# +# int main() { +# int a = 10; +# int b = 1; +# swap(&a, &b); +# return a; +# } +# +# +# RUN: llvm-objcopy %p/Inputs/gapfillpadto.elf %t +# RUN: not llvm-objcopy --gap-fill %t 2>&1 | FileCheck %s --check-prefix=EMPTY +# RUN: not llvm-objcopy --gap-fill= %t 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT +# RUN: not llvm-objcopy --gap-fill=x %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT +# RUN: not llvm-objcopy --gap-fill=0x1G %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT2 +# +# Verify truncating is not allowed. +# RUN: not llvm-objcopy --gap-fill=0x1122 %t 2>&1 | FileCheck %s --check-prefix=BAD-NUMBER + +# Verify the headers are expected. +# RUN: llvm-objcopy --gap-fill=0xe9 %t %t-filled +# RUN: llvm-readelf -h %t-filled | FileCheck %s --check-prefix=FHDR +# RUN: llvm-readelf -S %t-filled | FileCheck %s --check-prefix=SHDR +# RUN: llvm-readelf -l %t-filled | FileCheck %s --check-prefix=PHDR + +# Verify the following section contents are not altered. +# RUN: llvm-objcopy -j .interp -j .note.ABI-tag -j .note.gnu.build-id -j .gnu_version_r -j .rodata \ +# RUN: -j .eh_frame_hdr -j .init_array -j .fini_array -j .got -j .got.plt -O binary %t %t1 +# RUN: llvm-objcopy -j .interp -j .note.ABI-tag -j .note.gnu.build-id -j .gnu_version_r -j .rodata \ +# RUN: -j .eh_frame_hdr -j .init_array -j .fini_array -j .got -j .got.plt -O binary %t-filled %t2 +# RUN: cmp %t1 %t2 +# RUN: llvm-objcopy -j .dynsym -j .rela.dyn -j .dynamic --allow-broken-links -O binary %t %t3 +# RUN: llvm-objcopy -j .dynsym -j .rela.dyn -j .dynamic --allow-broken-links -O binary %t-filled %t4 +# RUN: cmp %t3 %t4 + +# Verify all section gaps are filled with 0xe9. The verification is done by two steps: +# First read the section original data and ensure they are not altered. +# Second, read the gaps and ensure they have the filled value. +# RUN: llvm-objcopy -j .init -O binary %t %t.init +# RUN: llvm-objcopy -j .init -O binary %t-filled %t-filled.init +# RUN: od -v -Ax -x -N 0x17 %t.init > %t.init1 +# RUN: od -v -Ax -x -N 0x17 %t-filled.init > %t.init2 +# RUN: cmp %t.init1 %t.init2 +# RUN: od -v -Ax -t x1 -j 0x17 -N 1 %t-filled.init | FileCheck %s --check-prefix=INIT +# +# RUN: llvm-objcopy -j .fini -O binary %t %t.fini +# RUN: llvm-objcopy -j .fini -O binary %t-filled %t-filled.fini +# RUN: od -v -Ax -x -N 0x9 %t.fini > %t.fini1 +# RUN: od -v -Ax -x -N 0x9 %t-filled.fini > %t.fini2 +# RUN: cmp %t.fini1 %t.fini2 +# RUN: od -v -Ax -t x1 -j 0x9 -N 3 %t-filled.fini | FileCheck %s --check-prefix=FINI +# +# RUN: llvm-objcopy -j .text -O binary %t %t.text +# RUN: llvm-objcopy -j .text -O binary %t-filled %t-filled.text +# RUN: od -v -Ax -x -N 0x1d2 %t.text > %t.text1 +# RUN: od -v -Ax -x -N 0x1d2 %t-filled.text > %t.text2 +# RUN: cmp %t.text1 %t.text2 +# RUN: od -v -Ax -t x1 -j 0x1d2 -N 2 %t-filled.text | FileCheck %s --check-prefix=TEXT +# +# RUN: llvm-objcopy -j .gnu.hash --allow-broken-links -O binary %t %t.hash +# RUN: llvm-objcopy -j .gnu.hash --allow-broken-links -O binary %t-filled %t-filled.hash +# RUN: od -v -Ax -x -N 0x1c %t.hash > %t.hash1 +# RUN: od -v -Ax -x -N 0x1c %t-filled.hash > %t.hash2 +# RUN: cmp %t.hash1 %t.hash2 +# RUN: od -v -Ax -t x1 -j 0x1c -N 4 %t-filled.hash | FileCheck %s --check-prefix=HASH +# +# RUN: llvm-objcopy -j .dynstr --allow-broken-links -O binary %t %t.dynstr +# RUN: llvm-objcopy -j .dynstr --allow-broken-links -O binary %t-filled %t-filled.dynstr +# RUN: od -v -Ax -x -N 0x5f %t.dynstr > %t.dynstr1 +# RUN: od -v -Ax -x -N 0x5f %t-filled.dynstr > %t.dynstr2 +# RUN: cmp %t.dynstr1 %t.dynstr2 +# RUN: od -v -Ax -t x1 -j 0x5f -N 1 %t-filled.dynstr | FileCheck %s --check-prefix=DYNSTR +# +# RUN: llvm-objcopy -j .gnu.version --allow-broken-links -O binary %t %t.ver +# RUN: llvm-objcopy -j .gnu.version --allow-broken-links -O binary %t-filled %t-filled.ver +# RUN: od -v -Ax -x -N 0x6 %t.ver > %t.ver1 +# RUN: od -v -Ax -x -N 0x6 %t-filled.ver > %t.ver2 +# RUN: cmp %t.ver1 %t.ver2 +# RUN: od -v -Ax -t x1 -j 0x6 -N 2 %t-filled.ver | FileCheck %s --check-prefix=VER +# +# RUN: llvm-objcopy -j .eh_frame -O binary %t %t.ehf +# RUN: llvm-objcopy -j .eh_frame -O binary %t-filled %t-filled.ehf +# RUN: od -v -Ax -x -N 0xf8 %t.ehf > %t.ehf1 +# RUN: od -v -Ax -x -N 0xf8 %t-filled.ehf > %t.ehf2 +# RUN: cmp %t.ehf1 %t.ehf2 +# Read 0x200738 bytes (equals to 0x200830-0xf8, i.e 2099000) +# RUN: od -v -An -t x1 -j 0xf8 -N 2099000 %t-filled.ehf | grep "e9" | wc -w | FileCheck %s --check-prefix=EHF + +# Verify the last loadable section is not altered. +# RUN: llvm-objcopy -j .data -O binary %t %t.data +# RUN: llvm-objcopy -j .data -O binary %t-filled %t-filled.data +# RUN: cmp %t.data %t-filled.data + +# EMPTY: no input file specified +# BAD-FORMAT: bad number for --gap-fill: +# BAD-INPUT: bad number for --gap-fill: x +# BAD-INPUT2: bad number for --gap-fill: 0x1G +# BAD-NUMBER: bad number for --gap-fill: 0x1122 +# +# FHDR: Start of section headers: 2103536 (bytes into file) +# +# SHDR: Section Headers: +# SHDR: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# SHDR: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# SHDR: [ 1] .interp PROGBITS 0000000000400238 000238 00001c 00 A 0 0 1 +# SHDR: [ 2] .note.ABI-tag NOTE 0000000000400254 000254 000020 00 A 0 0 4 +# SHDR: [ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4 +# SHDR: [ 4] .gnu.hash GNU_HASH 0000000000400298 000298 000020 00 A 5 0 8 +# SHDR: [ 5] .dynsym DYNSYM 00000000004002b8 0002b8 000048 18 A 6 1 8 +# SHDR: [ 6] .dynstr STRTAB 0000000000400300 000300 000060 00 A 0 0 1 +# SHDR: [ 7] .gnu.version VERSYM 0000000000400360 000360 000008 02 A 5 0 2 +# SHDR: [ 8] .gnu.version_r VERNEED 0000000000400368 000368 000020 00 A 6 1 8 +# SHDR: [ 9] .rela.dyn RELA 0000000000400388 000388 000030 18 A 5 0 8 +# SHDR: [10] .init PROGBITS 00000000004003b8 0003b8 000018 00 AX 0 0 4 +# SHDR: [11] .text PROGBITS 00000000004003d0 0003d0 0001d4 00 AX 0 0 16 +# SHDR: [12] .fini PROGBITS 00000000004005a4 0005a4 00000c 00 AX 0 0 4 +# SHDR: [13] .rodata PROGBITS 00000000004005b0 0005b0 000004 04 AM 0 0 4 +# SHDR: [14] .eh_frame_hdr PROGBITS 00000000004005b4 0005b4 00003c 00 A 0 0 4 +# SHDR: [15] .eh_frame PROGBITS 00000000004005f0 0005f0 200830 00 A 0 0 8 +# SHDR: [16] .init_array INIT_ARRAY 0000000000600e20 200e20 000008 08 WA 0 0 8 +# SHDR: [17] .fini_array FINI_ARRAY 0000000000600e28 200e28 000008 08 WA 0 0 8 +# SHDR: [18] .dynamic DYNAMIC 0000000000600e30 200e30 0001c0 10 WA 6 0 8 +# SHDR: [19] .got PROGBITS 0000000000600ff0 200ff0 000010 08 WA 0 0 8 +# SHDR: [20] .got.plt PROGBITS 0000000000601000 201000 000018 08 WA 0 0 8 +# SHDR: [21] .data PROGBITS 0000000000601018 201018 000010 00 WA 0 0 8 +# SHDR: [22] .bss NOBITS 0000000000601028 201028 000008 00 WA 0 0 1 +# SHDR: [23] .comment PROGBITS 0000000000000000 201028 000078 01 MS 0 0 1 +# SHDR: [24] .symtab SYMTAB 0000000000000000 2010a0 000588 18 25 41 8 +# SHDR: [25] .strtab STRTAB 0000000000000000 201628 0001cc 00 0 0 1 +# SHDR: [26] .shstrtab STRTAB 0000000000000000 2017f4 0000f9 00 0 0 1 +# +# PHDR: Program Headers: +# PHDR: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# PHDR: PHDR 0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R 0x8 +# PHDR: INTERP 0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R 0x1 +# PHDR: [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] +# PHDR: LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x200e20 0x200e20 R E 0x200000 +# PHDR: LOAD 0x200e20 0x0000000000600e20 0x0000000000600e20 0x000208 0x000210 RW 0x200000 +# PHDR: DYNAMIC 0x200e30 0x0000000000600e30 0x0000000000600e30 0x0001c0 0x0001c0 RW 0x8 +# PHDR: NOTE 0x000254 0x0000000000400254 0x0000000000400254 0x000044 0x000044 R 0x4 +# PHDR: GNU_EH_FRAME 0x0005b4 0x00000000004005b4 0x00000000004005b4 0x00003c 0x00003c R 0x4 +# PHDR: GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 +# PHDR: GNU_RELRO 0x200e20 0x0000000000600e20 0x0000000000600e20 0x0001e0 0x0001e0 R 0x1 + +# INIT: 000017 e9 +# FINI: 000009 e9 e9 e9 +# TEXT: 0001d2 e9 e9 +# HASH: 00001c e9 e9 e9 e9 +# DYNSTR: 00005f e9 +# VER: 000006 e9 e9 +# EHF: 2099000 Index: llvm/test/tools/llvm-objcopy/ELF/pad-to.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/pad-to.test @@ -0,0 +1,100 @@ +# REQUIRES: shell +# RUN: llvm-objcopy %p/Inputs/gapfillpadto.elf %t +# RUN: not llvm-objcopy --pad-to %t 2>&1 | FileCheck %s --check-prefix=EMPTY +# RUN: not llvm-objcopy --pad-to= %t 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT +# RUN: not llvm-objcopy --pad-to=x %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT +# RUN: not llvm-objcopy --pad-to=0x1G %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT2 +# RUN: not llvm-objcopy --pad-to=0x112233445566778899 %t 2>&1 | FileCheck %s --check-prefix=BAD-NUMBER +# +# The last loadable section is .data (0x601018 - 0x601028). +# Verify that nothing is done if provided any pad-to address lower than 0x601028. +# RUN: llvm-objcopy --pad-to=0x20 %t %t-p1 +# RUN: cmp %t %t-p1 +# RUN: llvm-objcopy --pad-to=0x600000 %t %t-p2 +# RUN: cmp %t %t-p2 +# RUN: llvm-objcopy --pad-to=0x601011 %t %t-p3 +# RUN: cmp %t %t-p3 +# +# Pad .data to a valid address. +# RUN: llvm-objcopy --pad-to=0x601101 %t %t-pad + +# Verify the following section contents are not altered. +# RUN: llvm-objcopy -j .interp -j .note.ABI-tag -j .note.gnu.build-id -j .gnu_version -j .gnu_version_r \ +# RUN: -j .init -j .fini -j .rodata -j .eh_frame_hdr -j .eh_frame -j .init_array -j .fini_array \ +# RUN: -j .got -j .got.plt -O binary %t %t1 +# RUN: llvm-objcopy -j .interp -j .note.ABI-tag -j .note.gnu.build-id -j .gnu_version -j .gnu_version_r \ +# RUN: -j .init -j .fini -j .rodata -j .eh_frame_hdr -j .eh_frame -j .init_array -j .fini_array \ +# RUN: -j .got -j .got.plt -O binary %t-pad %t2 +# RUN: cmp %t1 %t2 +# RUN: llvm-objcopy -j .gnu_hash -j .dynstr -j .dynsym -j .rela.dyn -j .dynamic --allow-broken-links -O binary %t %t3 +# RUN: llvm-objcopy -j .gnu_hash -j .dynstr -j .dynsym -j .rela.dyn -j .dynamic --allow-broken-links -O binary %t-pad %t4 +# RUN: cmp %t3 %t4 + +# Verify .data is padded to the address 0x601101 with a default value 0. +# RUN: llvm-objcopy -j .data -O binary %t %t.data +# RUN: llvm-objcopy -j .data -O binary %t-pad %t-pad.data +# RUN: od -v -Ax -x -N 0x10 %t.data > %t.data1 +# RUN: od -v -Ax -x -N 0x10 %t-pad.data > %t.data2 +# The original data is not altered. +# RUN: cmp %t.data1 %t.data2 +# .data is padded from 0x601028-0x601101. Read 0xd9 bytes (equals to 0x601101 - 0x601028, i.e 217) +# RUN: od -v -An -t x1 -j 0x10 -N 217 %t-pad.data | grep "00" | wc -w | FileCheck %s --check-prefix=DATA + +# Verify the headers are expected. +# RUN: llvm-readelf -h %t-pad | FileCheck %s --check-prefix=FHDR +# RUN: llvm-readelf -S %t-pad | FileCheck %s --check-prefix=SHDR +# RUN: llvm-readelf -l %t-pad | FileCheck %s --check-prefix=PHDR + +# EMPTY: no input file specified +# BAD-FORMAT: bad address for --pad-to: +# BAD-INPUT: bad address for --pad-to: x +# BAD-INPUT2: bad address for --pad-to: 0x1G +# BAD-NUMBER: bad address for --pad-to: 0x112233445566778899 +# +# +# FHDR: Start of section headers: 6608 (bytes into file) +# +# SHDR: Section Headers: +# SHDR: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# SHDR: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# SHDR: [ 1] .interp PROGBITS 0000000000400238 000238 00001c 00 A 0 0 1 +# SHDR: [ 2] .note.ABI-tag NOTE 0000000000400254 000254 000020 00 A 0 0 4 +# SHDR: [ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4 +# SHDR: [ 4] .gnu.hash GNU_HASH 0000000000400298 000298 00001c 00 A 5 0 8 +# SHDR: [ 5] .dynsym DYNSYM 00000000004002b8 0002b8 000048 18 A 6 1 8 +# SHDR: [ 6] .dynstr STRTAB 0000000000400300 000300 00005f 00 A 0 0 1 +# SHDR: [ 7] .gnu.version VERSYM 0000000000400360 000360 000006 02 A 5 0 2 +# SHDR: [ 8] .gnu.version_r VERNEED 0000000000400368 000368 000020 00 A 6 1 8 +# SHDR: [ 9] .rela.dyn RELA 0000000000400388 000388 000030 18 A 5 0 8 +# SHDR: [10] .init PROGBITS 00000000004003b8 0003b8 000017 00 AX 0 0 4 +# SHDR: [11] .text PROGBITS 00000000004003d0 0003d0 0001d2 00 AX 0 0 16 +# SHDR: [12] .fini PROGBITS 00000000004005a4 0005a4 000009 00 AX 0 0 4 +# SHDR: [13] .rodata PROGBITS 00000000004005b0 0005b0 000004 04 AM 0 0 4 +# SHDR: [14] .eh_frame_hdr PROGBITS 00000000004005b4 0005b4 00003c 00 A 0 0 4 +# SHDR: [15] .eh_frame PROGBITS 00000000004005f0 0005f0 0000f8 00 A 0 0 8 +# SHDR: [16] .init_array INIT_ARRAY 0000000000600e20 000e20 000008 08 WA 0 0 8 +# SHDR: [17] .fini_array FINI_ARRAY 0000000000600e28 000e28 000008 08 WA 0 0 8 +# SHDR: [18] .dynamic DYNAMIC 0000000000600e30 000e30 0001c0 10 WA 6 0 8 +# SHDR: [19] .got PROGBITS 0000000000600ff0 000ff0 000010 08 WA 0 0 8 +# SHDR: [20] .got.plt PROGBITS 0000000000601000 001000 000018 08 WA 0 0 8 +# SHDR: [21] .data PROGBITS 0000000000601018 001018 0000e9 00 WA 0 0 8 +# SHDR: [22] .bss NOBITS 0000000000601028 001101 000008 00 WA 0 0 1 +# SHDR: [23] .comment PROGBITS 0000000000000000 001101 000078 01 MS 0 0 1 +# SHDR: [24] .symtab SYMTAB 0000000000000000 001180 000588 18 25 41 8 +# SHDR: [25] .strtab STRTAB 0000000000000000 001708 0001cc 00 0 0 1 +# SHDR: [26] .shstrtab STRTAB 0000000000000000 0018d4 0000f9 00 0 0 1 +# +# PHDR: Program Headers: +# PHDR: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# PHDR: PHDR 0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R 0x8 +# PHDR: INTERP 0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R 0x1 +# PHDR: [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] +# PHDR: LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x0006e8 0x0006e8 R E 0x200000 +# PHDR: LOAD 0x000e20 0x0000000000600e20 0x0000000000600e20 0x0002e1 0x0002e9 RW 0x200000 +# PHDR: DYNAMIC 0x000e30 0x0000000000600e30 0x0000000000600e30 0x0001c0 0x0001c0 RW 0x8 +# PHDR: NOTE 0x000254 0x0000000000400254 0x0000000000400254 0x000044 0x000044 R 0x4 +# PHDR: GNU_EH_FRAME 0x0005b4 0x00000000004005b4 0x00000000004005b4 0x00003c 0x00003c R 0x4 +# PHDR: GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 +# PHDR: GNU_RELRO 0x000e20 0x0000000000600e20 0x0000000000600e20 0x0001e0 0x0001e0 R 0x1 +# +# DATA: 217 Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -139,6 +139,8 @@ Optional BuildIdLinkInput; Optional BuildIdLinkOutput; Optional ExtractPartition; + Optional GapFill; + Optional PadTo; StringRef SplitDWO; StringRef SymbolsPrefix; StringRef AllocSectionsPrefix; Index: llvm/tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.cpp +++ llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -554,7 +554,21 @@ InputArgs.getLastArgValue(OBJCOPY_prefix_alloc_sections); if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition)) Config.ExtractPartition = Arg->getValue(); - + if (auto Arg = InputArgs.getLastArg(OBJCOPY_gap_fill)) { + // Truncation is not allowed. + auto Val = getAsInteger(Arg->getValue()); + if (!Val) + return createStringError(Val.getError(), "bad number for --gap-fill: %s", + Arg->getValue()); + Config.GapFill = *Val; + } + if (auto Arg = InputArgs.getLastArg(OBJCOPY_pad_to)) { + auto addr = getAsInteger(Arg->getValue()); + if (!addr) + return createStringError(addr.getError(), "bad address for --pad-to: %s", + Arg->getValue()); + Config.PadTo = *addr; + } for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { if (!StringRef(Arg->getValue()).contains('=')) return createStringError(errc::invalid_argument, Index: llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -583,6 +583,153 @@ return Obj.removeSections(Config.AllowBrokenLinks, RemovePred); } +static Error padSections(const CopyConfig &Config, Object &Obj) { + SmallVector LoadableSections; + for (SectionBase &Sec : Obj.sections()) { + if ((Sec.Flags & SectionFlag::SecLoad) && (Sec.Type != SHT_NOBITS)) + LoadableSections.push_back(&Sec); + } + + if (LoadableSections.empty()) + return Error::success(); + + llvm::stable_sort(LoadableSections, + [](const SectionBase *LHS, const SectionBase *RHS) { + if (LHS->Addr < RHS->Addr) + return true; + if (LHS->Addr > RHS->Addr) + return false; + return (LHS->Size < RHS->Size); + }); + + std::map SectionGapMap; + // Default filled value for '--pad-to' option. + uint8_t Value = 0; + + // Fill the gaps if the section's not in any segement. Otherwise just record + // the gaps. + auto setSectionGaps = [&SectionGapMap](SectionBase *Sec, uint32_t Value, + uint64_t GapsSize) { + if (Sec->ParentSegment) + SectionGapMap[Sec] = GapsSize; + else { + Sec->Size += GapsSize; + uint8_t *N = new uint8_t[Sec->Size]; + llvm::copy(Sec->OriginalData, &N[0]); + memset(N + Sec->OriginalData.size(), Value, GapsSize); + Sec->OriginalData = makeArrayRef(N, Sec->Size); + } + }; + + if (Config.GapFill) { + Value = Config.GapFill.getValue(); + for (SmallVectorImpl::const_iterator + I = LoadableSections.begin(), + E = LoadableSections.end() - 1; + I != E; ++I) { + if ((*I)->Size == 0) + continue; + uint64_t GapBegin = (*I)->Addr + (*I)->Size; + uint64_t GapEnd = (*(I + 1))->Addr; + if (GapEnd <= GapBegin) + continue; + setSectionGaps(*I, Value, GapEnd - GapBegin); + } + } + + if (Config.PadTo) { + uint64_t LastSec = *(LoadableSections.end() - 1); + uint64_t SecEndAddr = LastSec->Addr + LastSec->Size; + if (LastSec->Size && SecEndAddr < Config.PadTo.getValue()) + setSectionGaps(LastSec, Value, Config.PadTo.getValue() - SecEndAddr); + } + + // Populate section and segment content. + // First, resize the section and its containing segment if needed. + std::map NewSegments; + for (auto &It : SectionGapMap) { + SectionBase *Sec = It.first; + uint64_t GapsSize = It.second; + // Two adjacent sections might overlap in the same segment. Adjust its + // layout in next step. + Sec->Size += GapsSize; + + auto SecParent = Sec->ParentSegment; + assert(SecParent != nullptr); + for (auto &Seg : Obj.segments()) { + auto SegmentEndAddr = Seg.VAddr + Seg.Contents.size(); + if (Sec->Addr >= Seg.VAddr && Sec->Addr < SegmentEndAddr && + (Sec->Addr + Sec->Size) > SegmentEndAddr) { + Seg.FileSize += GapsSize; + Seg.MemSize += GapsSize; + + if (!SecParent->ParentSegment && SecParent == &Seg) { + if (NewSegments.find(SecParent) == NewSegments.end()) { + NewSegments[SecParent] = new uint8_t[SecParent->FileSize]; + llvm::copy(SecParent->getContents(), NewSegments[SecParent]); + } + } + } + } + } + + if (NewSegments.empty()) + return Error::success(); + + // Second, adjust segments and sections layouts after the resizing. + auto PreSec = *(LoadableSections.end() - 1); + auto Index = PreSec->Index; + auto PreSecEnd = PreSec->OriginalOffset + PreSec->Size; + Index++; + Twine Msg; + for (auto Size = Obj.sections().size(); Index < Size; ++Index) { + auto CurSec = Obj.sections().getSection(Index, Msg); + if (CurSec->OriginalOffset < PreSecEnd) { + CurSec->OriginalOffset = PreSecEnd; + PreSecEnd = CurSec->OriginalOffset + CurSec->Size; + } + } + + // Third, set the new segment's content. + for (auto &It : SectionGapMap) { + SectionBase *Sec = It.first; + uint64_t GapsSize = It.second; + auto SecParent = It.first->ParentSegment; + const auto Iter = NewSegments.find(SecParent); + if (Iter != NewSegments.end()) { + uint64_t Offset = Sec->OriginalOffset - SecParent->OriginalOffset; + std::memset(Iter->second + Offset + Sec->OriginalData.size(), Value, + GapsSize); + } + } + + for (auto &It : NewSegments) + It.first->Contents = makeArrayRef(It.second, It.first->FileSize); + + // Fourth, set the new segment's child content. + for (auto &Seg : Obj.segments()) { + auto SegParent = Seg.ParentSegment; + const auto Iter = NewSegments.find(SegParent); + if (Iter != NewSegments.end()) { + uint64_t Offset = Seg.OriginalOffset - SegParent->OriginalOffset; + Seg.Contents = makeArrayRef(Iter->second + Offset, Seg.FileSize); + } + } + + // Last step, set the section content. + for (auto &Sec : Obj.sections()) { + auto SecParent = Sec.ParentSegment; + const auto Iter = NewSegments.find(SecParent); + if (Iter != NewSegments.end() && Sec.Type != SHT_NOBITS) { + uint64_t Offset = + Sec.OriginalOffset - SecParent->OriginalOffset + SecParent->Offset; + Sec.OriginalData = makeArrayRef(Iter->second + Offset, Sec.Size); + } + } + + return Error::success(); +} + // This function handles the high level operations of GNU objcopy including // handling command line options. It's important to outline certain properties // we expect to hold of the command line operations. Any operation that "keeps" @@ -718,6 +865,11 @@ Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); } + if ((Config.GapFill || Config.PadTo) && Obj.sections().size()) { + if (Error E = padSections(Config, Obj)) + return E; + } + if (Config.EntryExpr) Obj.Entry = Config.EntryExpr(Obj.Entry); return Error::success(); Index: llvm/tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -223,3 +223,13 @@ "compatibility: debug, constructor, warning, indirect, synthetic, " "unique-object, before.">, MetaVarName<"name=[section:]value[,flags]">; + +defm gap_fill + : Eq<"gap-fill", + "Fill gaps between two loadable sections with .">, + MetaVarName<"val">; + +defm pad_to + : Eq<"pad-to", + "Pad the last loadable section up to with the value specified by --gap-fill if present or zero by default.">, + MetaVarName<"addr">;