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