1 | //===- MachOObjcopy.cpp -----------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "llvm/ObjCopy/MachO/MachOObjcopy.h" |
10 | #include "Archive.h" |
11 | #include "MachOReader.h" |
12 | #include "MachOWriter.h" |
13 | #include "llvm/ADT/DenseSet.h" |
14 | #include "llvm/ObjCopy/CommonConfig.h" |
15 | #include "llvm/ObjCopy/MachO/MachOConfig.h" |
16 | #include "llvm/ObjCopy/MultiFormatConfig.h" |
17 | #include "llvm/ObjCopy/ObjCopy.h" |
18 | #include "llvm/Object/ArchiveWriter.h" |
19 | #include "llvm/Object/MachOUniversal.h" |
20 | #include "llvm/Object/MachOUniversalWriter.h" |
21 | #include "llvm/Support/Errc.h" |
22 | #include "llvm/Support/Error.h" |
23 | #include "llvm/Support/FileOutputBuffer.h" |
24 | #include "llvm/Support/Path.h" |
25 | #include "llvm/Support/SmallVectorMemoryBuffer.h" |
26 | |
27 | using namespace llvm; |
28 | using namespace llvm::objcopy; |
29 | using namespace llvm::objcopy::macho; |
30 | using namespace llvm::object; |
31 | |
32 | using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>; |
33 | using LoadCommandPred = std::function<bool(const LoadCommand &LC)>; |
34 | |
35 | #ifndef NDEBUG |
36 | static bool isLoadCommandWithPayloadString(const LoadCommand &LC) { |
37 | // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and |
38 | // LC_LAZY_LOAD_DYLIB |
39 | return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH || |
40 | LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB || |
41 | LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB || |
42 | LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB; |
43 | } |
44 | #endif |
45 | |
46 | static StringRef getPayloadString(const LoadCommand &LC) { |
47 | assert(isLoadCommandWithPayloadString(LC) && |
48 | "unsupported load command encountered" ); |
49 | |
50 | return StringRef(reinterpret_cast<const char *>(LC.Payload.data()), |
51 | LC.Payload.size()) |
52 | .rtrim(Char: '\0'); |
53 | } |
54 | |
55 | static Error removeSections(const CommonConfig &Config, Object &Obj) { |
56 | SectionPred RemovePred = [](const std::unique_ptr<Section> &) { |
57 | return false; |
58 | }; |
59 | |
60 | if (!Config.ToRemove.empty()) { |
61 | RemovePred = [&Config, RemovePred](const std::unique_ptr<Section> &Sec) { |
62 | return Config.ToRemove.matches(S: Sec->CanonicalName); |
63 | }; |
64 | } |
65 | |
66 | if (Config.StripAll || Config.StripDebug) { |
67 | // Remove all debug sections. |
68 | RemovePred = [RemovePred](const std::unique_ptr<Section> &Sec) { |
69 | if (Sec->Segname == "__DWARF" ) |
70 | return true; |
71 | |
72 | return RemovePred(Sec); |
73 | }; |
74 | } |
75 | |
76 | if (!Config.OnlySection.empty()) { |
77 | // Overwrite RemovePred because --only-section takes priority. |
78 | RemovePred = [&Config](const std::unique_ptr<Section> &Sec) { |
79 | return !Config.OnlySection.matches(S: Sec->CanonicalName); |
80 | }; |
81 | } |
82 | |
83 | return Obj.removeSections(ToRemove: RemovePred); |
84 | } |
85 | |
86 | static void markSymbols(const CommonConfig &, Object &Obj) { |
87 | // Symbols referenced from the indirect symbol table must not be removed. |
88 | for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols) |
89 | if (ISE.Symbol) |
90 | (*ISE.Symbol)->Referenced = true; |
91 | } |
92 | |
93 | static void updateAndRemoveSymbols(const CommonConfig &Config, |
94 | const MachOConfig &MachOConfig, |
95 | Object &Obj) { |
96 | Obj.SymTable.updateSymbols(Callable: [&](SymbolEntry &Sym) { |
97 | if (Config.SymbolsToSkip.matches(S: Sym.Name)) |
98 | return; |
99 | |
100 | if (!Sym.isUndefinedSymbol() && Config.SymbolsToLocalize.matches(S: Sym.Name)) |
101 | Sym.n_type &= ~MachO::N_EXT; |
102 | |
103 | // Note: these two globalize flags have very similar names but different |
104 | // meanings: |
105 | // |
106 | // --globalize-symbol: promote a symbol to global |
107 | // --keep-global-symbol: all symbols except for these should be made local |
108 | // |
109 | // If --globalize-symbol is specified for a given symbol, it will be |
110 | // global in the output file even if it is not included via |
111 | // --keep-global-symbol. Because of that, make sure to check |
112 | // --globalize-symbol second. |
113 | if (!Sym.isUndefinedSymbol() && !Config.SymbolsToKeepGlobal.empty() && |
114 | !Config.SymbolsToKeepGlobal.matches(S: Sym.Name)) |
115 | Sym.n_type &= ~MachO::N_EXT; |
116 | |
117 | if (!Sym.isUndefinedSymbol() && Config.SymbolsToGlobalize.matches(S: Sym.Name)) |
118 | Sym.n_type |= MachO::N_EXT; |
119 | |
120 | if (Sym.isExternalSymbol() && !Sym.isUndefinedSymbol() && |
121 | (Config.Weaken || Config.SymbolsToWeaken.matches(S: Sym.Name))) |
122 | Sym.n_desc |= MachO::N_WEAK_DEF; |
123 | |
124 | auto I = Config.SymbolsToRename.find(Key: Sym.Name); |
125 | if (I != Config.SymbolsToRename.end()) |
126 | Sym.Name = std::string(I->getValue()); |
127 | }); |
128 | |
129 | auto RemovePred = [&Config, &MachOConfig, |
130 | &Obj](const std::unique_ptr<SymbolEntry> &N) { |
131 | if (N->Referenced) |
132 | return false; |
133 | if (MachOConfig.KeepUndefined && N->isUndefinedSymbol()) |
134 | return false; |
135 | if (N->n_desc & MachO::REFERENCED_DYNAMICALLY) |
136 | return false; |
137 | if (Config.StripAll) |
138 | return true; |
139 | if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT)) |
140 | return true; |
141 | // This behavior is consistent with cctools' strip. |
142 | if (Config.StripDebug && (N->n_type & MachO::N_STAB)) |
143 | return true; |
144 | // This behavior is consistent with cctools' strip. |
145 | if (MachOConfig.StripSwiftSymbols && |
146 | (Obj.Header.Flags & MachO::MH_DYLDLINK) && Obj.SwiftVersion && |
147 | *Obj.SwiftVersion && N->isSwiftSymbol()) |
148 | return true; |
149 | return false; |
150 | }; |
151 | |
152 | Obj.SymTable.removeSymbols(ToRemove: RemovePred); |
153 | } |
154 | |
155 | template <typename LCType> |
156 | static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) { |
157 | assert(isLoadCommandWithPayloadString(LC) && |
158 | "unsupported load command encountered" ); |
159 | |
160 | uint32_t NewCmdsize = alignTo(Value: sizeof(LCType) + S.size() + 1, Align: 8); |
161 | |
162 | LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize; |
163 | LC.Payload.assign(n: NewCmdsize - sizeof(LCType), val: 0); |
164 | llvm::copy(Range&: S, Out: LC.Payload.begin()); |
165 | } |
166 | |
167 | static LoadCommand buildRPathLoadCommand(StringRef Path) { |
168 | LoadCommand LC; |
169 | MachO::rpath_command RPathLC; |
170 | RPathLC.cmd = MachO::LC_RPATH; |
171 | RPathLC.path = sizeof(MachO::rpath_command); |
172 | RPathLC.cmdsize = alignTo(Value: sizeof(MachO::rpath_command) + Path.size() + 1, Align: 8); |
173 | LC.MachOLoadCommand.rpath_command_data = RPathLC; |
174 | LC.Payload.assign(n: RPathLC.cmdsize - sizeof(MachO::rpath_command), val: 0); |
175 | llvm::copy(Range&: Path, Out: LC.Payload.begin()); |
176 | return LC; |
177 | } |
178 | |
179 | static Error processLoadCommands(const MachOConfig &MachOConfig, Object &Obj) { |
180 | // Remove RPaths. |
181 | DenseSet<StringRef> RPathsToRemove(MachOConfig.RPathsToRemove.begin(), |
182 | MachOConfig.RPathsToRemove.end()); |
183 | |
184 | LoadCommandPred RemovePred = [&RPathsToRemove, |
185 | &MachOConfig](const LoadCommand &LC) { |
186 | if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) { |
187 | // When removing all RPaths we don't need to care |
188 | // about what it contains |
189 | if (MachOConfig.RemoveAllRpaths) |
190 | return true; |
191 | |
192 | StringRef RPath = getPayloadString(LC); |
193 | if (RPathsToRemove.count(V: RPath)) { |
194 | RPathsToRemove.erase(V: RPath); |
195 | return true; |
196 | } |
197 | } |
198 | return false; |
199 | }; |
200 | |
201 | if (Error E = Obj.removeLoadCommands(ToRemove: RemovePred)) |
202 | return E; |
203 | |
204 | // Emit an error if the Mach-O binary does not contain an rpath path name |
205 | // specified in -delete_rpath. |
206 | for (StringRef RPath : MachOConfig.RPathsToRemove) { |
207 | if (RPathsToRemove.count(V: RPath)) |
208 | return createStringError(EC: errc::invalid_argument, |
209 | Fmt: "no LC_RPATH load command with path: %s" , |
210 | Vals: RPath.str().c_str()); |
211 | } |
212 | |
213 | DenseSet<StringRef> RPaths; |
214 | |
215 | // Get all existing RPaths. |
216 | for (LoadCommand &LC : Obj.LoadCommands) { |
217 | if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) |
218 | RPaths.insert(V: getPayloadString(LC)); |
219 | } |
220 | |
221 | // Throw errors for invalid RPaths. |
222 | for (const auto &OldNew : MachOConfig.RPathsToUpdate) { |
223 | StringRef Old = OldNew.getFirst(); |
224 | StringRef New = OldNew.getSecond(); |
225 | if (!RPaths.contains(V: Old)) |
226 | return createStringError(EC: errc::invalid_argument, |
227 | S: "no LC_RPATH load command with path: " + Old); |
228 | if (RPaths.contains(V: New)) |
229 | return createStringError(EC: errc::invalid_argument, |
230 | S: "rpath '" + New + |
231 | "' would create a duplicate load command" ); |
232 | } |
233 | |
234 | // Update load commands. |
235 | for (LoadCommand &LC : Obj.LoadCommands) { |
236 | switch (LC.MachOLoadCommand.load_command_data.cmd) { |
237 | case MachO::LC_ID_DYLIB: |
238 | if (MachOConfig.SharedLibId) |
239 | updateLoadCommandPayloadString<MachO::dylib_command>( |
240 | LC, S: *MachOConfig.SharedLibId); |
241 | break; |
242 | |
243 | case MachO::LC_RPATH: { |
244 | StringRef RPath = getPayloadString(LC); |
245 | StringRef NewRPath = MachOConfig.RPathsToUpdate.lookup(Val: RPath); |
246 | if (!NewRPath.empty()) |
247 | updateLoadCommandPayloadString<MachO::rpath_command>(LC, S: NewRPath); |
248 | break; |
249 | } |
250 | |
251 | // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB |
252 | // here once llvm-objcopy supports them. |
253 | case MachO::LC_LOAD_DYLIB: |
254 | case MachO::LC_LOAD_WEAK_DYLIB: |
255 | StringRef InstallName = getPayloadString(LC); |
256 | StringRef NewInstallName = |
257 | MachOConfig.InstallNamesToUpdate.lookup(Val: InstallName); |
258 | if (!NewInstallName.empty()) |
259 | updateLoadCommandPayloadString<MachO::dylib_command>(LC, |
260 | S: NewInstallName); |
261 | break; |
262 | } |
263 | } |
264 | |
265 | // Add new RPaths. |
266 | for (StringRef RPath : MachOConfig.RPathToAdd) { |
267 | if (RPaths.contains(V: RPath)) |
268 | return createStringError(EC: errc::invalid_argument, |
269 | S: "rpath '" + RPath + |
270 | "' would create a duplicate load command" ); |
271 | RPaths.insert(V: RPath); |
272 | Obj.LoadCommands.push_back(x: buildRPathLoadCommand(Path: RPath)); |
273 | } |
274 | |
275 | for (StringRef RPath : MachOConfig.RPathToPrepend) { |
276 | if (RPaths.contains(V: RPath)) |
277 | return createStringError(EC: errc::invalid_argument, |
278 | S: "rpath '" + RPath + |
279 | "' would create a duplicate load command" ); |
280 | |
281 | RPaths.insert(V: RPath); |
282 | Obj.LoadCommands.insert(position: Obj.LoadCommands.begin(), |
283 | x: buildRPathLoadCommand(Path: RPath)); |
284 | } |
285 | |
286 | // Unlike appending rpaths, the indexes of subsequent load commands must |
287 | // be recalculated after prepending one. |
288 | if (!MachOConfig.RPathToPrepend.empty()) |
289 | Obj.updateLoadCommandIndexes(); |
290 | |
291 | // Remove any empty segments if required. |
292 | if (!MachOConfig.EmptySegmentsToRemove.empty()) { |
293 | auto RemovePred = [&MachOConfig](const LoadCommand &LC) { |
294 | if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_SEGMENT_64 || |
295 | LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_SEGMENT) { |
296 | return LC.Sections.empty() && |
297 | MachOConfig.EmptySegmentsToRemove.contains(V: *LC.getSegmentName()); |
298 | } |
299 | return false; |
300 | }; |
301 | if (Error E = Obj.removeLoadCommands(ToRemove: RemovePred)) |
302 | return E; |
303 | } |
304 | |
305 | return Error::success(); |
306 | } |
307 | |
308 | static Error dumpSectionToFile(StringRef SecName, StringRef Filename, |
309 | StringRef InputFilename, Object &Obj) { |
310 | for (LoadCommand &LC : Obj.LoadCommands) |
311 | for (const std::unique_ptr<Section> &Sec : LC.Sections) { |
312 | if (Sec->CanonicalName == SecName) { |
313 | Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = |
314 | FileOutputBuffer::create(FilePath: Filename, Size: Sec->Content.size()); |
315 | if (!BufferOrErr) |
316 | return createFileError(F: Filename, E: BufferOrErr.takeError()); |
317 | std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); |
318 | llvm::copy(Range&: Sec->Content, Out: Buf->getBufferStart()); |
319 | |
320 | if (Error E = Buf->commit()) |
321 | return createFileError(F: Filename, E: std::move(E)); |
322 | return Error::success(); |
323 | } |
324 | } |
325 | |
326 | return createFileError(F: InputFilename, EC: object_error::parse_failed, |
327 | Fmt: "section '%s' not found" , Vals: SecName.str().c_str()); |
328 | } |
329 | |
330 | static Error addSection(const NewSectionInfo &NewSection, Object &Obj) { |
331 | std::pair<StringRef, StringRef> Pair = NewSection.SectionName.split(Separator: ','); |
332 | StringRef TargetSegName = Pair.first; |
333 | Section Sec(TargetSegName, Pair.second); |
334 | Sec.Content = |
335 | Obj.NewSectionsContents.save(S: NewSection.SectionData->getBuffer()); |
336 | Sec.Size = Sec.Content.size(); |
337 | |
338 | // Add the a section into an existing segment. |
339 | for (LoadCommand &LC : Obj.LoadCommands) { |
340 | std::optional<StringRef> SegName = LC.getSegmentName(); |
341 | if (SegName && SegName == TargetSegName) { |
342 | uint64_t Addr = *LC.getSegmentVMAddr(); |
343 | for (const std::unique_ptr<Section> &S : LC.Sections) |
344 | Addr = std::max(a: Addr, b: S->Addr + S->Size); |
345 | LC.Sections.push_back(x: std::make_unique<Section>(args&: Sec)); |
346 | LC.Sections.back()->Addr = Addr; |
347 | return Error::success(); |
348 | } |
349 | } |
350 | |
351 | // There's no segment named TargetSegName. Create a new load command and |
352 | // Insert a new section into it. |
353 | LoadCommand &NewSegment = |
354 | Obj.addSegment(SegName: TargetSegName, SegVMSize: alignTo(Value: Sec.Size, Align: 16384)); |
355 | NewSegment.Sections.push_back(x: std::make_unique<Section>(args&: Sec)); |
356 | NewSegment.Sections.back()->Addr = *NewSegment.getSegmentVMAddr(); |
357 | return Error::success(); |
358 | } |
359 | |
360 | static Expected<Section &> findSection(StringRef SecName, Object &O) { |
361 | StringRef SegName; |
362 | std::tie(args&: SegName, args&: SecName) = SecName.split(Separator: "," ); |
363 | // For compactness, intermediate object files (MH_OBJECT) contain |
364 | // only one segment in which all sections are placed. |
365 | // The static linker places each section in the named segment when building |
366 | // the final product (any file that is not of type MH_OBJECT). |
367 | // |
368 | // Source: |
369 | // https://math-atlas.sourceforge.net/devel/assembly/MachORuntime.pdf |
370 | // page 57 |
371 | if (O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) { |
372 | for (const auto& LC : O.LoadCommands) |
373 | for (const auto& Sec : LC.Sections) |
374 | if (Sec->Segname == SegName && Sec->Sectname == SecName) |
375 | return *Sec; |
376 | |
377 | StringRef ErrMsg = "could not find section with name '%s' in '%s' segment" ; |
378 | return createStringError(EC: errc::invalid_argument, Fmt: ErrMsg.str().c_str(), |
379 | Vals: SecName.str().c_str(), Vals: SegName.str().c_str()); |
380 | } |
381 | auto FoundSeg = |
382 | llvm::find_if(Range&: O.LoadCommands, P: [SegName](const LoadCommand &LC) { |
383 | return LC.getSegmentName() == SegName; |
384 | }); |
385 | if (FoundSeg == O.LoadCommands.end()) |
386 | return createStringError(EC: errc::invalid_argument, |
387 | Fmt: "could not find segment with name '%s'" , |
388 | Vals: SegName.str().c_str()); |
389 | auto FoundSec = llvm::find_if(Range&: FoundSeg->Sections, |
390 | P: [SecName](const std::unique_ptr<Section> &Sec) { |
391 | return Sec->Sectname == SecName; |
392 | }); |
393 | if (FoundSec == FoundSeg->Sections.end()) |
394 | return createStringError(EC: errc::invalid_argument, |
395 | Fmt: "could not find section with name '%s'" , |
396 | Vals: SecName.str().c_str()); |
397 | |
398 | assert(FoundSec->get()->CanonicalName == (SegName + "," + SecName).str()); |
399 | return **FoundSec; |
400 | } |
401 | |
402 | static Error updateSection(const NewSectionInfo &NewSection, Object &O) { |
403 | Expected<Section &> SecToUpdateOrErr = findSection(SecName: NewSection.SectionName, O); |
404 | |
405 | if (!SecToUpdateOrErr) |
406 | return SecToUpdateOrErr.takeError(); |
407 | Section &Sec = *SecToUpdateOrErr; |
408 | |
409 | if (NewSection.SectionData->getBufferSize() > Sec.Size) |
410 | return createStringError( |
411 | EC: errc::invalid_argument, |
412 | S: "new section cannot be larger than previous section" ); |
413 | Sec.Content = O.NewSectionsContents.save(S: NewSection.SectionData->getBuffer()); |
414 | Sec.Size = Sec.Content.size(); |
415 | return Error::success(); |
416 | } |
417 | |
418 | // isValidMachOCannonicalName returns success if Name is a MachO cannonical name |
419 | // ("<segment>,<section>") and lengths of both segment and section names are |
420 | // valid. |
421 | static Error isValidMachOCannonicalName(StringRef Name) { |
422 | if (Name.count(C: ',') != 1) |
423 | return createStringError(EC: errc::invalid_argument, |
424 | Fmt: "invalid section name '%s' (should be formatted " |
425 | "as '<segment name>,<section name>')" , |
426 | Vals: Name.str().c_str()); |
427 | |
428 | std::pair<StringRef, StringRef> Pair = Name.split(Separator: ','); |
429 | if (Pair.first.size() > 16) |
430 | return createStringError(EC: errc::invalid_argument, |
431 | Fmt: "too long segment name: '%s'" , |
432 | Vals: Pair.first.str().c_str()); |
433 | if (Pair.second.size() > 16) |
434 | return createStringError(EC: errc::invalid_argument, |
435 | Fmt: "too long section name: '%s'" , |
436 | Vals: Pair.second.str().c_str()); |
437 | return Error::success(); |
438 | } |
439 | |
440 | static Error handleArgs(const CommonConfig &Config, |
441 | const MachOConfig &MachOConfig, Object &Obj) { |
442 | // Dump sections before add/remove for compatibility with GNU objcopy. |
443 | for (StringRef Flag : Config.DumpSection) { |
444 | StringRef SectionName; |
445 | StringRef FileName; |
446 | std::tie(args&: SectionName, args&: FileName) = Flag.split(Separator: '='); |
447 | if (Error E = |
448 | dumpSectionToFile(SecName: SectionName, Filename: FileName, InputFilename: Config.InputFilename, Obj)) |
449 | return E; |
450 | } |
451 | |
452 | if (Error E = removeSections(Config, Obj)) |
453 | return createFileError(F: Config.InputFilename, E: std::move(E)); |
454 | |
455 | // Mark symbols to determine which symbols are still needed. |
456 | if (Config.StripAll) |
457 | markSymbols(Config, Obj); |
458 | |
459 | updateAndRemoveSymbols(Config, MachOConfig, Obj); |
460 | |
461 | if (Config.StripAll) |
462 | for (LoadCommand &LC : Obj.LoadCommands) |
463 | for (std::unique_ptr<Section> &Sec : LC.Sections) |
464 | Sec->Relocations.clear(); |
465 | |
466 | for (const NewSectionInfo &NewSection : Config.AddSection) { |
467 | if (Error E = isValidMachOCannonicalName(Name: NewSection.SectionName)) |
468 | return createFileError(F: Config.InputFilename, E: std::move(E)); |
469 | if (Error E = addSection(NewSection, Obj)) |
470 | return createFileError(F: Config.InputFilename, E: std::move(E)); |
471 | } |
472 | |
473 | for (const NewSectionInfo &NewSection : Config.UpdateSection) { |
474 | if (Error E = isValidMachOCannonicalName(Name: NewSection.SectionName)) |
475 | return createFileError(F: Config.InputFilename, E: std::move(E)); |
476 | if (Error E = updateSection(NewSection, O&: Obj)) |
477 | return createFileError(F: Config.InputFilename, E: std::move(E)); |
478 | } |
479 | |
480 | if (Error E = processLoadCommands(MachOConfig, Obj)) |
481 | return createFileError(F: Config.InputFilename, E: std::move(E)); |
482 | |
483 | return Error::success(); |
484 | } |
485 | |
486 | Error objcopy::macho::executeObjcopyOnBinary(const CommonConfig &Config, |
487 | const MachOConfig &MachOConfig, |
488 | object::MachOObjectFile &In, |
489 | raw_ostream &Out) { |
490 | MachOReader Reader(In); |
491 | Expected<std::unique_ptr<Object>> O = Reader.create(); |
492 | if (!O) |
493 | return createFileError(F: Config.InputFilename, E: O.takeError()); |
494 | |
495 | if (O->get()->Header.FileType == MachO::HeaderFileType::MH_PRELOAD) |
496 | return createStringError(EC: std::errc::not_supported, |
497 | Fmt: "%s: MH_PRELOAD files are not supported" , |
498 | Vals: Config.InputFilename.str().c_str()); |
499 | |
500 | if (Error E = handleArgs(Config, MachOConfig, Obj&: **O)) |
501 | return E; |
502 | |
503 | // Page size used for alignment of segment sizes in Mach-O executables and |
504 | // dynamic libraries. |
505 | uint64_t PageSize; |
506 | switch (In.getArch()) { |
507 | case Triple::ArchType::arm: |
508 | case Triple::ArchType::aarch64: |
509 | case Triple::ArchType::aarch64_32: |
510 | PageSize = 16384; |
511 | break; |
512 | default: |
513 | PageSize = 4096; |
514 | } |
515 | |
516 | MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(), |
517 | sys::path::filename(path: Config.OutputFilename), PageSize, Out); |
518 | if (auto E = Writer.finalize()) |
519 | return E; |
520 | return Writer.write(); |
521 | } |
522 | |
523 | Error objcopy::macho::executeObjcopyOnMachOUniversalBinary( |
524 | const MultiFormatConfig &Config, const MachOUniversalBinary &In, |
525 | raw_ostream &Out) { |
526 | SmallVector<OwningBinary<Binary>, 2> Binaries; |
527 | SmallVector<Slice, 2> Slices; |
528 | for (const auto &O : In.objects()) { |
529 | Expected<std::unique_ptr<Archive>> ArOrErr = O.getAsArchive(); |
530 | if (ArOrErr) { |
531 | Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr = |
532 | createNewArchiveMembers(Config, Ar: **ArOrErr); |
533 | if (!NewArchiveMembersOrErr) |
534 | return NewArchiveMembersOrErr.takeError(); |
535 | auto Kind = (*ArOrErr)->kind(); |
536 | if (Kind == object::Archive::K_BSD) |
537 | Kind = object::Archive::K_DARWIN; |
538 | Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr = |
539 | writeArchiveToBuffer( |
540 | NewMembers: *NewArchiveMembersOrErr, |
541 | WriteSymtab: (*ArOrErr)->hasSymbolTable() ? SymtabWritingMode::NormalSymtab |
542 | : SymtabWritingMode::NoSymtab, |
543 | Kind, Deterministic: Config.getCommonConfig().DeterministicArchives, |
544 | Thin: (*ArOrErr)->isThin()); |
545 | if (!OutputBufferOrErr) |
546 | return OutputBufferOrErr.takeError(); |
547 | Expected<std::unique_ptr<Binary>> BinaryOrErr = |
548 | object::createBinary(Source: **OutputBufferOrErr); |
549 | if (!BinaryOrErr) |
550 | return BinaryOrErr.takeError(); |
551 | Binaries.emplace_back(Args: std::move(*BinaryOrErr), |
552 | Args: std::move(*OutputBufferOrErr)); |
553 | Slices.emplace_back(Args&: *cast<Archive>(Val: Binaries.back().getBinary()), |
554 | Args: O.getCPUType(), Args: O.getCPUSubType(), |
555 | Args: O.getArchFlagName(), Args: O.getAlign()); |
556 | continue; |
557 | } |
558 | // The methods getAsArchive, getAsObjectFile, getAsIRObject of the class |
559 | // ObjectForArch return an Error in case of the type mismatch. We need to |
560 | // check each in turn to see what kind of slice this is, so ignore errors |
561 | // produced along the way. |
562 | consumeError(Err: ArOrErr.takeError()); |
563 | |
564 | Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile(); |
565 | if (!ObjOrErr) { |
566 | consumeError(Err: ObjOrErr.takeError()); |
567 | return createStringError( |
568 | EC: std::errc::invalid_argument, |
569 | Fmt: "slice for '%s' of the universal Mach-O binary " |
570 | "'%s' is not a Mach-O object or an archive" , |
571 | Vals: O.getArchFlagName().c_str(), |
572 | Vals: Config.getCommonConfig().InputFilename.str().c_str()); |
573 | } |
574 | std::string ArchFlagName = O.getArchFlagName(); |
575 | |
576 | SmallVector<char, 0> Buffer; |
577 | raw_svector_ostream MemStream(Buffer); |
578 | |
579 | Expected<const MachOConfig &> MachO = Config.getMachOConfig(); |
580 | if (!MachO) |
581 | return MachO.takeError(); |
582 | |
583 | if (Error E = executeObjcopyOnBinary(Config: Config.getCommonConfig(), MachOConfig: *MachO, |
584 | In&: **ObjOrErr, Out&: MemStream)) |
585 | return E; |
586 | |
587 | auto MB = std::make_unique<SmallVectorMemoryBuffer>( |
588 | args: std::move(Buffer), args&: ArchFlagName, /*RequiresNullTerminator=*/args: false); |
589 | Expected<std::unique_ptr<Binary>> BinaryOrErr = object::createBinary(Source: *MB); |
590 | if (!BinaryOrErr) |
591 | return BinaryOrErr.takeError(); |
592 | Binaries.emplace_back(Args: std::move(*BinaryOrErr), Args: std::move(MB)); |
593 | Slices.emplace_back(Args&: *cast<MachOObjectFile>(Val: Binaries.back().getBinary()), |
594 | Args: O.getAlign()); |
595 | } |
596 | |
597 | if (Error Err = writeUniversalBinaryToStream(Slices, Out)) |
598 | return Err; |
599 | |
600 | return Error::success(); |
601 | } |
602 | |