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 "MachOObjcopy.h" 10#include "../CopyConfig.h" 11#include "MachOReader.h" 12#include "MachOWriter.h" 13#include "llvm/ADT/DenseSet.h" 14#include "llvm/Support/Errc.h" 15#include "llvm/Support/Error.h" 16 17namespace llvm { 18namespace objcopy { 19namespace macho { 20 21using namespace object; 22using SectionPred = std::function<bool(const std::unique_ptr<Section> &Sec)>; 23using LoadCommandPred = std::function<bool(const LoadCommand &LC)>; 24 25#ifndef NDEBUG 26static bool isLoadCommandWithPayloadString(const LoadCommand &LC) { 27 // TODO: Add support for LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB and 28 // LC_LAZY_LOAD_DYLIB 29 return LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH || 30 LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_ID_DYLIB || 31 LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_DYLIB || 32 LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_LOAD_WEAK_DYLIB; 33} 34#endif 35 36static StringRef getPayloadString(const LoadCommand &LC) { 37 assert(isLoadCommandWithPayloadString(LC) && 38 "unsupported load command encountered"); 39 40 return StringRef(reinterpret_cast<const char *>(LC.Payload.data()), 41 LC.Payload.size()) 42 .rtrim('\0'); 43} 44 45static Error removeSections(const CopyConfig &Config, Object &Obj) { 46 SectionPred RemovePred = [](const std::unique_ptr<Section> &) { 47 return false; 48 }; 49 50 if (!Config.ToRemove.empty()) { 51 RemovePred = [&Config, RemovePred](const std::unique_ptr<Section> &Sec) { 52 return Config.ToRemove.matches(Sec->CanonicalName); 53 }; 54 } 55 56 if (Config.StripAll || Config.StripDebug) { 57 // Remove all debug sections. 58 RemovePred = [RemovePred](const std::unique_ptr<Section> &Sec) { 59 if (Sec->Segname == "__DWARF") 60 return true; 61 62 return RemovePred(Sec); 63 }; 64 } 65 66 if (!Config.OnlySection.empty()) { 67 // Overwrite RemovePred because --only-section takes priority. 68 RemovePred = [&Config](const std::unique_ptr<Section> &Sec) { 69 return !Config.OnlySection.matches(Sec->CanonicalName); 70 }; 71 } 72 73 return Obj.removeSections(RemovePred); 74} 75 76static void markSymbols(const CopyConfig &Config, Object &Obj) { 77 // Symbols referenced from the indirect symbol table must not be removed. 78 for (IndirectSymbolEntry &ISE : Obj.IndirectSymTable.Symbols) 79 if (ISE.Symbol) 80 (*ISE.Symbol)->Referenced = true; 81} 82 83static void updateAndRemoveSymbols(const CopyConfig &Config, Object &Obj) { 84 for (SymbolEntry &Sym : Obj.SymTable) { 85 auto I = Config.SymbolsToRename.find(Sym.Name); 86 if (I != Config.SymbolsToRename.end()) 87 Sym.Name = std::string(I->getValue()); 88 } 89 90 auto RemovePred = [Config, &Obj](const std::unique_ptr<SymbolEntry> &N) { 91 if (N->Referenced) 92 return false; 93 if (Config.StripAll) 94 return true; 95 if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT)) 96 return true; 97 // This behavior is consistent with cctools' strip. 98 if (Config.StripSwiftSymbols && (Obj.Header.Flags & MachO::MH_DYLDLINK) && 99 Obj.SwiftVersion && *Obj.SwiftVersion && N->isSwiftSymbol()) 100 return true; 101 return false; 102 }; 103 104 Obj.SymTable.removeSymbols(RemovePred); 105} 106 107template <typename LCType> 108static void updateLoadCommandPayloadString(LoadCommand &LC, StringRef S) { 109 assert(isLoadCommandWithPayloadString(LC) && 110 "unsupported load command encountered"); 111 112 uint32_t NewCmdsize = alignTo(sizeof(LCType) + S.size() + 1, 8); 113 114 LC.MachOLoadCommand.load_command_data.cmdsize = NewCmdsize; 115 LC.Payload.assign(NewCmdsize - sizeof(LCType), 0); 116 std::copy(S.begin(), S.end(), LC.Payload.begin()); 117} 118 119static LoadCommand buildRPathLoadCommand(StringRef Path) { 120 LoadCommand LC; 121 MachO::rpath_command RPathLC; 122 RPathLC.cmd = MachO::LC_RPATH; 123 RPathLC.path = sizeof(MachO::rpath_command); 124 RPathLC.cmdsize = alignTo(sizeof(MachO::rpath_command) + Path.size() + 1, 8); 125 LC.MachOLoadCommand.rpath_command_data = RPathLC; 126 LC.Payload.assign(RPathLC.cmdsize - sizeof(MachO::rpath_command), 0); 127 std::copy(Path.begin(), Path.end(), LC.Payload.begin()); 128 return LC; 129} 130 131static Error processLoadCommands(const CopyConfig &Config, Object &Obj) { 132 // Remove RPaths. 133 DenseSet<StringRef> RPathsToRemove(Config.RPathsToRemove.begin(), 134 Config.RPathsToRemove.end()); 135 136 LoadCommandPred RemovePred = [&RPathsToRemove](const LoadCommand &LC) { 137 if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) { 138 StringRef RPath = getPayloadString(LC); 139 if (RPathsToRemove.count(RPath)) { 140 RPathsToRemove.erase(RPath); 141 return true; 142 } 143 } 144 return false; 145 }; 146 147 if (Error E = Obj.removeLoadCommands(RemovePred)) 148 return E; 149 150 // Emit an error if the Mach-O binary does not contain an rpath path name 151 // specified in -delete_rpath. 152 for (StringRef RPath : Config.RPathsToRemove) { 153 if (RPathsToRemove.count(RPath)) 154 return createStringError(errc::invalid_argument, 155 "no LC_RPATH load command with path: %s", 156 RPath.str().c_str()); 157 } 158 159 DenseSet<StringRef> RPaths; 160 161 // Get all existing RPaths. 162 for (LoadCommand &LC : Obj.LoadCommands) { 163 if (LC.MachOLoadCommand.load_command_data.cmd == MachO::LC_RPATH) 164 RPaths.insert(getPayloadString(LC)); 165 } 166 167 // Throw errors for invalid RPaths. 168 for (const auto &OldNew : Config.RPathsToUpdate) { 169 StringRef Old = OldNew.getFirst(); 170 StringRef New = OldNew.getSecond(); 171 if (RPaths.count(Old) == 0) 172 return createStringError(errc::invalid_argument, 173 "no LC_RPATH load command with path: " + Old); 174 if (RPaths.count(New) != 0) 175 return createStringError(errc::invalid_argument, 176 "rpath " + New + 177 " would create a duplicate load command"); 178 } 179 180 // Update load commands. 181 for (LoadCommand &LC : Obj.LoadCommands) { 182 switch (LC.MachOLoadCommand.load_command_data.cmd) { 183 case MachO::LC_ID_DYLIB: 184 if (Config.SharedLibId) { 185 StringRef Id = Config.SharedLibId.getValue(); 186 if (Id.empty()) 187 return createStringError(errc::invalid_argument, 188 "cannot specify an empty id"); 189 updateLoadCommandPayloadString<MachO::dylib_command>(LC, Id); 190 } 191 break; 192 193 case MachO::LC_RPATH: { 194 StringRef RPath = getPayloadString(LC); 195 StringRef NewRPath = Config.RPathsToUpdate.lookup(RPath); 196 if (!NewRPath.empty()) 197 updateLoadCommandPayloadString<MachO::rpath_command>(LC, NewRPath); 198 break; 199 } 200 201 // TODO: Add LC_REEXPORT_DYLIB, LC_LAZY_LOAD_DYLIB, and LC_LOAD_UPWARD_DYLIB 202 // here once llvm-objcopy supports them. 203 case MachO::LC_LOAD_DYLIB: 204 case MachO::LC_LOAD_WEAK_DYLIB: 205 StringRef InstallName = getPayloadString(LC); 206 StringRef NewInstallName = 207 Config.InstallNamesToUpdate.lookup(InstallName); 208 if (!NewInstallName.empty()) 209 updateLoadCommandPayloadString<MachO::dylib_command>(LC, 210 NewInstallName); 211 break; 212 } 213 } 214 215 // Add new RPaths. 216 for (StringRef RPath : Config.RPathToAdd) { 217 if (RPaths.count(RPath) != 0) 218 return createStringError(errc::invalid_argument, 219 "rpath " + RPath + 220 " would create a duplicate load command"); 221 RPaths.insert(RPath); 222 Obj.addLoadCommand(buildRPathLoadCommand(RPath)); 223 } 224 225 return Error::success(); 226} 227 228static Error dumpSectionToFile(StringRef SecName, StringRef Filename, 229 Object &Obj) { 230 for (LoadCommand &LC : Obj.LoadCommands) 231 for (const std::unique_ptr<Section> &Sec : LC.Sections) { 232 if (Sec->CanonicalName == SecName) { 233 Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = 234 FileOutputBuffer::create(Filename, Sec->Content.size()); 235 if (!BufferOrErr) 236 return BufferOrErr.takeError(); 237 std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); 238 llvm::copy(Sec->Content, Buf->getBufferStart()); 239 240 if (Error E = Buf->commit()) 241 return E; 242 return Error::success(); 243 } 244 } 245 246 return createStringError(object_error::parse_failed, "section '%s' not found", 247 SecName.str().c_str()); 248} 249 250static Error addSection(StringRef SecName, StringRef Filename, Object &Obj) { 251 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 252 MemoryBuffer::getFile(Filename); 253 if (!BufOrErr) 254 return createFileError(Filename, errorCodeToError(BufOrErr.getError())); 255 std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); 256 257 std::pair<StringRef, StringRef> Pair = SecName.split(','); 258 StringRef TargetSegName = Pair.first; 259 Section Sec(TargetSegName, Pair.second); 260 Sec.Content = Obj.NewSectionsContents.save(Buf->getBuffer()); 261 262 // Add the a section into an existing segment. 263 for (LoadCommand &LC : Obj.LoadCommands) { 264 Optional<StringRef> SegName = LC.getSegmentName(); 265 if (SegName && SegName == TargetSegName) { 266 LC.Sections.push_back(std::make_unique<Section>(Sec)); 267 return Error::success(); 268 } 269 } 270 271 // There's no segment named TargetSegName. Create a new load command and 272 // Insert a new section into it. 273 LoadCommand &NewSegment = Obj.addSegment(TargetSegName); 274 NewSegment.Sections.push_back(std::make_unique<Section>(Sec)); 275 return Error::success(); 276} 277 278// isValidMachOCannonicalName returns success if Name is a MachO cannonical name 279// ("<segment>,<section>") and lengths of both segment and section names are 280// valid. 281Error isValidMachOCannonicalName(StringRef Name) { 282 if (Name.count(',') != 1) 283 return createStringError(errc::invalid_argument, 284 "invalid section name '%s' (should be formatted " 285 "as '<segment name>,<section name>')", 286 Name.str().c_str()); 287 288 std::pair<StringRef, StringRef> Pair = Name.split(','); 289 if (Pair.first.size() > 16) 290 return createStringError(errc::invalid_argument, 291 "too long segment name: '%s'", 292 Pair.first.str().c_str()); 293 if (Pair.second.size() > 16) 294 return createStringError(errc::invalid_argument, 295 "too long section name: '%s'", 296 Pair.second.str().c_str()); 297 return Error::success(); 298} 299 300static Error handleArgs(const CopyConfig &Config, Object &Obj) { 301 if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() || 302 Config.BuildIdLinkInput || Config.BuildIdLinkOutput || 303 !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() || 304 !Config.AllocSectionsPrefix.empty() || !Config.KeepSection.empty() || 305 Config.NewSymbolVisibility || !Config.SymbolsToGlobalize.empty() || 306 !Config.SymbolsToKeep.empty() || !Config.SymbolsToLocalize.empty() || 307 !Config.SymbolsToWeaken.empty() || !Config.SymbolsToKeepGlobal.empty() || 308 !Config.SectionsToRename.empty() || 309 !Config.UnneededSymbolsToRemove.empty() || 310 !Config.SetSectionAlignment.empty() || !Config.SetSectionFlags.empty() || 311 Config.ExtractDWO || Config.LocalizeHidden || Config.PreserveDates || 312 Config.StripAllGNU || Config.StripDWO || Config.StripNonAlloc || 313 Config.StripSections || Config.Weaken || Config.DecompressDebugSections || 314 Config.StripNonAlloc || Config.StripSections || Config.StripUnneeded || 315 Config.DiscardMode == DiscardType::Locals || 316 !Config.SymbolsToAdd.empty() || Config.EntryExpr) { 317 return createStringError(llvm::errc::invalid_argument, 318 "option not supported by llvm-objcopy for MachO"); 319 } 320 321 // Dump sections before add/remove for compatibility with GNU objcopy. 322 for (StringRef Flag : Config.DumpSection) { 323 StringRef SectionName; 324 StringRef FileName; 325 std::tie(SectionName, FileName) = Flag.split('='); 326 if (Error E = dumpSectionToFile(SectionName, FileName, Obj)) 327 return E; 328 } 329 330 if (Error E = removeSections(Config, Obj)) 331 return E; 332 333 // Mark symbols to determine which symbols are still needed. 334 if (Config.StripAll) 335 markSymbols(Config, Obj); 336 337 updateAndRemoveSymbols(Config, Obj); 338 339 if (Config.StripAll) 340 for (LoadCommand &LC : Obj.LoadCommands) 341 for (std::unique_ptr<Section> &Sec : LC.Sections) 342 Sec->Relocations.clear(); 343 344 for (const auto &Flag : Config.AddSection) { 345 std::pair<StringRef, StringRef> SecPair = Flag.split("="); 346 StringRef SecName = SecPair.first; 347 StringRef File = SecPair.second; 348 if (Error E = isValidMachOCannonicalName(SecName)) 349 return E; 350 if (Error E = addSection(SecName, File, Obj)) 351 return E; 352 } 353 354 if (Error E = processLoadCommands(Config, Obj)) 355 return E; 356 357 return Error::success(); 358} 359 360Error executeObjcopyOnBinary(const CopyConfig &Config, 361 object::MachOObjectFile &In, Buffer &Out) { 362 MachOReader Reader(In); 363 std::unique_ptr<Object> O = Reader.create(); 364 if (!O) 365 return createFileError( 366 Config.InputFilename, 367 createStringError(object_error::parse_failed, 368 "unable to deserialize MachO object")); 369 370 if (Error E = handleArgs(Config, *O)) 371 return createFileError(Config.InputFilename, std::move(E)); 372 373 // Page size used for alignment of segment sizes in Mach-O executables and 374 // dynamic libraries. 375 uint64_t PageSize; 376 switch (In.getArch()) { 377 case Triple::ArchType::arm: 378 case Triple::ArchType::aarch64: 379 case Triple::ArchType::aarch64_32: 380 PageSize = 16384; 381 break; 382 default: 383 PageSize = 4096; 384 } 385 386 MachOWriter Writer(*O, In.is64Bit(), In.isLittleEndian(), PageSize, Out); 387 if (auto E = Writer.finalize()) 388 return E; 389 return Writer.write(); 390} 391 392} // end namespace macho 393} // end namespace objcopy 394} // end namespace llvm 395