1 //===- llvm-objcopy.cpp ---------------------------------------------------===// 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.h" 10 #include "COFF/COFFObjcopy.h" 11 #include "CopyConfig.h" 12 #include "ELF/ELFObjcopy.h" 13 #include "MachO/MachOObjcopy.h" 14 #include "wasm/WasmObjcopy.h" 15 16 #include "llvm/ADT/STLExtras.h" 17 #include "llvm/ADT/SmallVector.h" 18 #include "llvm/ADT/StringRef.h" 19 #include "llvm/ADT/Twine.h" 20 #include "llvm/BinaryFormat/ELF.h" 21 #include "llvm/Object/Archive.h" 22 #include "llvm/Object/ArchiveWriter.h" 23 #include "llvm/Object/Binary.h" 24 #include "llvm/Object/COFF.h" 25 #include "llvm/Object/ELFObjectFile.h" 26 #include "llvm/Object/ELFTypes.h" 27 #include "llvm/Object/Error.h" 28 #include "llvm/Object/MachO.h" 29 #include "llvm/Object/MachOUniversal.h" 30 #include "llvm/Object/Wasm.h" 31 #include "llvm/Option/Arg.h" 32 #include "llvm/Option/ArgList.h" 33 #include "llvm/Option/Option.h" 34 #include "llvm/Support/Casting.h" 35 #include "llvm/Support/CommandLine.h" 36 #include "llvm/Support/Errc.h" 37 #include "llvm/Support/Error.h" 38 #include "llvm/Support/ErrorHandling.h" 39 #include "llvm/Support/ErrorOr.h" 40 #include "llvm/Support/Host.h" 41 #include "llvm/Support/InitLLVM.h" 42 #include "llvm/Support/Memory.h" 43 #include "llvm/Support/Path.h" 44 #include "llvm/Support/Process.h" 45 #include "llvm/Support/SmallVectorMemoryBuffer.h" 46 #include "llvm/Support/StringSaver.h" 47 #include "llvm/Support/WithColor.h" 48 #include "llvm/Support/raw_ostream.h" 49 #include <algorithm> 50 #include <cassert> 51 #include <cstdlib> 52 #include <memory> 53 #include <string> 54 #include <system_error> 55 #include <utility> 56 57 namespace llvm { 58 namespace objcopy { 59 60 Error writeToFile(StringRef OutputFileName, 61 std::function<Error(raw_ostream &)> Write, bool KeepOwnership, 62 unsigned UserID, unsigned GroupID) { 63 if (OutputFileName == "-") 64 return Write(outs()); 65 66 if (OutputFileName == "/dev/null") { 67 raw_null_ostream Out; 68 return Write(Out); 69 } 70 71 unsigned Mode = sys::fs::all_read | sys::fs::all_write | sys::fs::all_exe; 72 Expected<sys::fs::TempFile> Temp = 73 sys::fs::TempFile::create(OutputFileName + ".temp-objcopy-%%%%%%", Mode); 74 if (!Temp) 75 return createFileError(OutputFileName, Temp.takeError()); 76 77 #ifndef _WIN32 78 // Try to preserve file ownership if requested. 79 if (KeepOwnership) { 80 sys::fs::file_status Stat; 81 if (!sys::fs::status(Temp->FD, Stat) && Stat.getUser() == 0) 82 sys::fs::changeFileOwnership(Temp->FD, UserID, GroupID); 83 } 84 #endif 85 86 raw_fd_ostream Out(Temp->FD, false); 87 88 if (Error E = Write(Out)) { 89 if (Error DiscardError = Temp->discard()) 90 return joinErrors(std::move(E), std::move(DiscardError)); 91 return E; 92 } 93 Out.flush(); 94 95 return Temp->keep(OutputFileName); 96 } 97 98 // The name this program was invoked as. 99 StringRef ToolName; 100 101 ErrorSuccess reportWarning(Error E) { 102 assert(E); 103 WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n'; 104 return Error::success(); 105 } 106 107 static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) { 108 StringRef Stem = sys::path::stem(ToolName); 109 auto Is = [=](StringRef Tool) { 110 // We need to recognize the following filenames: 111 // 112 // llvm-objcopy -> objcopy 113 // strip-10.exe -> strip 114 // powerpc64-unknown-freebsd13-objcopy -> objcopy 115 // llvm-install-name-tool -> install-name-tool 116 auto I = Stem.rfind_lower(Tool); 117 return I != StringRef::npos && 118 (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); 119 }; 120 121 if (Is("bitcode-strip") || Is("bitcode_strip")) 122 return parseBitcodeStripOptions(Args); 123 else if (Is("strip")) 124 return parseStripOptions(Args, reportWarning); 125 else if (Is("install-name-tool") || Is("install_name_tool")) 126 return parseInstallNameToolOptions(Args); 127 else 128 return parseObjcopyOptions(Args, reportWarning); 129 } 130 131 } // end namespace objcopy 132 } // end namespace llvm 133 134 using namespace llvm; 135 using namespace llvm::object; 136 using namespace llvm::objcopy; 137 138 // For regular archives this function simply calls llvm::writeArchive, 139 // For thin archives it writes the archive file itself as well as its members. 140 static Error deepWriteArchive(StringRef ArcName, 141 ArrayRef<NewArchiveMember> NewMembers, 142 bool WriteSymtab, object::Archive::Kind Kind, 143 bool Deterministic, bool Thin) { 144 if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind, 145 Deterministic, Thin)) 146 return createFileError(ArcName, std::move(E)); 147 148 if (!Thin) 149 return Error::success(); 150 151 for (const NewArchiveMember &Member : NewMembers) { 152 // For regular files (as is the case for deepWriteArchive), 153 // FileOutputBuffer::create will return OnDiskBuffer. 154 // OnDiskBuffer uses a temporary file and then renames it. So in reality 155 // there is no inefficiency / duplicated in-memory buffers in this case. For 156 // now in-memory buffers can not be completely avoided since 157 // NewArchiveMember still requires them even though writeArchive does not 158 // write them on disk. 159 Expected<std::unique_ptr<FileOutputBuffer>> FB = FileOutputBuffer::create( 160 Member.MemberName, Member.Buf->getBufferSize(), 161 FileOutputBuffer::F_executable | FileOutputBuffer::F_keep_ownership); 162 if (!FB) 163 return FB.takeError(); 164 std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), 165 (*FB)->getBufferStart()); 166 if (Error E = (*FB)->commit()) 167 return E; 168 } 169 return Error::success(); 170 } 171 172 /// The function executeObjcopyOnIHex does the dispatch based on the format 173 /// of the output specified by the command line options. 174 static Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In, 175 raw_ostream &Out) { 176 // TODO: support output formats other than ELF. 177 if (Error E = Config.parseELFConfig()) 178 return E; 179 return elf::executeObjcopyOnIHex(Config, In, Out); 180 } 181 182 /// The function executeObjcopyOnRawBinary does the dispatch based on the format 183 /// of the output specified by the command line options. 184 static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, 185 raw_ostream &Out) { 186 switch (Config.OutputFormat) { 187 case FileFormat::ELF: 188 // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the 189 // output format is binary/ihex or it's not given. This behavior differs from 190 // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details. 191 case FileFormat::Binary: 192 case FileFormat::IHex: 193 case FileFormat::Unspecified: 194 if (Error E = Config.parseELFConfig()) 195 return E; 196 return elf::executeObjcopyOnRawBinary(Config, In, Out); 197 } 198 199 llvm_unreachable("unsupported output format"); 200 } 201 202 /// The function executeObjcopyOnBinary does the dispatch based on the format 203 /// of the input binary (ELF, MachO or COFF). 204 static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, 205 raw_ostream &Out) { 206 if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) { 207 if (Error E = Config.parseELFConfig()) 208 return E; 209 return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); 210 } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) 211 return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); 212 else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) 213 return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); 214 else if (auto *MachOUniversalBinary = 215 dyn_cast<object::MachOUniversalBinary>(&In)) 216 return macho::executeObjcopyOnMachOUniversalBinary( 217 Config, *MachOUniversalBinary, Out); 218 else if (auto *WasmBinary = dyn_cast<object::WasmObjectFile>(&In)) 219 return objcopy::wasm::executeObjcopyOnBinary(Config, *WasmBinary, Out); 220 else 221 return createStringError(object_error::invalid_file_type, 222 "unsupported object file format"); 223 } 224 225 namespace llvm { 226 namespace objcopy { 227 228 Expected<std::vector<NewArchiveMember>> 229 createNewArchiveMembers(CopyConfig &Config, const Archive &Ar) { 230 std::vector<NewArchiveMember> NewArchiveMembers; 231 Error Err = Error::success(); 232 for (const Archive::Child &Child : Ar.children(Err)) { 233 Expected<StringRef> ChildNameOrErr = Child.getName(); 234 if (!ChildNameOrErr) 235 return createFileError(Ar.getFileName(), ChildNameOrErr.takeError()); 236 237 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); 238 if (!ChildOrErr) 239 return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", 240 ChildOrErr.takeError()); 241 242 SmallVector<char, 0> Buffer; 243 raw_svector_ostream MemStream(Buffer); 244 245 if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MemStream)) 246 return std::move(E); 247 248 Expected<NewArchiveMember> Member = 249 NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); 250 if (!Member) 251 return createFileError(Ar.getFileName(), Member.takeError()); 252 253 Member->Buf = std::make_unique<SmallVectorMemoryBuffer>( 254 std::move(Buffer), ChildNameOrErr.get()); 255 Member->MemberName = Member->Buf->getBufferIdentifier(); 256 NewArchiveMembers.push_back(std::move(*Member)); 257 } 258 if (Err) 259 return createFileError(Config.InputFilename, std::move(Err)); 260 return std::move(NewArchiveMembers); 261 } 262 263 } // end namespace objcopy 264 } // end namespace llvm 265 266 static Error executeObjcopyOnArchive(CopyConfig &Config, 267 const object::Archive &Ar) { 268 Expected<std::vector<NewArchiveMember>> NewArchiveMembersOrErr = 269 createNewArchiveMembers(Config, Ar); 270 if (!NewArchiveMembersOrErr) 271 return NewArchiveMembersOrErr.takeError(); 272 return deepWriteArchive(Config.OutputFilename, *NewArchiveMembersOrErr, 273 Ar.hasSymbolTable(), Ar.kind(), 274 Config.DeterministicArchives, Ar.isThin()); 275 } 276 277 static Error restoreStatOnFile(StringRef Filename, 278 const sys::fs::file_status &Stat, 279 const CopyConfig &Config) { 280 int FD; 281 282 // Writing to stdout should not be treated as an error here, just 283 // do not set access/modification times or permissions. 284 if (Filename == "-") 285 return Error::success(); 286 287 if (auto EC = 288 sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) 289 return createFileError(Filename, EC); 290 291 if (Config.PreserveDates) 292 if (auto EC = sys::fs::setLastAccessAndModificationTime( 293 FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) 294 return createFileError(Filename, EC); 295 296 sys::fs::file_status OStat; 297 if (std::error_code EC = sys::fs::status(FD, OStat)) 298 return createFileError(Filename, EC); 299 if (OStat.type() == sys::fs::file_type::regular_file) { 300 sys::fs::perms Perm = Stat.permissions(); 301 if (Config.InputFilename != Config.OutputFilename) 302 Perm = static_cast<sys::fs::perms>(Perm & ~sys::fs::getUmask() & ~06000); 303 #ifdef _WIN32 304 if (auto EC = sys::fs::setPermissions(Filename, Perm)) 305 #else 306 if (auto EC = sys::fs::setPermissions(FD, Perm)) 307 #endif 308 return createFileError(Filename, EC); 309 } 310 311 if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) 312 return createFileError(Filename, EC); 313 314 return Error::success(); 315 } 316 317 /// The function executeObjcopy does the higher level dispatch based on the type 318 /// of input (raw binary, archive or single object file) and takes care of the 319 /// format-agnostic modifications, i.e. preserving dates. 320 static Error executeObjcopy(CopyConfig &Config) { 321 sys::fs::file_status Stat; 322 if (Config.InputFilename != "-") { 323 if (auto EC = sys::fs::status(Config.InputFilename, Stat)) 324 return createFileError(Config.InputFilename, EC); 325 } else { 326 Stat.permissions(static_cast<sys::fs::perms>(0777)); 327 } 328 329 using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, raw_ostream &); 330 ProcessRawFn ProcessRaw; 331 switch (Config.InputFormat) { 332 case FileFormat::Binary: 333 ProcessRaw = executeObjcopyOnRawBinary; 334 break; 335 case FileFormat::IHex: 336 ProcessRaw = executeObjcopyOnIHex; 337 break; 338 default: 339 ProcessRaw = nullptr; 340 } 341 342 if (ProcessRaw) { 343 auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); 344 if (!BufOrErr) 345 return createFileError(Config.InputFilename, BufOrErr.getError()); 346 347 if (Error E = writeToFile( 348 Config.OutputFilename, [&](raw_ostream &OutFile) -> Error { 349 return ProcessRaw(Config, *BufOrErr->get(), OutFile); 350 })) 351 return E; 352 } else { 353 Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = 354 createBinary(Config.InputFilename); 355 if (!BinaryOrErr) 356 return createFileError(Config.InputFilename, BinaryOrErr.takeError()); 357 358 if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { 359 if (Error E = executeObjcopyOnArchive(Config, *Ar)) 360 return E; 361 } else { 362 if (Error E = writeToFile( 363 Config.OutputFilename, 364 [&](raw_ostream &OutFile) -> Error { 365 return executeObjcopyOnBinary( 366 Config, *BinaryOrErr.get().getBinary(), OutFile); 367 }, 368 Config.InputFilename != "-" && 369 Config.InputFilename == Config.OutputFilename, 370 Stat.getUser(), Stat.getGroup())) 371 return E; 372 } 373 } 374 375 if (Error E = restoreStatOnFile(Config.OutputFilename, Stat, Config)) 376 return E; 377 378 if (!Config.SplitDWO.empty()) { 379 Stat.permissions(static_cast<sys::fs::perms>(0666)); 380 if (Error E = restoreStatOnFile(Config.SplitDWO, Stat, Config)) 381 return E; 382 } 383 384 return Error::success(); 385 } 386 387 namespace { 388 389 } // anonymous namespace 390 391 int main(int argc, char **argv) { 392 InitLLVM X(argc, argv); 393 ToolName = argv[0]; 394 395 // Expand response files. 396 // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp, 397 // into a separate function in the CommandLine library and call that function 398 // here. This is duplicated code. 399 SmallVector<const char *, 20> NewArgv(argv, argv + argc); 400 BumpPtrAllocator A; 401 StringSaver Saver(A); 402 cl::ExpandResponseFiles(Saver, 403 Triple(sys::getProcessTriple()).isOSWindows() 404 ? cl::TokenizeWindowsCommandLine 405 : cl::TokenizeGNUCommandLine, 406 NewArgv); 407 408 auto Args = makeArrayRef(NewArgv).drop_front(); 409 Expected<DriverConfig> DriverConfig = getDriverConfig(Args); 410 411 if (!DriverConfig) { 412 logAllUnhandledErrors(DriverConfig.takeError(), 413 WithColor::error(errs(), ToolName)); 414 return 1; 415 } 416 for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { 417 if (Error E = executeObjcopy(CopyConfig)) { 418 logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); 419 return 1; 420 } 421 } 422 423 return 0; 424 } 425