| 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 | |