Path: blob/main/contrib/llvm-project/llvm/lib/ObjCopy/MachO/MachOObjcopy.cpp
35294 views
//===- MachOObjcopy.cpp -----------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/ObjCopy/MachO/MachOObjcopy.h"9#include "Archive.h"10#include "MachOReader.h"11#include "MachOWriter.h"12#include "llvm/ADT/DenseSet.h"13#include "llvm/ObjCopy/CommonConfig.h"14#include "llvm/ObjCopy/MachO/MachOConfig.h"15#include "llvm/ObjCopy/MultiFormatConfig.h"16#include "llvm/ObjCopy/ObjCopy.h"17#include "llvm/Object/ArchiveWriter.h"18#include "llvm/Object/MachOUniversal.h"19#include "llvm/Object/MachOUniversalWriter.h"20#include "llvm/Support/Errc.h"21#include "llvm/Support/Error.h"22#include "llvm/Support/FileOutputBuffer.h"23#include "llvm/Support/Path.h"24#include "llvm/Support/SmallVectorMemoryBuffer.h"2526using namespace llvm;27using namespace llvm::objcopy;28using namespace llvm::objcopy::macho;29using namespace llvm::object;3031using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>;32using LoadCommandPred = std::function<bool(const LoadCommand &LC)>;3334#ifndef NDEBUG35static bool isLoadCommandWithPayloadString(const LoadCommand &LC) {36// TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and37// LC_LAZY_LOAD_DYLIB38return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH ||39LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB ||40LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB ||41LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB;42}43#endif4445static StringRef getPayloadString(const LoadCommand &LC) {46assert(isLoadCommandWithPayloadString(LC) &&47"unsupported load command encountered");4849return StringRef(reinterpret_cast<const char *>(LC.Payload.data()),50LC.Payload.size())51.rtrim('\0');52}5354static Error removeSections(const CommonConfig &Config, Object &Obj) {55SectionPred RemovePred = [](const std::unique_ptr<Section> &) {56return false;57};5859if (!Config.ToRemove.empty()) {60RemovePred = [&Config, RemovePred](const std::unique_ptr<Section> &Sec) {61return Config.ToRemove.matches(Sec->CanonicalName);62};63}6465if (Config.StripAll || Config.StripDebug) {66// Remove all debug sections.67RemovePred = [RemovePred](const std::unique_ptr<Section> &Sec) {68if (Sec->Segname == "__DWARF")69return true;7071return RemovePred(Sec);72};73}7475if (!Config.OnlySection.empty()) {76// Overwrite RemovePred because --only-section takes priority.77RemovePred = [&Config](const std::unique_ptr<Section> &Sec) {78return !Config.OnlySection.matches(Sec->CanonicalName);79};80}8182return Obj.removeSections(RemovePred);83}8485static void markSymbols(const CommonConfig &, Object &Obj) {86// Symbols referenced from the indirect symbol table must not be removed.87for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols)88if (ISE.Symbol)89(*ISE.Symbol)->Referenced = true;90}9192static void updateAndRemoveSymbols(const CommonConfig &Config,93const MachOConfig &MachOConfig,94Object &Obj) {95for (SymbolEntry &Sym : Obj.SymTable) {96// Weaken symbols first to match ELFObjcopy behavior.97bool IsExportedAndDefined =98(Sym.n_type & llvm::MachO::N_EXT) &&99(Sym.n_type & llvm::MachO::N_TYPE) != llvm::MachO::N_UNDF;100if (IsExportedAndDefined &&101(Config.Weaken || Config.SymbolsToWeaken.matches(Sym.Name)))102Sym.n_desc |= llvm::MachO::N_WEAK_DEF;103104auto I = Config.SymbolsToRename.find(Sym.Name);105if (I != Config.SymbolsToRename.end())106Sym.Name = std::string(I->getValue());107}108109auto RemovePred = [&Config, &MachOConfig,110&Obj](const std::unique_ptr<SymbolEntry> &N) {111if (N->Referenced)112return false;113if (MachOConfig.KeepUndefined && N->isUndefinedSymbol())114return false;115if (N->n_desc & MachO::REFERENCED_DYNAMICALLY)116return false;117if (Config.StripAll)118return true;119if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT))120return true;121// This behavior is consistent with cctools' strip.122if (Config.StripDebug && (N->n_type & MachO::N_STAB))123return true;124// This behavior is consistent with cctools' strip.125if (MachOConfig.StripSwiftSymbols &&126(Obj.Header.Flags & MachO::MH_DYLDLINK) && Obj.SwiftVersion &&127*Obj.SwiftVersion && N->isSwiftSymbol())128return true;129return false;130};131132Obj.SymTable.removeSymbols(RemovePred);133}134135template <typename LCType>136static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) {137assert(isLoadCommandWithPayloadString(LC) &&138"unsupported load command encountered");139140uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8);141142LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize;143LC.Payload.assign(NewCmdsize - sizeof(LCType), 0);144std::copy(S.begin(), S.end(), LC.Payload.begin());145}146147static LoadCommand buildRPathLoadCommand(StringRef Path) {148LoadCommand LC;149MachO::rpath_command RPathLC;150RPathLC.cmd = MachO::LC_RPATH;151RPathLC.path = sizeof(MachO::rpath_command);152RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size() + 1, 8);153LC.MachOLoadCommand.rpath_command_data = RPathLC;154LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0);155std::copy(Path.begin(), Path.end(), LC.Payload.begin());156return LC;157}158159static Error processLoadCommands(const MachOConfig &MachOConfig, Object &Obj) {160// Remove RPaths.161DenseSet<StringRef> RPathsToRemove(MachOConfig.RPathsToRemove.begin(),162MachOConfig.RPathsToRemove.end());163164LoadCommandPred RemovePred = [&RPathsToRemove,165&MachOConfig](const LoadCommand &LC) {166if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) {167// When removing all RPaths we don't need to care168// about what it contains169if (MachOConfig.RemoveAllRpaths)170return true;171172StringRef RPath = getPayloadString(LC);173if (RPathsToRemove.count(RPath)) {174RPathsToRemove.erase(RPath);175return true;176}177}178return false;179};180181if (Error E = Obj.removeLoadCommands(RemovePred))182return E;183184// Emit an error if the Mach-O binary does not contain an rpath path name185// specified in -delete_rpath.186for (StringRef RPath : MachOConfig.RPathsToRemove) {187if (RPathsToRemove.count(RPath))188return createStringError(errc::invalid_argument,189"no LC_RPATH load command with path: %s",190RPath.str().c_str());191}192193DenseSet<StringRef> RPaths;194195// Get all existing RPaths.196for (LoadCommand &LC : Obj.LoadCommands) {197if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH)198RPaths.insert(getPayloadString(LC));199}200201// Throw errors for invalid RPaths.202for (const auto &OldNew : MachOConfig.RPathsToUpdate) {203StringRef Old = OldNew.getFirst();204StringRef New = OldNew.getSecond();205if (!RPaths.contains(Old))206return createStringError(errc::invalid_argument,207"no LC_RPATH load command with path: " + Old);208if (RPaths.contains(New))209return createStringError(errc::invalid_argument,210"rpath '" + New +211"' would create a duplicate load command");212}213214// Update load commands.215for (LoadCommand &LC : Obj.LoadCommands) {216switch (LC.MachOLoadCommand.load_command_data.cmd) {217case MachO::LC_ID_DYLIB:218if (MachOConfig.SharedLibId)219updateLoadCommandPayloadString<MachO::dylib_command>(220LC, *MachOConfig.SharedLibId);221break;222223case MachO::LC_RPATH: {224StringRef RPath = getPayloadString(LC);225StringRef NewRPath = MachOConfig.RPathsToUpdate.lookup(RPath);226if (!NewRPath.empty())227updateLoadCommandPayloadString<MachO::rpath_command>(LC, NewRPath);228break;229}230231// TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB232// here once llvm-objcopy supports them.233case MachO::LC_LOAD_DYLIB:234case MachO::LC_LOAD_WEAK_DYLIB:235StringRef InstallName = getPayloadString(LC);236StringRef NewInstallName =237MachOConfig.InstallNamesToUpdate.lookup(InstallName);238if (!NewInstallName.empty())239updateLoadCommandPayloadString<MachO::dylib_command>(LC,240NewInstallName);241break;242}243}244245// Add new RPaths.246for (StringRef RPath : MachOConfig.RPathToAdd) {247if (RPaths.contains(RPath))248return createStringError(errc::invalid_argument,249"rpath '" + RPath +250"' would create a duplicate load command");251RPaths.insert(RPath);252Obj.LoadCommands.push_back(buildRPathLoadCommand(RPath));253}254255for (StringRef RPath : MachOConfig.RPathToPrepend) {256if (RPaths.contains(RPath))257return createStringError(errc::invalid_argument,258"rpath '" + RPath +259"' would create a duplicate load command");260261RPaths.insert(RPath);262Obj.LoadCommands.insert(Obj.LoadCommands.begin(),263buildRPathLoadCommand(RPath));264}265266// Unlike appending rpaths, the indexes of subsequent load commands must267// be recalculated after prepending one.268if (!MachOConfig.RPathToPrepend.empty())269Obj.updateLoadCommandIndexes();270271// Remove any empty segments if required.272if (!MachOConfig.EmptySegmentsToRemove.empty()) {273auto RemovePred = [&MachOConfig](const LoadCommand &LC) {274if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_SEGMENT_64 ||275LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_SEGMENT) {276return LC.Sections.empty() &&277MachOConfig.EmptySegmentsToRemove.contains(*LC.getSegmentName());278}279return false;280};281if (Error E = Obj.removeLoadCommands(RemovePred))282return E;283}284285return Error::success();286}287288static Error dumpSectionToFile(StringRef SecName, StringRef Filename,289Object &Obj) {290for (LoadCommand &LC : Obj.LoadCommands)291for (const std::unique_ptr<Section> &Sec : LC.Sections) {292if (Sec->CanonicalName == SecName) {293Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =294FileOutputBuffer::create(Filename, Sec->Content.size());295if (!BufferOrErr)296return BufferOrErr.takeError();297std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);298llvm::copy(Sec->Content, Buf->getBufferStart());299300if (Error E = Buf->commit())301return E;302return Error::success();303}304}305306return createStringError(object_error::parse_failed, "section '%s' not found",307SecName.str().c_str());308}309310static Error addSection(const NewSectionInfo &NewSection, Object &Obj) {311std::pair<StringRef, StringRef> Pair = NewSection.SectionName.split(',');312StringRef TargetSegName = Pair.first;313Section Sec(TargetSegName, Pair.second);314Sec.Content =315Obj.NewSectionsContents.save(NewSection.SectionData->getBuffer());316Sec.Size = Sec.Content.size();317318// Add the a section into an existing segment.319for (LoadCommand &LC : Obj.LoadCommands) {320std::optional<StringRef> SegName = LC.getSegmentName();321if (SegName && SegName == TargetSegName) {322uint64_t Addr = *LC.getSegmentVMAddr();323for (const std::unique_ptr<Section> &S : LC.Sections)324Addr = std::max(Addr, S->Addr + S->Size);325LC.Sections.push_back(std::make_unique<Section>(Sec));326LC.Sections.back()->Addr = Addr;327return Error::success();328}329}330331// There's no segment named TargetSegName. Create a new load command and332// Insert a new section into it.333LoadCommand &NewSegment =334Obj.addSegment(TargetSegName, alignTo(Sec.Size, 16384));335NewSegment.Sections.push_back(std::make_unique<Section>(Sec));336NewSegment.Sections.back()->Addr = *NewSegment.getSegmentVMAddr();337return Error::success();338}339340static Expected<Section &> findSection(StringRef SecName, Object &O) {341StringRef SegName;342std::tie(SegName, SecName) = SecName.split(",");343auto FoundSeg =344llvm::find_if(O.LoadCommands, [SegName](const LoadCommand &LC) {345return LC.getSegmentName() == SegName;346});347if (FoundSeg == O.LoadCommands.end())348return createStringError(errc::invalid_argument,349"could not find segment with name '%s'",350SegName.str().c_str());351auto FoundSec = llvm::find_if(FoundSeg->Sections,352[SecName](const std::unique_ptr<Section> &Sec) {353return Sec->Sectname == SecName;354});355if (FoundSec == FoundSeg->Sections.end())356return createStringError(errc::invalid_argument,357"could not find section with name '%s'",358SecName.str().c_str());359360assert(FoundSec->get()->CanonicalName == (SegName + "," + SecName).str());361return **FoundSec;362}363364static Error updateSection(const NewSectionInfo &NewSection, Object &O) {365Expected<Section &> SecToUpdateOrErr = findSection(NewSection.SectionName, O);366367if (!SecToUpdateOrErr)368return SecToUpdateOrErr.takeError();369Section &Sec = *SecToUpdateOrErr;370371if (NewSection.SectionData->getBufferSize() > Sec.Size)372return createStringError(373errc::invalid_argument,374"new section cannot be larger than previous section");375Sec.Content = O.NewSectionsContents.save(NewSection.SectionData->getBuffer());376Sec.Size = Sec.Content.size();377return Error::success();378}379380// isValidMachOCannonicalName returns success if Name is a MachO cannonical name381// ("<segment>,<section>") and lengths of both segment and section names are382// valid.383static Error isValidMachOCannonicalName(StringRef Name) {384if (Name.count(',') != 1)385return createStringError(errc::invalid_argument,386"invalid section name '%s' (should be formatted "387"as '<segment name>,<section name>')",388Name.str().c_str());389390std::pair<StringRef, StringRef> Pair = Name.split(',');391if (Pair.first.size() > 16)392return createStringError(errc::invalid_argument,393"too long segment name: '%s'",394Pair.first.str().c_str());395if (Pair.second.size() > 16)396return createStringError(errc::invalid_argument,397"too long section name: '%s'",398Pair.second.str().c_str());399return Error::success();400}401402static Error handleArgs(const CommonConfig &Config,403const MachOConfig &MachOConfig, Object &Obj) {404// Dump sections before add/remove for compatibility with GNU objcopy.405for (StringRef Flag : Config.DumpSection) {406StringRef SectionName;407StringRef FileName;408std::tie(SectionName, FileName) = Flag.split('=');409if (Error E = dumpSectionToFile(SectionName, FileName, Obj))410return E;411}412413if (Error E = removeSections(Config, Obj))414return E;415416// Mark symbols to determine which symbols are still needed.417if (Config.StripAll)418markSymbols(Config, Obj);419420updateAndRemoveSymbols(Config, MachOConfig, Obj);421422if (Config.StripAll)423for (LoadCommand &LC : Obj.LoadCommands)424for (std::unique_ptr<Section> &Sec : LC.Sections)425Sec->Relocations.clear();426427for (const NewSectionInfo &NewSection : Config.AddSection) {428if (Error E = isValidMachOCannonicalName(NewSection.SectionName))429return E;430if (Error E = addSection(NewSection, Obj))431return E;432}433434for (const NewSectionInfo &NewSection : Config.UpdateSection) {435if (Error E = isValidMachOCannonicalName(NewSection.SectionName))436return E;437if (Error E = updateSection(NewSection, Obj))438return E;439}440441if (Error E = processLoadCommands(MachOConfig, Obj))442return E;443444return Error::success();445}446447Error objcopy::macho::executeObjcopyOnBinary(const CommonConfig &Config,448const MachOConfig &MachOConfig,449object::MachOObjectFile &In,450raw_ostream &Out) {451MachOReader Reader(In);452Expected<std::unique_ptr<Object>> O = Reader.create();453if (!O)454return createFileError(Config.InputFilename, O.takeError());455456if (O->get()->Header.FileType == MachO::HeaderFileType::MH_PRELOAD)457return createStringError(std::errc::not_supported,458"%s: MH_PRELOAD files are not supported",459Config.InputFilename.str().c_str());460461if (Error E = handleArgs(Config, MachOConfig, **O))462return createFileError(Config.InputFilename, std::move(E));463464// Page size used for alignment of segment sizes in Mach-O executables and465// dynamic libraries.466uint64_t PageSize;467switch (In.getArch()) {468case Triple::ArchType::arm:469case Triple::ArchType::aarch64:470case Triple::ArchType::aarch64_32:471PageSize = 16384;472break;473default:474PageSize = 4096;475}476477MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(),478sys::path::filename(Config.OutputFilename), PageSize, Out);479if (auto E = Writer.finalize())480return E;481return Writer.write();482}483484Error objcopy::macho::executeObjcopyOnMachOUniversalBinary(485const MultiFormatConfig &Config, const MachOUniversalBinary &In,486raw_ostream &Out) {487SmallVector<OwningBinary<Binary>, 2> Binaries;488SmallVector<Slice, 2> Slices;489for (const auto &O : In.objects()) {490Expected<std::unique_ptr<Archive>> ArOrErr = O.getAsArchive();491if (ArOrErr) {492Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr =493createNewArchiveMembers(Config, **ArOrErr);494if (!NewArchiveMembersOrErr)495return NewArchiveMembersOrErr.takeError();496auto Kind = (*ArOrErr)->kind();497if (Kind == object::Archive::K_BSD)498Kind = object::Archive::K_DARWIN;499Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =500writeArchiveToBuffer(501*NewArchiveMembersOrErr,502(*ArOrErr)->hasSymbolTable() ? SymtabWritingMode::NormalSymtab503: SymtabWritingMode::NoSymtab,504Kind, Config.getCommonConfig().DeterministicArchives,505(*ArOrErr)->isThin());506if (!OutputBufferOrErr)507return OutputBufferOrErr.takeError();508Expected<std::unique_ptr<Binary>> BinaryOrErr =509object::createBinary(**OutputBufferOrErr);510if (!BinaryOrErr)511return BinaryOrErr.takeError();512Binaries.emplace_back(std::move(*BinaryOrErr),513std::move(*OutputBufferOrErr));514Slices.emplace_back(*cast<Archive>(Binaries.back().getBinary()),515O.getCPUType(), O.getCPUSubType(),516O.getArchFlagName(), O.getAlign());517continue;518}519// The methods getAsArchive, getAsObjectFile, getAsIRObject of the class520// ObjectForArch return an Error in case of the type mismatch. We need to521// check each in turn to see what kind of slice this is, so ignore errors522// produced along the way.523consumeError(ArOrErr.takeError());524525Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = O.getAsObjectFile();526if (!ObjOrErr) {527consumeError(ObjOrErr.takeError());528return createStringError(529std::errc::invalid_argument,530"slice for '%s' of the universal Mach-O binary "531"'%s' is not a Mach-O object or an archive",532O.getArchFlagName().c_str(),533Config.getCommonConfig().InputFilename.str().c_str());534}535std::string ArchFlagName = O.getArchFlagName();536537SmallVector<char, 0> Buffer;538raw_svector_ostream MemStream(Buffer);539540Expected<const MachOConfig &> MachO = Config.getMachOConfig();541if (!MachO)542return MachO.takeError();543544if (Error E = executeObjcopyOnBinary(Config.getCommonConfig(), *MachO,545**ObjOrErr, MemStream))546return E;547548auto MB = std::make_unique<SmallVectorMemoryBuffer>(549std::move(Buffer), ArchFlagName, /*RequiresNullTerminator=*/false);550Expected<std::unique_ptr<Binary>> BinaryOrErr = object::createBinary(*MB);551if (!BinaryOrErr)552return BinaryOrErr.takeError();553Binaries.emplace_back(std::move(*BinaryOrErr), std::move(MB));554Slices.emplace_back(*cast<MachOObjectFile>(Binaries.back().getBinary()),555O.getAlign());556}557558if (Error Err = writeUniversalBinaryToStream(Slices, Out))559return Err;560561return Error::success();562}563564565