Skip to content

Commit 9e119ad

Browse files
committed
[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
1 parent d25db94 commit 9e119ad

File tree

6 files changed

+326
-24
lines changed

6 files changed

+326
-24
lines changed

llvm/docs/CommandGuide/llvm-objcopy.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ multiple file formats.
4343
starts with ".note". Otherwise, it will have type `SHT_PROGBITS`. Can be
4444
specified multiple times to add multiple sections.
4545

46+
For MachO objects, ``<section>`` must be formatted as
47+
``<segment name>,<section name>``.
48+
4649
.. option:: --binary-architecture <arch>, -B
4750

4851
Ignored for compatibility.
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
## Show that llvm-objcopy adds a new section into the object if
2+
## --add-section is given.
3+
4+
# RUN: yaml2obj --docnum=1 %s -o %t.64bit
5+
# RUN: yaml2obj --docnum=2 %s -o %t.32bit
6+
# RUN: echo -n abcdefg > %t.data
7+
8+
## Error case 1: Nonexistent input file is specified by --add-section.
9+
# RUN: not llvm-objcopy --add-section __TEXT,__text=%t.missing %t.64bit %t.nonexistent-file 2>&1 \
10+
# RUN: | FileCheck %s -DINPUT=%t.64bit -DSECTION_DATA_FILE=%t.missing --check-prefix=NONEXSITENT-FILE
11+
# NONEXSITENT-FILE: error: '[[INPUT]]': '[[SECTION_DATA_FILE]]': {{[Nn]}}o such file or directory
12+
13+
## Error case 2: Too long segment name.
14+
# RUN: not llvm-objcopy --add-section __TOOOOOOOOO_LONG,__text=%t.data %t.64bit %t.too-long-seg-name 2>&1 \
15+
# RUN: | FileCheck %s -DINPUT=%t.64bit --check-prefix=TOO-LONG-SEG-NAME
16+
# TOO-LONG-SEG-NAME: error: '[[INPUT]]': too long segment name: '__TOOOOOOOOO_LONG'
17+
18+
## Case 1: Add a new section into an existing segment.
19+
# RUN: llvm-objcopy --add-section __TEXT,__text=%t.data %t.64bit %t.out1.64bit
20+
# RUN: llvm-objcopy --add-section __TEXT,__text=%t.data %t.32bit %t.out1.32bit
21+
# RUN: llvm-readobj --sections --section-data %t.out1.64bit \
22+
# RUN: | FileCheck %s --check-prefixes=64BIT,COMMON,CASE1
23+
# RUN: llvm-readobj --sections --section-data %t.out1.32bit \
24+
# RUN: | FileCheck %s --check-prefixes=32BIT,COMMON,CASE1
25+
26+
## Case 2: Add a new section into a nonexistent segment.
27+
# RUN: llvm-objcopy --add-section __FOO,__bar=%t.data %t.64bit %t.out2.64bit
28+
# RUN: llvm-objcopy --add-section __FOO,__bar=%t.data %t.32bit %t.out2.32bit
29+
# RUN: llvm-readobj --sections --section-data --macho-segment %t.out2.64bit \
30+
# RUN: | FileCheck %s --check-prefixes=64BIT,COMMON,CASE2,CASE2-64BIT
31+
# RUN: llvm-readobj --sections --section-data --macho-segment %t.out2.32bit \
32+
# RUN: | FileCheck %s --check-prefixes=32BIT,COMMON,CASE2,CASE2-32BIT
33+
34+
## Case 3: Add a new section with /dev/null.
35+
# RUN: llvm-objcopy --add-section __TEXT,__text=/dev/null %t.64bit %t.devnull
36+
# RUN: llvm-readobj --sections --section-data %t.devnull \
37+
# RUN: | FileCheck %s --check-prefixes=64BIT,COMMON,DEVNULL
38+
39+
## 64-bit binary
40+
--- !mach-o
41+
FileHeader:
42+
magic: 0xFEEDFACF
43+
cputype: 0x01000007
44+
cpusubtype: 0x00000003
45+
filetype: 0x00000001
46+
ncmds: 1
47+
sizeofcmds: 152
48+
flags: 0x00002000
49+
reserved: 0x00000000
50+
LoadCommands:
51+
- cmd: LC_SEGMENT_64
52+
cmdsize: 152
53+
segname: ''
54+
vmaddr: 0
55+
vmsize: 4
56+
fileoff: 184
57+
filesize: 4
58+
maxprot: 7
59+
initprot: 7
60+
nsects: 1
61+
flags: 0
62+
Sections:
63+
- sectname: __text
64+
segname: __TEXT
65+
addr: 0x0000000000000000
66+
content: 'AABBCCDD'
67+
size: 4
68+
offset: 184
69+
align: 0
70+
reloff: 0x00000000
71+
nreloc: 0
72+
flags: 0x80000400
73+
reserved1: 0x00000000
74+
reserved2: 0x00000000
75+
reserved3: 0x00000000
76+
77+
## 32-bit binary
78+
--- !mach-o
79+
FileHeader:
80+
magic: 0xFEEDFACE
81+
cputype: 0x00000007
82+
cpusubtype: 0x00000003
83+
filetype: 0x00000001
84+
ncmds: 1
85+
sizeofcmds: 124
86+
flags: 0x00002000
87+
LoadCommands:
88+
- cmd: LC_SEGMENT
89+
cmdsize: 124
90+
segname: ''
91+
vmaddr: 0
92+
vmsize: 4
93+
fileoff: 184
94+
filesize: 4
95+
maxprot: 7
96+
initprot: 7
97+
nsects: 1
98+
flags: 0
99+
Sections:
100+
- sectname: __text
101+
segname: __TEXT
102+
addr: 0x0000000000000000
103+
content: 'AABBCCDD'
104+
size: 4
105+
offset: 184
106+
align: 0
107+
reloff: 0x00000000
108+
nreloc: 0
109+
flags: 0x80000400
110+
reserved1: 0x00000000
111+
reserved2: 0x00000000
112+
reserved3: 0x00000000
113+
114+
# COMMON: Index: 0
115+
# COMMON-NEXT: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
116+
# COMMON-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
117+
# COMMON: Index: 1
118+
# CASE1-NEXT: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
119+
# CASE1-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
120+
# CASE2-NEXT: Name: __bar (5F 5F 62 61 72 00 00 00 00 00 00 00 00 00 00 00)
121+
# CASE2-NEXT: Segment: __FOO (5F 5F 46 4F 4F 00 00 00 00 00 00 00 00 00 00 00)
122+
# DEVNULL-NEXT: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00)
123+
# DEVNULL-NEXT: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00)
124+
# COMMON-NEXT: Address: 0x0
125+
# CASE1-NEXT: Size: 0x7
126+
# CASE2-NEXT: Size: 0x7
127+
# DEVNULL-NEXT: Size: 0x0
128+
# 64BIT-NEXT: Offset: 340
129+
# 32BIT-NEXT: Offset: 280
130+
# COMMON-NEXT: Alignment: 0
131+
# COMMON-NEXT: RelocationOffset: 0x0
132+
# COMMON-NEXT: RelocationCount: 0
133+
# COMMON-NEXT: Type: Regular (0x0)
134+
# COMMON-NEXT: Attributes [ (0x0)
135+
# COMMON-NEXT: ]
136+
# COMMON-NEXT: Reserved1: 0x0
137+
# COMMON-NEXT: Reserved2: 0x0
138+
# 64BIT-NEXT: Reserved3: 0x0
139+
# COMMON-NEXT: SectionData (
140+
# CASE1-NEXT: 0000: 61626364 656667 |abcdefg|
141+
# CASE2-NEXT: 0000: 61626364 656667 |abcdefg|
142+
# COMMON-NEXT: )
143+
144+
# CASE2: Segment {
145+
# CASE2-64BIT-NEXT: Cmd: LC_SEGMENT_64
146+
# CASE2-32BIT-NEXT: Cmd: LC_SEGMENT{{$}}
147+
# CASE2-NEXT: Name:
148+
# CASE2-64BIT-NEXT: Size: 152
149+
# CASE2-32BIT-NEXT: Size: 124
150+
# CASE2-NEXT: vmaddr: 0x0
151+
# CASE2-NEXT: vmsize: 0x4
152+
# CASE2-64BIT-NEXT: fileoff: 336
153+
# CASE2-32BIT-NEXT: fileoff: 276
154+
# CASE2-NEXT: filesize: 4
155+
# CASE2-NEXT: maxprot: rwx
156+
# CASE2-NEXT: initprot: rwx
157+
# CASE2-NEXT: nsects: 1
158+
# CASE2-NEXT: flags: 0x0
159+
# CASE2-NEXT: }
160+
# CASE2-NEXT: Segment {
161+
# CASE2-64BIT-NEXT: Cmd: LC_SEGMENT_64
162+
# CASE2-32BIT-NEXT: Cmd: LC_SEGMENT{{$}}
163+
# CASE2-NEXT: Name: __FOO
164+
# CASE2-64BIT-NEXT: Size: 152
165+
# CASE2-32BIT-NEXT: Size: 124
166+
# CASE2-NEXT: vmaddr: 0x0
167+
# CASE2-NEXT: vmsize: 0x7
168+
# CASE2-64BIT-NEXT: fileoff: 340
169+
# CASE2-32BIT-NEXT: fileoff: 280
170+
# CASE2-NEXT: filesize: 7
171+
# CASE2-NEXT: maxprot: ---
172+
# CASE2-NEXT: initprot: ---
173+
# CASE2-NEXT: nsects: 1
174+
# CASE2-NEXT: flags: 0x0
175+
# CASE2-NEXT: }

llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,65 @@ static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
106106
SecName.str().c_str());
107107
}
108108

109+
static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) {
110+
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
111+
MemoryBuffer::getFile(Filename);
112+
if (!BufOrErr)
113+
return createFileError(Filename, errorCodeToError(BufOrErr.getError()));
114+
std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr);
115+
116+
std::pair<StringRef, StringRef> Pair = SecName.split(',');
117+
StringRef TargetSegName = Pair.first;
118+
Section Sec(TargetSegName, Pair.second);
119+
Sec.Content = Obj.NewSectionsContents.save(Buf->getBuffer());
120+
121+
// Add the a section into an existing segment.
122+
for (LoadCommand &LC : Obj.LoadCommands) {
123+
Optional<StringRef> SegName = LC.getSegmentName();
124+
if (SegName && SegName == TargetSegName) {
125+
LC.Sections.push_back(Sec);
126+
return Error::success();
127+
}
128+
}
129+
130+
// There's no segment named TargetSegName. Create a new load command and
131+
// Insert a new section into it.
132+
LoadCommand &NewSegment = Obj.addSegment(TargetSegName);
133+
NewSegment.Sections.push_back(Sec);
134+
return Error::success();
135+
}
136+
137+
// isValidMachOCannonicalName returns success if Name is a MachO cannonical name
138+
// ("<segment>,<section>") and lengths of both segment and section names are
139+
// valid.
140+
Error isValidMachOCannonicalName(StringRef Name) {
141+
if (Name.count(',') != 1)
142+
return createStringError(errc::invalid_argument,
143+
"invalid section name '%s' (should be formatted "
144+
"as '<segment name>,<section name>')",
145+
Name.str().c_str());
146+
147+
std::pair<StringRef, StringRef> Pair = Name.split(',');
148+
if (Pair.first.size() > 16)
149+
return createStringError(errc::invalid_argument,
150+
"too long segment name: '%s'",
151+
Pair.first.str().c_str());
152+
if (Pair.second.size() > 16)
153+
return createStringError(errc::invalid_argument,
154+
"too long section name: '%s'",
155+
Pair.second.str().c_str());
156+
return Error::success();
157+
}
158+
109159
static Error handleArgs(const CopyConfig &Config, Object &Obj) {
110160
if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() ||
111161
Config.BuildIdLinkInput || Config.BuildIdLinkOutput ||
112162
!Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() ||
113-
!Config.AllocSectionsPrefix.empty() || !Config.AddSection.empty() ||
114-
!Config.KeepSection.empty() || Config.NewSymbolVisibility ||
115-
!Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() ||
116-
!Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() ||
117-
!Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() ||
163+
!Config.AllocSectionsPrefix.empty() || !Config.KeepSection.empty() ||
164+
Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() ||
165+
!Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() ||
166+
!Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() ||
167+
!Config.SectionsToRename.empty() ||
118168
!Config.UnneededSymbolsToRemove.empty() ||
119169
!Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() ||
120170
Config.ExtractDWO || Config.KeepFileSymbols || Config.LocalizeHidden ||
@@ -148,6 +198,16 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj) {
148198
return E;
149199
}
150200

201+
for (const auto &Flag : Config.AddSection) {
202+
std::pair<StringRef, StringRef> SecPair = Flag.split("=");
203+
StringRef SecName = SecPair.first;
204+
StringRef File = SecPair.second;
205+
if (Error E = isValidMachOCannonicalName(SecName))
206+
return E;
207+
if (Error E = addSection(SecName, File, Obj))
208+
return E;
209+
}
210+
151211
for (StringRef RPath : Config.RPathToAdd) {
152212
for (LoadCommand &LC : Obj.LoadCommands) {
153213
if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH &&

llvm/tools/llvm-objcopy/MachO/MachOReader.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,9 @@ void MachOReader::readHeader(Object &O) const {
2929

3030
template <typename SectionType>
3131
Section constructSectionCommon(SectionType Sec) {
32-
Section S;
33-
S.Sectname =
34-
StringRef(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname)))
35-
.str();
36-
S.Segname =
37-
StringRef(Sec.segname, strnlen(Sec.segname, sizeof(Sec.sectname))).str();
38-
S.CanonicalName = (Twine(S.Segname) + "," + S.Sectname).str();
32+
StringRef SegName(Sec.segname, strnlen(Sec.segname, sizeof(Sec.segname)));
33+
StringRef SectName(Sec.sectname, strnlen(Sec.sectname, sizeof(Sec.sectname)));
34+
Section S(SegName, SectName);
3935
S.Addr = Sec.addr;
4036
S.Size = Sec.size;
4137
S.Offset = Sec.offset;

llvm/tools/llvm-objcopy/MachO/Object.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,47 @@ void Object::addLoadCommand(LoadCommand LC) {
3333
LoadCommands.push_back(std::move(LC));
3434
}
3535

36+
template <typename SegmentType>
37+
static void constructSegment(SegmentType &Seg,
38+
llvm::MachO::LoadCommandType CmdType,
39+
StringRef SegName) {
40+
assert(SegName.size() <= sizeof(Seg.segname) && "too long segment name");
41+
memset(&Seg, 0, sizeof(SegmentType));
42+
Seg.cmd = CmdType;
43+
strncpy(Seg.segname, SegName.data(), SegName.size());
44+
}
45+
46+
LoadCommand &Object::addSegment(StringRef SegName) {
47+
LoadCommand LC;
48+
if (is64Bit())
49+
constructSegment(LC.MachOLoadCommand.segment_command_64_data,
50+
MachO::LC_SEGMENT_64, SegName);
51+
else
52+
constructSegment(LC.MachOLoadCommand.segment_command_data,
53+
MachO::LC_SEGMENT, SegName);
54+
55+
LoadCommands.push_back(LC);
56+
return LoadCommands.back();
57+
}
58+
59+
/// Extracts a segment name from a string which is possibly non-null-terminated.
60+
static StringRef extractSegmentName(const char *SegName) {
61+
return StringRef(SegName,
62+
strnlen(SegName, sizeof(MachO::segment_command::segname)));
63+
}
64+
65+
Optional<StringRef> LoadCommand::getSegmentName() const {
66+
const MachO::macho_load_command &MLC = MachOLoadCommand;
67+
switch (MLC.load_command_data.cmd) {
68+
case MachO::LC_SEGMENT:
69+
return extractSegmentName(MLC.segment_command_data.segname);
70+
case MachO::LC_SEGMENT_64:
71+
return extractSegmentName(MLC.segment_command_64_data.segname);
72+
default:
73+
return None;
74+
}
75+
}
76+
3677
} // end namespace macho
3778
} // end namespace objcopy
3879
} // end namespace llvm

0 commit comments

Comments
 (0)