602 lines · cpp
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-exception6//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 27using namespace llvm;28using namespace llvm::objcopy;29using namespace llvm::objcopy::macho;30using namespace llvm::object;31 32using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>;33using LoadCommandPred = std::function<bool(const LoadCommand &LC)>;34 35#ifndef NDEBUG36static bool isLoadCommandWithPayloadString(const LoadCommand &LC) {37 // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and38 // LC_LAZY_LOAD_DYLIB39 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#endif45 46static 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('\0');53}54 55static 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(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(Sec->CanonicalName);80 };81 }82 83 return Obj.removeSections(RemovePred);84}85 86static 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 93static void updateAndRemoveSymbols(const CommonConfig &Config,94 const MachOConfig &MachOConfig,95 Object &Obj) {96 Obj.SymTable.updateSymbols([&](SymbolEntry &Sym) {97 if (Config.SymbolsToSkip.matches(Sym.Name))98 return;99 100 if (!Sym.isUndefinedSymbol() && Config.SymbolsToLocalize.matches(Sym.Name))101 Sym.n_type &= ~MachO::N_EXT;102 103 // Note: these two globalize flags have very similar names but different104 // meanings:105 //106 // --globalize-symbol: promote a symbol to global107 // --keep-global-symbol: all symbols except for these should be made local108 //109 // If --globalize-symbol is specified for a given symbol, it will be110 // global in the output file even if it is not included via111 // --keep-global-symbol. Because of that, make sure to check112 // --globalize-symbol second.113 if (!Sym.isUndefinedSymbol() && !Config.SymbolsToKeepGlobal.empty() &&114 !Config.SymbolsToKeepGlobal.matches(Sym.Name))115 Sym.n_type &= ~MachO::N_EXT;116 117 if (!Sym.isUndefinedSymbol() && Config.SymbolsToGlobalize.matches(Sym.Name))118 Sym.n_type |= MachO::N_EXT;119 120 if (Sym.isExternalSymbol() && !Sym.isUndefinedSymbol() &&121 (Config.Weaken || Config.SymbolsToWeaken.matches(Sym.Name)))122 Sym.n_desc |= MachO::N_WEAK_DEF;123 124 auto I = Config.SymbolsToRename.find(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(RemovePred);153}154 155template <typename LCType>156static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) {157 assert(isLoadCommandWithPayloadString(LC) &&158 "unsupported load command encountered");159 160 uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8);161 162 LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize;163 LC.Payload.assign(NewCmdsize - sizeof(LCType), 0);164 llvm::copy(S, LC.Payload.begin());165}166 167static 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(sizeof(MachO::rpath_command) + Path.size() + 1, 8);173 LC.MachOLoadCommand.rpath_command_data = RPathLC;174 LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0);175 llvm::copy(Path, LC.Payload.begin());176 return LC;177}178 179static 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 care188 // about what it contains189 if (MachOConfig.RemoveAllRpaths)190 return true;191 192 StringRef RPath = getPayloadString(LC);193 if (RPathsToRemove.count(RPath)) {194 RPathsToRemove.erase(RPath);195 return true;196 }197 }198 return false;199 };200 201 if (Error E = Obj.removeLoadCommands(RemovePred))202 return E;203 204 // Emit an error if the Mach-O binary does not contain an rpath path name205 // specified in -delete_rpath.206 for (StringRef RPath : MachOConfig.RPathsToRemove) {207 if (RPathsToRemove.count(RPath))208 return createStringError(errc::invalid_argument,209 "no LC_RPATH load command with path: %s",210 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(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(Old))226 return createStringError(errc::invalid_argument,227 "no LC_RPATH load command with path: " + Old);228 if (RPaths.contains(New))229 return createStringError(errc::invalid_argument,230 "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, *MachOConfig.SharedLibId);241 break;242 243 case MachO::LC_RPATH: {244 StringRef RPath = getPayloadString(LC);245 StringRef NewRPath = MachOConfig.RPathsToUpdate.lookup(RPath);246 if (!NewRPath.empty())247 updateLoadCommandPayloadString<MachO::rpath_command>(LC, NewRPath);248 break;249 }250 251 // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB252 // 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(InstallName);258 if (!NewInstallName.empty())259 updateLoadCommandPayloadString<MachO::dylib_command>(LC,260 NewInstallName);261 break;262 }263 }264 265 // Add new RPaths.266 for (StringRef RPath : MachOConfig.RPathToAdd) {267 if (RPaths.contains(RPath))268 return createStringError(errc::invalid_argument,269 "rpath '" + RPath +270 "' would create a duplicate load command");271 RPaths.insert(RPath);272 Obj.LoadCommands.push_back(buildRPathLoadCommand(RPath));273 }274 275 for (StringRef RPath : MachOConfig.RPathToPrepend) {276 if (RPaths.contains(RPath))277 return createStringError(errc::invalid_argument,278 "rpath '" + RPath +279 "' would create a duplicate load command");280 281 RPaths.insert(RPath);282 Obj.LoadCommands.insert(Obj.LoadCommands.begin(),283 buildRPathLoadCommand(RPath));284 }285 286 // Unlike appending rpaths, the indexes of subsequent load commands must287 // 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(*LC.getSegmentName());298 }299 return false;300 };301 if (Error E = Obj.removeLoadCommands(RemovePred))302 return E;303 }304 305 return Error::success();306}307 308static 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(Filename, Sec->Content.size());315 if (!BufferOrErr)316 return createFileError(Filename, BufferOrErr.takeError());317 std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);318 llvm::copy(Sec->Content, Buf->getBufferStart());319 320 if (Error E = Buf->commit())321 return createFileError(Filename, std::move(E));322 return Error::success();323 }324 }325 326 return createFileError(InputFilename, object_error::parse_failed,327 "section '%s' not found", SecName.str().c_str());328}329 330static Error addSection(const NewSectionInfo &NewSection, Object &Obj) {331 std::pair<StringRef, StringRef> Pair = NewSection.SectionName.split(',');332 StringRef TargetSegName = Pair.first;333 Section Sec(TargetSegName, Pair.second);334 Sec.Content =335 Obj.NewSectionsContents.save(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(Addr, S->Addr + S->Size);345 LC.Sections.push_back(std::make_unique<Section>(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 and352 // Insert a new section into it.353 LoadCommand &NewSegment =354 Obj.addSegment(TargetSegName, alignTo(Sec.Size, 16384));355 NewSegment.Sections.push_back(std::make_unique<Section>(Sec));356 NewSegment.Sections.back()->Addr = *NewSegment.getSegmentVMAddr();357 return Error::success();358}359 360static Expected<Section &> findSection(StringRef SecName, Object &O) {361 StringRef SegName;362 std::tie(SegName, SecName) = SecName.split(",");363 // For compactness, intermediate object files (MH_OBJECT) contain364 // only one segment in which all sections are placed.365 // The static linker places each section in the named segment when building366 // the final product (any file that is not of type MH_OBJECT).367 //368 // Source:369 // https://math-atlas.sourceforge.net/devel/assembly/MachORuntime.pdf370 // page 57371 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(errc::invalid_argument, ErrMsg.str().c_str(),379 SecName.str().c_str(), SegName.str().c_str());380 }381 auto FoundSeg =382 llvm::find_if(O.LoadCommands, [SegName](const LoadCommand &LC) {383 return LC.getSegmentName() == SegName;384 });385 if (FoundSeg == O.LoadCommands.end())386 return createStringError(errc::invalid_argument,387 "could not find segment with name '%s'",388 SegName.str().c_str());389 auto FoundSec = llvm::find_if(FoundSeg->Sections,390 [SecName](const std::unique_ptr<Section> &Sec) {391 return Sec->Sectname == SecName;392 });393 if (FoundSec == FoundSeg->Sections.end())394 return createStringError(errc::invalid_argument,395 "could not find section with name '%s'",396 SecName.str().c_str());397 398 assert(FoundSec->get()->CanonicalName == (SegName + "," + SecName).str());399 return **FoundSec;400}401 402static Error updateSection(const NewSectionInfo &NewSection, Object &O) {403 Expected<Section &> SecToUpdateOrErr = findSection(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 errc::invalid_argument,412 "new section cannot be larger than previous section");413 Sec.Content = O.NewSectionsContents.save(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 name419// ("<segment>,<section>") and lengths of both segment and section names are420// valid.421static Error isValidMachOCannonicalName(StringRef Name) {422 if (Name.count(',') != 1)423 return createStringError(errc::invalid_argument,424 "invalid section name '%s' (should be formatted "425 "as '<segment name>,<section name>')",426 Name.str().c_str());427 428 std::pair<StringRef, StringRef> Pair = Name.split(',');429 if (Pair.first.size() > 16)430 return createStringError(errc::invalid_argument,431 "too long segment name: '%s'",432 Pair.first.str().c_str());433 if (Pair.second.size() > 16)434 return createStringError(errc::invalid_argument,435 "too long section name: '%s'",436 Pair.second.str().c_str());437 return Error::success();438}439 440static 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(SectionName, FileName) = Flag.split('=');447 if (Error E =448 dumpSectionToFile(SectionName, FileName, Config.InputFilename, Obj))449 return E;450 }451 452 if (Error E = removeSections(Config, Obj))453 return createFileError(Config.InputFilename, 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(NewSection.SectionName))468 return createFileError(Config.InputFilename, std::move(E));469 if (Error E = addSection(NewSection, Obj))470 return createFileError(Config.InputFilename, std::move(E));471 }472 473 for (const NewSectionInfo &NewSection : Config.UpdateSection) {474 if (Error E = isValidMachOCannonicalName(NewSection.SectionName))475 return createFileError(Config.InputFilename, std::move(E));476 if (Error E = updateSection(NewSection, Obj))477 return createFileError(Config.InputFilename, std::move(E));478 }479 480 if (Error E = processLoadCommands(MachOConfig, Obj))481 return createFileError(Config.InputFilename, std::move(E));482 483 return Error::success();484}485 486Error 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(Config.InputFilename, O.takeError());494 495 if (O->get()->Header.FileType == MachO::HeaderFileType::MH_PRELOAD)496 return createStringError(std::errc::not_supported,497 "%s: MH_PRELOAD files are not supported",498 Config.InputFilename.str().c_str());499 500 if (Error E = handleArgs(Config, MachOConfig, **O))501 return E;502 503 // Page size used for alignment of segment sizes in Mach-O executables and504 // 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(Config.OutputFilename), PageSize, Out);518 if (auto E = Writer.finalize())519 return E;520 return Writer.write();521}522 523Error 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, **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 *NewArchiveMembersOrErr,541 (*ArOrErr)->hasSymbolTable() ? SymtabWritingMode::NormalSymtab542 : SymtabWritingMode::NoSymtab,543 Kind, Config.getCommonConfig().DeterministicArchives,544 (*ArOrErr)->isThin());545 if (!OutputBufferOrErr)546 return OutputBufferOrErr.takeError();547 Expected<std::unique_ptr<Binary>> BinaryOrErr =548 object::createBinary(**OutputBufferOrErr);549 if (!BinaryOrErr)550 return BinaryOrErr.takeError();551 Binaries.emplace_back(std::move(*BinaryOrErr),552 std::move(*OutputBufferOrErr));553 Slices.emplace_back(*cast<Archive>(Binaries.back().getBinary()),554 O.getCPUType(), O.getCPUSubType(),555 O.getArchFlagName(), O.getAlign());556 continue;557 }558 // The methods getAsArchive, getAsObjectFile, getAsIRObject of the class559 // ObjectForArch return an Error in case of the type mismatch. We need to560 // check each in turn to see what kind of slice this is, so ignore errors561 // produced along the way.562 consumeError(ArOrErr.takeError());563 564 Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile();565 if (!ObjOrErr) {566 consumeError(ObjOrErr.takeError());567 return createStringError(568 std::errc::invalid_argument,569 "slice for '%s' of the universal Mach-O binary "570 "'%s' is not a Mach-O object or an archive",571 O.getArchFlagName().c_str(),572 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.getCommonConfig(), *MachO,584 **ObjOrErr, MemStream))585 return E;586 587 auto MB = std::make_unique<SmallVectorMemoryBuffer>(588 std::move(Buffer), ArchFlagName, /*RequiresNullTerminator=*/false);589 Expected<std::unique_ptr<Binary>> BinaryOrErr = object::createBinary(*MB);590 if (!BinaryOrErr)591 return BinaryOrErr.takeError();592 Binaries.emplace_back(std::move(*BinaryOrErr), std::move(MB));593 Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()),594 O.getAlign());595 }596 597 if (Error Err = writeUniversalBinaryToStream(Slices, Out))598 return Err;599 600 return Error::success();601}602