forked from OSchip/llvm-project
[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:
parent
d25db94fa7
commit
9e119ad69d
|
@ -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.
|
||||
|
|
|
@ -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: }
|
|
@ -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 &&
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue