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