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

Reviewers: alexshap, rupprecht, jhenderson

Reviewed By: alexshap, jhenderson

Subscribers: mgorny, jakehehrlich, abrachet, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D66283
This commit is contained in:
Seiya Nuta 2019-12-16 14:05:06 +09:00
parent d25db94fa7
commit 9e119ad69d
No known key found for this signature in database
GPG Key ID: 82737BC094EC908C
6 changed files with 326 additions and 24 deletions

View File

@ -43,6 +43,9 @@ multiple file formats.
starts with ".note". Otherwise, it will have type `SHT_PROGBITS`. Can be
specified multiple times to add multiple sections.
For MachO objects, ``<section>`` must be formatted as
``<segment name>,<section name>``.
.. option:: --binary-architecture <arch>, -B
Ignored for compatibility.

View File

@ -0,0 +1,175 @@
## 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

@ -106,15 +106,65 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
SecName.str().c_str());
}
static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFile(Filename);
if (!BufOrErr)
return createFileError(Filename, errorCodeToError(BufOrErr.getError()));
std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr);
std::pair<StringRef, StringRef> Pair = SecName.split(',');
StringRef TargetSegName = Pair.first;
Section Sec(TargetSegName, Pair.second);
Sec.Content = Obj.NewSectionsContents.save(Buf->getBuffer());
// Add the a section into an existing segment.
for (LoadCommand &LC : Obj.LoadCommands) {
Optional<StringRef> SegName = LC.getSegmentName();
if (SegName && SegName == TargetSegName) {
LC.Sections.push_back(Sec);
return Error::success();
}
}
// There's no segment named TargetSegName. Create a new load command and
// Insert a new section into it.
LoadCommand &NewSegment = Obj.addSegment(TargetSegName);
NewSegment.Sections.push_back(Sec);
return Error::success();
}
// isValidMachOCannonicalName returns success if Name is a MachO cannonical name
// ("<segment>,<section>") and lengths of both segment and section names are
// valid.
Error isValidMachOCannonicalName(StringRef Name) {
if (Name.count(',') != 1)
return createStringError(errc::invalid_argument,
"invalid section name '%s' (should be formatted "
"as '<segment name>,<section name>')",
Name.str().c_str());
std::pair<StringRef, StringRef> Pair = Name.split(',');
if (Pair.first.size() > 16)
return createStringError(errc::invalid_argument,
"too long segment name: '%s'",
Pair.first.str().c_str());
if (Pair.second.size() > 16)
return createStringError(errc::invalid_argument,
"too long section name: '%s'",
Pair.second.str().c_str());
return Error::success();
}
static Error handleArgs(const CopyConfig &Config, Object &Obj) {
if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() ||
Config.BuildIdLinkInput || Config.BuildIdLinkOutput ||
!Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() ||
!Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() ||
!Config.KeepSection.empty() || Config.NewSymbolVisibility ||
!Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() ||
!Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() ||
!Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() ||
!Config.AllocSectionsPrefix.empty() || !Config.KeepSection.empty() ||
Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() ||
!Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() ||
!Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() ||
!Config.SectionsToRename.empty() ||
!Config.UnneededSymbolsToRemove.empty() ||
!Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() ||
Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden ||
@ -148,6 +198,16 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
return E;
}
for (const auto &Flag : Config.AddSection) {
std::pair<StringRef, StringRef> SecPair = Flag.split("=");
StringRef SecName = SecPair.first;
StringRef File = SecPair.second;
if (Error E = isValidMachOCannonicalName(SecName))
return E;
if (Error E = addSection(SecName, File, Obj))
return E;
}
for (StringRef RPath : Config.RPathToAdd) {
for (LoadCommand &LC : Obj.LoadCommands) {
if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH &&

View File

@ -29,13 +29,9 @@ void MachOReader::readHeader(Object &O) const {
template <typename SectionType>
Section constructSectionCommon(SectionType Sec) {
Section S;
S.Sectname =
StringRef(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname)))
.str();
S.Segname =
StringRef(Sec.segname, strnlen(Sec.segname, sizeof(Sec.sectname))).str();
S.CanonicalName = (Twine(S.Segname) + "," + S.Sectname).str();
StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname)));
StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname)));
Section S(SegName, SectName);
S.Addr = Sec.addr;
S.Size = Sec.size;
S.Offset = Sec.offset;

View File

@ -33,6 +33,47 @@ void Object::addLoadCommand(LoadCommand LC) {
LoadCommands.push_back(std::move(LC));
}
template <typename SegmentType>
static void constructSegment(SegmentType &Seg,
llvm::MachO::LoadCommandType CmdType,
StringRef SegName) {
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());
}
LoadCommand &Object::addSegment(StringRef SegName) {
LoadCommand LC;
if (is64Bit())
constructSegment(LC.MachOLoadCommand.segment_command_64_data,
MachO::LC_SEGMENT_64, SegName);
else
constructSegment(LC.MachOLoadCommand.segment_command_data,
MachO::LC_SEGMENT, SegName);
LoadCommands.push_back(LC);
return LoadCommands.back();
}
/// Extracts a segment name from a string which is possibly non-null-terminated.
static StringRef extractSegmentName(const char *SegName) {
return StringRef(SegName,
strnlen(SegName, sizeof(MachO::segment_command::segname)));
}
Optional<StringRef> LoadCommand::getSegmentName() const {
const MachO::macho_load_command &MLC = MachOLoadCommand;
switch (MLC.load_command_data.cmd) {
case MachO::LC_SEGMENT:
return extractSegmentName(MLC.segment_command_data.segname);
case MachO::LC_SEGMENT_64:
return extractSegmentName(MLC.segment_command_64_data.segname);
default:
return None;
}
}
} // end namespace macho
} // end namespace objcopy
} // end namespace llvm

View File

@ -14,6 +14,7 @@
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/ObjectYAML/DWARFYAML.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/YAMLTraits.h"
#include <cstdint>
#include <string>
@ -36,24 +37,32 @@ struct MachHeader {
struct RelocationInfo;
struct Section {
std::string Sectname;
std::string Segname;
std::string Sectname;
// CanonicalName is a string formatted as “<Segname>,<Sectname>".
std::string CanonicalName;
uint64_t Addr;
uint64_t Size;
uint32_t Offset;
uint32_t Align;
uint32_t RelOff;
uint32_t NReloc;
uint32_t Flags;
uint32_t Reserved1;
uint32_t Reserved2;
uint32_t Reserved3;
uint64_t Addr = 0;
uint64_t Size = 0;
uint32_t Offset = 0;
uint32_t Align = 0;
uint32_t RelOff = 0;
uint32_t NReloc = 0;
uint32_t Flags = 0;
uint32_t Reserved1 = 0;
uint32_t Reserved2 = 0;
uint32_t Reserved3 = 0;
StringRef Content;
std::vector<RelocationInfo> Relocations;
Section(StringRef SegName, StringRef SectName)
: Segname(SegName), Sectname(SectName),
CanonicalName((Twine(SegName) + Twine(',') + SectName).str()) {}
Section(StringRef SegName, StringRef SectName, StringRef Content)
: Segname(SegName), Sectname(SectName),
CanonicalName((Twine(SegName) + Twine(',') + SectName).str()),
Content(Content) {}
MachO::SectionType getType() const {
return static_cast<MachO::SectionType>(Flags & MachO::SECTION_TYPE);
}
@ -81,6 +90,9 @@ struct LoadCommand {
// Section describes only sections' metadata and where to find the
// corresponding content inside the binary.
std::vector<Section> Sections;
// Returns the segment name if the load command is a segment command.
Optional<StringRef> getSegmentName() const;
};
// A symbol information. Fields which starts with "n_" are same as them in the
@ -275,8 +287,23 @@ struct Object {
/// The index LC_FUNCTION_STARTS load comamnd if present.
Optional<size_t> FunctionStartsCommandIndex;
BumpPtrAllocator Alloc;
StringSaver NewSectionsContents;
Object() : NewSectionsContents(Alloc) {}
void removeSections(function_ref<bool(const Section &)> ToRemove);
void addLoadCommand(LoadCommand LC);
/// Creates a new segment load command in the object and returns a reference
/// to the newly created load command. The caller should verify that SegName
/// is not too long (SegName.size() should be less than or equal to 16).
LoadCommand &addSegment(StringRef SegName);
bool is64Bit() const {
return Header.Magic == MachO::MH_MAGIC_64 ||
Header.Magic == MachO::MH_CIGAM_64;
}
};
} // end namespace macho