[llvm-objcopy][MachO] Fix --add-section

This diff fixes --add-section functionality and simplifies the tests organization.

Test plan: make check-all

Differential revision: https://reviews.llvm.org/D87497
This commit is contained in:
Alexander Shaposhnikov 2020-09-24 01:48:21 -07:00
parent 98756d865b
commit e60a760b7d
7 changed files with 301 additions and 178 deletions

View File

@ -0,0 +1,114 @@
## Show that llvm-objcopy adds a new section into a 32-bit object file if
## --add-section is given.
# RUN: yaml2obj %s -o %t
# RUN: echo -n abcdefg > %t.data
## Case 1: Add a new section into an existing segment.
# RUN: llvm-objcopy --add-section __TEXT,__bar=%t.data %t %t.out1
# RUN: llvm-readobj --sections --section-data %t.out1 \
# RUN: | FileCheck %s --check-prefixes=COMMON,CASE1
## Case 2: Add a new section into a nonexistent segment.
# RUN: llvm-objcopy --add-section __FOO,__bar=%t.data %t %t.out2
# RUN: llvm-readobj --sections --section-data %t.out2 \
# RUN: | FileCheck %s --check-prefixes=COMMON,CASE2
## Case 3: Add a new section into an existing segment using /dev/null as an input.
# RUN: llvm-objcopy --add-section __TEXT,__bar=/dev/null %t %t.out3
# RUN: llvm-readobj --sections --section-data %t.out3 \
# RUN: | FileCheck %s --check-prefixes=COMMON,CASE3
--- !mach-o
FileHeader:
magic: 0xFEEDFACE
cputype: 0x00000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 1
sizeofcmds: 124
flags: 0x00002000
LoadCommands:
- cmd: LC_SEGMENT
cmdsize: 124
segname: __TEXT
vmaddr: 0
vmsize: 4
fileoff: 184
filesize: 4
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
content: 'AABBCCDD'
size: 4
offset: 184
align: 0
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
# COMMON: Index: 0
# COMMON-NEXT: Name: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
# COMMON-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
# COMMON-NEXT: Address: 0x0
# COMMON-NEXT: Size: 0x4
# COMMON-NEXT: Offset:
# COMMON-NEXT: Alignment: 0
# COMMON-NEXT: RelocationOffset: 0x0
# COMMON-NEXT: RelocationCount: 0
# COMMON-NEXT: Type: Regular (0x0)
# COMMON-NEXT: Attributes [ (0x800004)
# COMMON-NEXT: PureInstructions (0x800000)
# COMMON-NEXT: SomeInstructions (0x4)
# COMMON-NEXT: ]
# COMMON-NEXT: Reserved1: 0x0
# COMMON-NEXT: Reserved2: 0x0
# COMMON-NEXT: SectionData (
# COMMON-NEXT: 0000: AABBCCDD |....|
# COMMON-NEXT: )
# COMMON: Index: 1
# COMMON-NEXT: Name: __bar (5F 5F 62 61 72 00 00 00 00 00 00 00 00 00 00 00)
# CASE1-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
# CASE1-NEXT: Address: 0x4
# CASE1-NEXT: Size: 0x7
# CASE1-NEXT: Offset: 224
# CASE2-NEXT: Segment: __FOO (5F 5F 46 4F 4F 00 00 00 00 00 00 00 00 00 00 00)
# CASE2-NEXT: Address: 0x98
# CASE2-NEXT: Size: 0x7
# CASE2-NEXT: Offset: 280
# CASE3-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
# CASE3-NEXT: Address: 0x4
# CASE3-NEXT: Size: 0x0
# CASE3-NEXT: Offset: 224
# COMMON-NEXT: Alignment: 0
# COMMON-NEXT: RelocationOffset: 0x0
# COMMON-NEXT: RelocationCount: 0
# COMMON-NEXT: Type: Regular (0x0)
# COMMON-NEXT: Attributes [ (0x0)
# COMMON-NEXT: ]
# COMMON-NEXT: Reserved1: 0x0
# COMMON-NEXT: Reserved2: 0x0
# CASE1-NEXT: SectionData (
# CASE1-NEXT: 0000: 61626364 656667 |abcdefg|
# CASE1-NEXT: )
# CASE2-NEXT: SectionData (
# CASE2-NEXT: 0000: 61626364 656667 |abcdefg|
# CASE2-NEXT: )
# CASE3-NEXT: SectionData (
# CASE3-NEXT: )

View File

@ -0,0 +1,119 @@
## Show that llvm-objcopy adds a new section into a 64-bit object if
## --add-section is given.
# RUN: yaml2obj %s -o %t
# RUN: echo -n abcdefg > %t.data
## Case 1: Add a new section into an existing segment.
# RUN: llvm-objcopy --add-section __TEXT,__bar=%t.data %t %t.out1
# RUN: llvm-readobj --sections --section-data %t.out1 \
# RUN: | FileCheck %s --check-prefixes=COMMON,CASE1
## Case 2: Add a new section into a nonexistent segment.
# RUN: llvm-objcopy --add-section __FOO,__bar=%t.data %t %t.out2
# RUN: llvm-readobj --sections --section-data %t.out2 \
# RUN: | FileCheck %s --check-prefixes=COMMON,CASE2
# RUN: llvm-readobj --sections --section-data %t.out2 \
# RUN: | FileCheck %s --check-prefixes=COMMON,CASE2
## Case 3: Add a new section into an existing segment using /dev/null as an input.
# RUN: llvm-objcopy --add-section __TEXT,__bar=/dev/null %t %t.out3
# RUN: llvm-readobj --sections --section-data %t.out3 \
# RUN: | FileCheck %s --check-prefixes=COMMON,CASE3
--- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 1
sizeofcmds: 152
flags: 0x00002000
reserved: 0x00000000
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: __TEXT
vmaddr: 0
vmsize: 4
fileoff: 184
filesize: 4
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
content: 'AABBCCDD'
size: 4
offset: 184
align: 0
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
# COMMON: Index: 0
# COMMON-NEXT: Name: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
# COMMON-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
# COMMON-NEXT: Address: 0x0
# COMMON-NEXT: Size: 0x4
# COMMON-NEXT: Offset:
# COMMON-NEXT: Alignment: 0
# COMMON-NEXT: RelocationOffset: 0x0
# COMMON-NEXT: RelocationCount: 0
# COMMON-NEXT: Type: Regular (0x0)
# COMMON-NEXT: Attributes [ (0x800004)
# COMMON-NEXT: PureInstructions (0x800000)
# COMMON-NEXT: SomeInstructions (0x4)
# COMMON-NEXT: ]
# COMMON-NEXT: Reserved1: 0x0
# COMMON-NEXT: Reserved2: 0x0
# COMMON-NEXT: Reserved3: 0x0
# COMMON-NEXT: SectionData (
# COMMON-NEXT: 0000: AABBCCDD |....|
# COMMON-NEXT: )
# COMMON: Index: 1
# COMMON-NEXT: Name: __bar (5F 5F 62 61 72 00 00 00 00 00 00 00 00 00 00 00)
# CASE1-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
# CASE1-NEXT: Address: 0x4
# CASE1-NEXT: Size: 0x7
# CASE1-NEXT: Offset: 268
# CASE2: Segment: __FOO (5F 5F 46 4F 4F 00 00 00 00 00 00 00 00 00 00 00)
# CASE2-NEXT: Address: 0xB8
# CASE2-NEXT: Size: 0x7
# CASE2-NEXT: Offset: 340
# CASE3-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
# CASE3-NEXT: Address: 0x4
# CASE3-NEXT: Size: 0x0
# CASE3-NEXT: Offset: 268
# COMMON-NEXT: Alignment: 0
# COMMON-NEXT: RelocationOffset: 0x0
# COMMON-NEXT: RelocationCount: 0
# COMMON-NEXT: Type: Regular (0x0)
# COMMON-NEXT: Attributes [ (0x0)
# COMMON-NEXT: ]
# COMMON-NEXT: Reserved1: 0x0
# COMMON-NEXT: Reserved2: 0x0
# COMMON-NEXT: Reserved3: 0x0
# CASE1-NEXT: SectionData (
# CASE1-NEXT: 0000: 61626364 656667 |abcdefg|
# CASE1-NEXT: )
# CASE2-NEXT: SectionData (
# CASE2-NEXT: 0000: 61626364 656667 |abcdefg|
# CASE2-NEXT: )
# CASE3-NEXT: SectionData (
# CASE3-NEXT: )

View File

@ -0,0 +1,14 @@
## Test --add-section error messages.
# RUN: yaml2obj %p/Inputs/x86_64.yaml -o %t
# RUN: echo -n abcdefg > %t.data
## Error case 1: Nonexistent input file is specified by --add-section.
# RUN: not llvm-objcopy --add-section __TEXT,__text=%t.missing %t %t.nonexistent-file 2>&1 \
# RUN: | FileCheck %s -DINPUT=%t -DSECTION_DATA_FILE=%t.missing --check-prefix=NONEXSITENT-FILE
# NONEXSITENT-FILE: error: '[[INPUT]]': '[[SECTION_DATA_FILE]]': {{[Nn]}}o such file or directory
## Error case 2: Too long segment name.
# RUN: not llvm-objcopy --add-section __TOOOOOOOOO_LONG,__text=%t.data %t %t.too-long-seg-name 2>&1 \
# RUN: | FileCheck %s -DINPUT=%t --check-prefix=TOO-LONG-SEG-NAME
# TOO-LONG-SEG-NAME: error: '[[INPUT]]': too long segment name: '__TOOOOOOOOO_LONG'

View File

@ -1,175 +0,0 @@
## Show that llvm-objcopy adds a new section into the object if
## --add-section is given.
# RUN: yaml2obj --docnum=1 %s -o %t.64bit
# RUN: yaml2obj --docnum=2 %s -o %t.32bit
# RUN: echo -n abcdefg > %t.data
## Error case 1: Nonexistent input file is specified by --add-section.
# RUN: not llvm-objcopy --add-section __TEXT,__text=%t.missing %t.64bit %t.nonexistent-file 2>&1 \
# RUN: | FileCheck %s -DINPUT=%t.64bit -DSECTION_DATA_FILE=%t.missing --check-prefix=NONEXSITENT-FILE
# NONEXSITENT-FILE: error: '[[INPUT]]': '[[SECTION_DATA_FILE]]': {{[Nn]}}o such file or directory
## Error case 2: Too long segment name.
# RUN: not llvm-objcopy --add-section __TOOOOOOOOO_LONG,__text=%t.data %t.64bit %t.too-long-seg-name 2>&1 \
# RUN: | FileCheck %s -DINPUT=%t.64bit --check-prefix=TOO-LONG-SEG-NAME
# TOO-LONG-SEG-NAME: error: '[[INPUT]]': too long segment name: '__TOOOOOOOOO_LONG'
## Case 1: Add a new section into an existing segment.
# RUN: llvm-objcopy --add-section __TEXT,__text=%t.data %t.64bit %t.out1.64bit
# RUN: llvm-objcopy --add-section __TEXT,__text=%t.data %t.32bit %t.out1.32bit
# RUN: llvm-readobj --sections --section-data %t.out1.64bit \
# RUN: | FileCheck %s --check-prefixes=64BIT,COMMON,CASE1
# RUN: llvm-readobj --sections --section-data %t.out1.32bit \
# RUN: | FileCheck %s --check-prefixes=32BIT,COMMON,CASE1
## Case 2: Add a new section into a nonexistent segment.
# RUN: llvm-objcopy --add-section __FOO,__bar=%t.data %t.64bit %t.out2.64bit
# RUN: llvm-objcopy --add-section __FOO,__bar=%t.data %t.32bit %t.out2.32bit
# RUN: llvm-readobj --sections --section-data --macho-segment %t.out2.64bit \
# RUN: | FileCheck %s --check-prefixes=64BIT,COMMON,CASE2,CASE2-64BIT
# RUN: llvm-readobj --sections --section-data --macho-segment %t.out2.32bit \
# RUN: | FileCheck %s --check-prefixes=32BIT,COMMON,CASE2,CASE2-32BIT
## Case 3: Add a new section with /dev/null.
# RUN: llvm-objcopy --add-section __TEXT,__text=/dev/null %t.64bit %t.devnull
# RUN: llvm-readobj --sections --section-data %t.devnull \
# RUN: | FileCheck %s --check-prefixes=64BIT,COMMON,DEVNULL
## 64-bit binary
--- !mach-o
FileHeader:
magic: 0xFEEDFACF
cputype: 0x01000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 1
sizeofcmds: 152
flags: 0x00002000
reserved: 0x00000000
LoadCommands:
- cmd: LC_SEGMENT_64
cmdsize: 152
segname: ''
vmaddr: 0
vmsize: 4
fileoff: 184
filesize: 4
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
content: 'AABBCCDD'
size: 4
offset: 184
align: 0
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
## 32-bit binary
--- !mach-o
FileHeader:
magic: 0xFEEDFACE
cputype: 0x00000007
cpusubtype: 0x00000003
filetype: 0x00000001
ncmds: 1
sizeofcmds: 124
flags: 0x00002000
LoadCommands:
- cmd: LC_SEGMENT
cmdsize: 124
segname: ''
vmaddr: 0
vmsize: 4
fileoff: 184
filesize: 4
maxprot: 7
initprot: 7
nsects: 1
flags: 0
Sections:
- sectname: __text
segname: __TEXT
addr: 0x0000000000000000
content: 'AABBCCDD'
size: 4
offset: 184
align: 0
reloff: 0x00000000
nreloc: 0
flags: 0x80000400
reserved1: 0x00000000
reserved2: 0x00000000
reserved3: 0x00000000
# COMMON: Index: 0
# COMMON-NEXT: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
# COMMON-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
# COMMON: Index: 1
# CASE1-NEXT: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
# CASE1-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
# CASE2-NEXT: Name: __bar (5F 5F 62 61 72 00 00 00 00 00 00 00 00 00 00 00)
# CASE2-NEXT: Segment: __FOO (5F 5F 46 4F 4F 00 00 00 00 00 00 00 00 00 00 00)
# DEVNULL-NEXT: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
# DEVNULL-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
# COMMON-NEXT: Address: 0x0
# CASE1-NEXT: Size: 0x7
# CASE2-NEXT: Size: 0x7
# DEVNULL-NEXT: Size: 0x0
# 64BIT-NEXT: Offset: 340
# 32BIT-NEXT: Offset: 280
# COMMON-NEXT: Alignment: 0
# COMMON-NEXT: RelocationOffset: 0x0
# COMMON-NEXT: RelocationCount: 0
# COMMON-NEXT: Type: Regular (0x0)
# COMMON-NEXT: Attributes [ (0x0)
# COMMON-NEXT: ]
# COMMON-NEXT: Reserved1: 0x0
# COMMON-NEXT: Reserved2: 0x0
# 64BIT-NEXT: Reserved3: 0x0
# COMMON-NEXT: SectionData (
# CASE1-NEXT: 0000: 61626364 656667 |abcdefg|
# CASE2-NEXT: 0000: 61626364 656667 |abcdefg|
# COMMON-NEXT: )
# CASE2: Segment {
# CASE2-64BIT-NEXT: Cmd: LC_SEGMENT_64
# CASE2-32BIT-NEXT: Cmd: LC_SEGMENT{{$}}
# CASE2-NEXT: Name:
# CASE2-64BIT-NEXT: Size: 152
# CASE2-32BIT-NEXT: Size: 124
# CASE2-NEXT: vmaddr: 0x0
# CASE2-NEXT: vmsize: 0x4
# CASE2-64BIT-NEXT: fileoff: 336
# CASE2-32BIT-NEXT: fileoff: 276
# CASE2-NEXT: filesize: 4
# CASE2-NEXT: maxprot: rwx
# CASE2-NEXT: initprot: rwx
# CASE2-NEXT: nsects: 1
# CASE2-NEXT: flags: 0x0
# CASE2-NEXT: }
# CASE2-NEXT: Segment {
# CASE2-64BIT-NEXT: Cmd: LC_SEGMENT_64
# CASE2-32BIT-NEXT: Cmd: LC_SEGMENT{{$}}
# CASE2-NEXT: Name: __FOO
# CASE2-64BIT-NEXT: Size: 152
# CASE2-32BIT-NEXT: Size: 124
# CASE2-NEXT: vmaddr: 0x0
# CASE2-NEXT: vmsize: 0x7
# CASE2-64BIT-NEXT: fileoff: 340
# CASE2-32BIT-NEXT: fileoff: 280
# CASE2-NEXT: filesize: 7
# CASE2-NEXT: maxprot: ---
# CASE2-NEXT: initprot: ---
# CASE2-NEXT: nsects: 1
# CASE2-NEXT: flags: 0x0
# CASE2-NEXT: }

View File

@ -259,7 +259,11 @@ static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) {
for (LoadCommand &LC : Obj.LoadCommands) {
Optional<StringRef> SegName = LC.getSegmentName();
if (SegName && SegName == TargetSegName) {
uint64_t Addr = *LC.getSegmentVMAddr();
for (const std::unique_ptr<Section> &S : LC.Sections)
Addr = std::max(Addr, S->Addr + S->Size);
LC.Sections.push_back(std::make_unique<Section>(Sec));
LC.Sections.back()->Addr = Addr;
return Error::success();
}
}
@ -268,6 +272,7 @@ static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) {
// Insert a new section into it.
LoadCommand &NewSegment = Obj.addSegment(TargetSegName);
NewSegment.Sections.push_back(std::make_unique<Section>(Sec));
NewSegment.Sections.back()->Addr = *NewSegment.getSegmentVMAddr();
return Error::success();
}

View File

@ -111,24 +111,53 @@ Error Object::removeSections(
return Error::success();
}
uint64_t Object::nextAvailableSegmentAddress() const {
uint64_t HeaderSize =
is64Bit() ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
uint64_t Addr = HeaderSize + Header.SizeOfCmds;
for (const LoadCommand &LC : LoadCommands) {
const MachO::macho_load_command &MLC = LC.MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
case MachO::LC_SEGMENT:
Addr = std::max(Addr,
static_cast<uint64_t>(MLC.segment_command_data.vmaddr) +
MLC.segment_command_data.vmsize);
break;
case MachO::LC_SEGMENT_64:
Addr = std::max(Addr, MLC.segment_command_64_data.vmaddr +
MLC.segment_command_64_data.vmsize);
break;
default:
continue;
}
}
return Addr;
}
template <typename SegmentType>
static void constructSegment(SegmentType &Seg,
llvm::MachO::LoadCommandType CmdType,
StringRef SegName) {
StringRef SegName, uint64_t SegVMAddr) {
assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name");
memset(&Seg, 0, sizeof(SegmentType));
Seg.cmd = CmdType;
strncpy(Seg.segname, SegName.data(), SegName.size());
Seg.maxprot |=
(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);
Seg.initprot |=
(MachO::VM_PROT_READ | MachO::VM_PROT_WRITE | MachO::VM_PROT_EXECUTE);
Seg.vmaddr = SegVMAddr;
}
LoadCommand &Object::addSegment(StringRef SegName) {
LoadCommand LC;
const uint64_t SegVMAddr = nextAvailableSegmentAddress();
if (is64Bit())
constructSegment(LC.MachOLoadCommand.segment_command_64_data,
MachO::LC_SEGMENT_64, SegName);
MachO::LC_SEGMENT_64, SegName, SegVMAddr);
else
constructSegment(LC.MachOLoadCommand.segment_command_data,
MachO::LC_SEGMENT, SegName);
MachO::LC_SEGMENT, SegName, SegVMAddr);
LoadCommands.push_back(std::move(LC));
return LoadCommands.back();
@ -152,6 +181,18 @@ Optional<StringRef> LoadCommand::getSegmentName() const {
}
}
Optional<uint64_t> LoadCommand::getSegmentVMAddr() const {
const MachO::macho_load_command &MLC = MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
case MachO::LC_SEGMENT:
return MLC.segment_command_data.vmaddr;
case MachO::LC_SEGMENT_64:
return MLC.segment_command_64_data.vmaddr;
default:
return None;
}
}
} // end namespace macho
} // end namespace objcopy
} // end namespace llvm

View File

@ -94,6 +94,9 @@ struct LoadCommand {
// Returns the segment name if the load command is a segment command.
Optional<StringRef> getSegmentName() const;
// Returns the segment vm address if the load command is a segment command.
Optional<uint64_t> getSegmentVMAddr() const;
};
// A symbol information. Fields which starts with "n_" are same as them in the
@ -340,6 +343,8 @@ struct Object {
return Header.Magic == MachO::MH_MAGIC_64 ||
Header.Magic == MachO::MH_CIGAM_64;
}
uint64_t nextAvailableSegmentAddress() const;
};
} // end namespace macho