1326938Sdim//===- llvm-objcopy.cpp ---------------------------------------------------===// 2326938Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6326938Sdim// 7326938Sdim//===----------------------------------------------------------------------===// 8326938Sdim 9326938Sdim#include "llvm-objcopy.h" 10344779Sdim#include "Buffer.h" 11344779Sdim#include "CopyConfig.h" 12344779Sdim#include "ELF/ELFObjcopy.h" 13353358Sdim#include "COFF/COFFObjcopy.h" 14353358Sdim#include "MachO/MachOObjcopy.h" 15344779Sdim 16326938Sdim#include "llvm/ADT/STLExtras.h" 17344779Sdim#include "llvm/ADT/SmallVector.h" 18326938Sdim#include "llvm/ADT/StringRef.h" 19326938Sdim#include "llvm/ADT/Twine.h" 20341825Sdim#include "llvm/Object/Archive.h" 21341825Sdim#include "llvm/Object/ArchiveWriter.h" 22326938Sdim#include "llvm/Object/Binary.h" 23344779Sdim#include "llvm/Object/COFF.h" 24326938Sdim#include "llvm/Object/ELFObjectFile.h" 25326938Sdim#include "llvm/Object/ELFTypes.h" 26326938Sdim#include "llvm/Object/Error.h" 27353358Sdim#include "llvm/Object/MachO.h" 28341825Sdim#include "llvm/Option/Arg.h" 29341825Sdim#include "llvm/Option/ArgList.h" 30341825Sdim#include "llvm/Option/Option.h" 31326938Sdim#include "llvm/Support/Casting.h" 32360784Sdim#include "llvm/Support/CommandLine.h" 33326938Sdim#include "llvm/Support/Error.h" 34326938Sdim#include "llvm/Support/ErrorHandling.h" 35326938Sdim#include "llvm/Support/ErrorOr.h" 36341825Sdim#include "llvm/Support/InitLLVM.h" 37344779Sdim#include "llvm/Support/Memory.h" 38341825Sdim#include "llvm/Support/Path.h" 39344779Sdim#include "llvm/Support/Process.h" 40360784Sdim#include "llvm/Support/StringSaver.h" 41344779Sdim#include "llvm/Support/WithColor.h" 42326938Sdim#include "llvm/Support/raw_ostream.h" 43326938Sdim#include <algorithm> 44326938Sdim#include <cassert> 45326938Sdim#include <cstdlib> 46326938Sdim#include <memory> 47326938Sdim#include <string> 48326938Sdim#include <system_error> 49326938Sdim#include <utility> 50326938Sdim 51326938Sdimnamespace llvm { 52341825Sdimnamespace objcopy { 53326938Sdim 54341825Sdim// The name this program was invoked as. 55341825SdimStringRef ToolName; 56341825Sdim 57326938SdimLLVM_ATTRIBUTE_NORETURN void error(Twine Message) { 58353358Sdim WithColor::error(errs(), ToolName) << Message << "\n"; 59326938Sdim exit(1); 60326938Sdim} 61326938Sdim 62353358SdimLLVM_ATTRIBUTE_NORETURN void error(Error E) { 63353358Sdim assert(E); 64353358Sdim std::string Buf; 65353358Sdim raw_string_ostream OS(Buf); 66353358Sdim logAllUnhandledErrors(std::move(E), OS); 67353358Sdim OS.flush(); 68353358Sdim WithColor::error(errs(), ToolName) << Buf; 69353358Sdim exit(1); 70353358Sdim} 71353358Sdim 72326938SdimLLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { 73326938Sdim assert(EC); 74353358Sdim error(createFileError(File, EC)); 75326938Sdim} 76326938Sdim 77326938SdimLLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { 78326938Sdim assert(E); 79326938Sdim std::string Buf; 80326938Sdim raw_string_ostream OS(Buf); 81344779Sdim logAllUnhandledErrors(std::move(E), OS); 82326938Sdim OS.flush(); 83344779Sdim WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; 84326938Sdim exit(1); 85326938Sdim} 86326938Sdim 87353358SdimErrorSuccess reportWarning(Error E) { 88353358Sdim assert(E); 89360784Sdim WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n'; 90353358Sdim return Error::success(); 91353358Sdim} 92353358Sdim 93341825Sdim} // end namespace objcopy 94326938Sdim} // end namespace llvm 95326938Sdim 96344779Sdimusing namespace llvm; 97344779Sdimusing namespace llvm::object; 98344779Sdimusing namespace llvm::objcopy; 99326938Sdim 100341825Sdim// For regular archives this function simply calls llvm::writeArchive, 101341825Sdim// For thin archives it writes the archive file itself as well as its members. 102341825Sdimstatic Error deepWriteArchive(StringRef ArcName, 103341825Sdim ArrayRef<NewArchiveMember> NewMembers, 104341825Sdim bool WriteSymtab, object::Archive::Kind Kind, 105341825Sdim bool Deterministic, bool Thin) { 106353358Sdim if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind, 107353358Sdim Deterministic, Thin)) 108353358Sdim return createFileError(ArcName, std::move(E)); 109353358Sdim 110353358Sdim if (!Thin) 111353358Sdim return Error::success(); 112353358Sdim 113341825Sdim for (const NewArchiveMember &Member : NewMembers) { 114341825Sdim // Internally, FileBuffer will use the buffer created by 115341825Sdim // FileOutputBuffer::create, for regular files (that is the case for 116341825Sdim // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer. 117341825Sdim // OnDiskBuffer uses a temporary file and then renames it. So in reality 118341825Sdim // there is no inefficiency / duplicated in-memory buffers in this case. For 119341825Sdim // now in-memory buffers can not be completely avoided since 120341825Sdim // NewArchiveMember still requires them even though writeArchive does not 121341825Sdim // write them on disk. 122341825Sdim FileBuffer FB(Member.MemberName); 123353358Sdim if (Error E = FB.allocate(Member.Buf->getBufferSize())) 124353358Sdim return E; 125341825Sdim std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), 126341825Sdim FB.getBufferStart()); 127353358Sdim if (Error E = FB.commit()) 128341825Sdim return E; 129326938Sdim } 130341825Sdim return Error::success(); 131341825Sdim} 132341825Sdim 133353358Sdim/// The function executeObjcopyOnIHex does the dispatch based on the format 134353358Sdim/// of the output specified by the command line options. 135360784Sdimstatic Error executeObjcopyOnIHex(CopyConfig &Config, MemoryBuffer &In, 136353358Sdim Buffer &Out) { 137353358Sdim // TODO: support output formats other than ELF. 138360784Sdim if (Error E = Config.parseELFConfig()) 139360784Sdim return E; 140353358Sdim return elf::executeObjcopyOnIHex(Config, In, Out); 141353358Sdim} 142353358Sdim 143344779Sdim/// The function executeObjcopyOnRawBinary does the dispatch based on the format 144344779Sdim/// of the output specified by the command line options. 145360784Sdimstatic Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, 146360784Sdim Buffer &Out) { 147353358Sdim switch (Config.OutputFormat) { 148353358Sdim case FileFormat::ELF: 149353358Sdim // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the 150353358Sdim // output format is binary/ihex or it's not given. This behavior differs from 151353358Sdim // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details. 152353358Sdim case FileFormat::Binary: 153353358Sdim case FileFormat::IHex: 154353358Sdim case FileFormat::Unspecified: 155360784Sdim if (Error E = Config.parseELFConfig()) 156360784Sdim return E; 157353358Sdim return elf::executeObjcopyOnRawBinary(Config, In, Out); 158353358Sdim } 159353358Sdim 160353358Sdim llvm_unreachable("unsupported output format"); 161344779Sdim} 162344779Sdim 163344779Sdim/// The function executeObjcopyOnBinary does the dispatch based on the format 164344779Sdim/// of the input binary (ELF, MachO or COFF). 165360784Sdimstatic Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, 166360784Sdim Buffer &Out) { 167360784Sdim if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) { 168360784Sdim if (Error E = Config.parseELFConfig()) 169360784Sdim return E; 170344779Sdim return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); 171360784Sdim } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) 172344779Sdim return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); 173353358Sdim else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) 174353358Sdim return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); 175344779Sdim else 176353358Sdim return createStringError(object_error::invalid_file_type, 177353358Sdim "unsupported object file format"); 178344779Sdim} 179344779Sdim 180360784Sdimstatic Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) { 181341825Sdim std::vector<NewArchiveMember> NewArchiveMembers; 182341825Sdim Error Err = Error::success(); 183341825Sdim for (const Archive::Child &Child : Ar.children(Err)) { 184353358Sdim Expected<StringRef> ChildNameOrErr = Child.getName(); 185353358Sdim if (!ChildNameOrErr) 186353358Sdim return createFileError(Ar.getFileName(), ChildNameOrErr.takeError()); 187353358Sdim 188341825Sdim Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); 189341825Sdim if (!ChildOrErr) 190353358Sdim return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", 191353358Sdim ChildOrErr.takeError()); 192344779Sdim 193341825Sdim MemBuffer MB(ChildNameOrErr.get()); 194353358Sdim if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) 195353358Sdim return E; 196341825Sdim 197341825Sdim Expected<NewArchiveMember> Member = 198344779Sdim NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); 199341825Sdim if (!Member) 200353358Sdim return createFileError(Ar.getFileName(), Member.takeError()); 201341825Sdim Member->Buf = MB.releaseMemoryBuffer(); 202341825Sdim Member->MemberName = Member->Buf->getBufferIdentifier(); 203341825Sdim NewArchiveMembers.push_back(std::move(*Member)); 204341825Sdim } 205353358Sdim if (Err) 206353358Sdim return createFileError(Config.InputFilename, std::move(Err)); 207341825Sdim 208353358Sdim return deepWriteArchive(Config.OutputFilename, NewArchiveMembers, 209353358Sdim Ar.hasSymbolTable(), Ar.kind(), 210353358Sdim Config.DeterministicArchives, Ar.isThin()); 211341825Sdim} 212341825Sdim 213353358Sdimstatic Error restoreStatOnFile(StringRef Filename, 214353358Sdim const sys::fs::file_status &Stat, 215353358Sdim bool PreserveDates) { 216344779Sdim int FD; 217341825Sdim 218353358Sdim // Writing to stdout should not be treated as an error here, just 219353358Sdim // do not set access/modification times or permissions. 220353358Sdim if (Filename == "-") 221353358Sdim return Error::success(); 222353358Sdim 223344779Sdim if (auto EC = 224344779Sdim sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) 225353358Sdim return createFileError(Filename, EC); 226341825Sdim 227353358Sdim if (PreserveDates) 228353358Sdim if (auto EC = sys::fs::setLastAccessAndModificationTime( 229353358Sdim FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) 230353358Sdim return createFileError(Filename, EC); 231344779Sdim 232353358Sdim sys::fs::file_status OStat; 233353358Sdim if (std::error_code EC = sys::fs::status(FD, OStat)) 234353358Sdim return createFileError(Filename, EC); 235353358Sdim if (OStat.type() == sys::fs::file_type::regular_file) 236353358Sdim#ifdef _WIN32 237353358Sdim if (auto EC = sys::fs::setPermissions( 238353358Sdim Filename, static_cast<sys::fs::perms>(Stat.permissions() & 239353358Sdim ~sys::fs::getUmask()))) 240353358Sdim#else 241353358Sdim if (auto EC = sys::fs::setPermissions( 242353358Sdim FD, static_cast<sys::fs::perms>(Stat.permissions() & 243353358Sdim ~sys::fs::getUmask()))) 244353358Sdim#endif 245353358Sdim return createFileError(Filename, EC); 246353358Sdim 247344779Sdim if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) 248353358Sdim return createFileError(Filename, EC); 249353358Sdim 250353358Sdim return Error::success(); 251341825Sdim} 252341825Sdim 253344779Sdim/// The function executeObjcopy does the higher level dispatch based on the type 254344779Sdim/// of input (raw binary, archive or single object file) and takes care of the 255344779Sdim/// format-agnostic modifications, i.e. preserving dates. 256360784Sdimstatic Error executeObjcopy(CopyConfig &Config) { 257344779Sdim sys::fs::file_status Stat; 258353358Sdim if (Config.InputFilename != "-") { 259344779Sdim if (auto EC = sys::fs::status(Config.InputFilename, Stat)) 260353358Sdim return createFileError(Config.InputFilename, EC); 261353358Sdim } else { 262353358Sdim Stat.permissions(static_cast<sys::fs::perms>(0777)); 263353358Sdim } 264341825Sdim 265360784Sdim using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &); 266353358Sdim ProcessRawFn ProcessRaw; 267353358Sdim switch (Config.InputFormat) { 268353358Sdim case FileFormat::Binary: 269353358Sdim ProcessRaw = executeObjcopyOnRawBinary; 270353358Sdim break; 271353358Sdim case FileFormat::IHex: 272353358Sdim ProcessRaw = executeObjcopyOnIHex; 273353358Sdim break; 274353358Sdim default: 275353358Sdim ProcessRaw = nullptr; 276353358Sdim } 277353358Sdim 278353358Sdim if (ProcessRaw) { 279353358Sdim auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); 280344779Sdim if (!BufOrErr) 281353358Sdim return createFileError(Config.InputFilename, BufOrErr.getError()); 282344779Sdim FileBuffer FB(Config.OutputFilename); 283353358Sdim if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) 284353358Sdim return E; 285344779Sdim } else { 286344779Sdim Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = 287344779Sdim createBinary(Config.InputFilename); 288344779Sdim if (!BinaryOrErr) 289353358Sdim return createFileError(Config.InputFilename, BinaryOrErr.takeError()); 290341825Sdim 291344779Sdim if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { 292353358Sdim if (Error E = executeObjcopyOnArchive(Config, *Ar)) 293353358Sdim return E; 294344779Sdim } else { 295344779Sdim FileBuffer FB(Config.OutputFilename); 296353358Sdim if (Error E = executeObjcopyOnBinary(Config, 297353358Sdim *BinaryOrErr.get().getBinary(), FB)) 298353358Sdim return E; 299344779Sdim } 300326938Sdim } 301341825Sdim 302353358Sdim if (Error E = 303353358Sdim restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates)) 304353358Sdim return E; 305353358Sdim 306353358Sdim if (!Config.SplitDWO.empty()) { 307353358Sdim Stat.permissions(static_cast<sys::fs::perms>(0666)); 308353358Sdim if (Error E = 309353358Sdim restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates)) 310353358Sdim return E; 311326938Sdim } 312353358Sdim 313353358Sdim return Error::success(); 314326938Sdim} 315341825Sdim 316360784Sdimnamespace { 317360784Sdim 318360784Sdimenum class ToolType { Objcopy, Strip, InstallNameTool }; 319360784Sdim 320360784Sdim} // anonymous namespace 321360784Sdim 322341825Sdimint main(int argc, char **argv) { 323341825Sdim InitLLVM X(argc, argv); 324341825Sdim ToolName = argv[0]; 325363496Sdim 326363496Sdim StringRef Stem = sys::path::stem(ToolName); 327363496Sdim auto Is = [=](StringRef Tool) { 328363496Sdim // We need to recognize the following filenames: 329363496Sdim // 330363496Sdim // llvm-objcopy -> objcopy 331363496Sdim // strip-10.exe -> strip 332363496Sdim // powerpc64-unknown-freebsd13-objcopy -> objcopy 333363496Sdim // llvm-install-name-tool -> install-name-tool 334363496Sdim auto I = Stem.rfind_lower(Tool); 335363496Sdim return I != StringRef::npos && 336363496Sdim (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); 337363496Sdim }; 338363496Sdim ToolType Tool = ToolType::Objcopy; 339363496Sdim if (Is("strip")) 340363496Sdim Tool = ToolType::Strip; 341363496Sdim else if (Is("install-name-tool") || Is("install_name_tool")) 342363496Sdim Tool = ToolType::InstallNameTool; 343363496Sdim 344360784Sdim // Expand response files. 345360784Sdim // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp, 346360784Sdim // into a separate function in the CommandLine library and call that function 347360784Sdim // here. This is duplicated code. 348360784Sdim SmallVector<const char *, 20> NewArgv(argv, argv + argc); 349360784Sdim BumpPtrAllocator A; 350360784Sdim StringSaver Saver(A); 351360784Sdim cl::ExpandResponseFiles(Saver, 352360784Sdim Triple(sys::getProcessTriple()).isOSWindows() 353360784Sdim ? cl::TokenizeWindowsCommandLine 354360784Sdim : cl::TokenizeGNUCommandLine, 355360784Sdim NewArgv); 356360784Sdim 357360784Sdim auto Args = makeArrayRef(NewArgv).drop_front(); 358353358Sdim Expected<DriverConfig> DriverConfig = 359360784Sdim (Tool == ToolType::Strip) ? parseStripOptions(Args, reportWarning) 360360784Sdim : ((Tool == ToolType::InstallNameTool) 361360784Sdim ? parseInstallNameToolOptions(Args) 362360784Sdim : parseObjcopyOptions(Args, reportWarning)); 363353358Sdim if (!DriverConfig) { 364353358Sdim logAllUnhandledErrors(DriverConfig.takeError(), 365353358Sdim WithColor::error(errs(), ToolName)); 366353358Sdim return 1; 367353358Sdim } 368360784Sdim for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { 369353358Sdim if (Error E = executeObjcopy(CopyConfig)) { 370353358Sdim logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); 371353358Sdim return 1; 372353358Sdim } 373353358Sdim } 374353358Sdim 375353358Sdim return 0; 376341825Sdim} 377