blob: fb1ff18b015b153e8c9c72f5b898654e4edcf118 [file] [log] [blame]
Eugene Zelenko66f724e2017-11-01 21:16:06 +00001//===- llvm-objcopy.cpp ---------------------------------------------------===//
Petr Hoseke5551a72017-08-01 00:33:58 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
Eugene Zelenko66f724e2017-11-01 21:16:06 +00009
Petr Hoseke5551a72017-08-01 00:33:58 +000010#include "llvm-objcopy.h"
Alexander Shaposhnikovd0437912018-10-16 05:40:18 +000011#include "Buffer.h"
Martin Storsjoe318fd62018-12-19 07:24:38 +000012#include "COFF/COFFObjcopy.h"
Alexander Shaposhnikovbd934ff2018-10-11 22:33:50 +000013#include "CopyConfig.h"
Alexander Shaposhnikov245f8d72018-10-29 21:22:58 +000014#include "ELF/ELFObjcopy.h"
Alexander Shaposhnikovbd934ff2018-10-11 22:33:50 +000015
Eugene Zelenko66f724e2017-11-01 21:16:06 +000016#include "llvm/ADT/STLExtras.h"
Jordan Rupprecht742c6792018-08-01 16:23:22 +000017#include "llvm/ADT/SmallVector.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000018#include "llvm/ADT/StringRef.h"
19#include "llvm/ADT/Twine.h"
Alexander Shaposhnikov80e7ea02018-07-06 17:51:03 +000020#include "llvm/Object/Archive.h"
21#include "llvm/Object/ArchiveWriter.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000022#include "llvm/Object/Binary.h"
Martin Storsjoe318fd62018-12-19 07:24:38 +000023#include "llvm/Object/COFF.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000024#include "llvm/Object/ELFObjectFile.h"
25#include "llvm/Object/ELFTypes.h"
26#include "llvm/Object/Error.h"
Alexander Shaposhnikovae1ca022018-04-24 05:43:32 +000027#include "llvm/Option/Arg.h"
28#include "llvm/Option/ArgList.h"
29#include "llvm/Option/Option.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000030#include "llvm/Support/Casting.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000031#include "llvm/Support/Error.h"
32#include "llvm/Support/ErrorHandling.h"
33#include "llvm/Support/ErrorOr.h"
Rui Ueyama0b9d56a2018-04-13 18:26:06 +000034#include "llvm/Support/InitLLVM.h"
Jordan Rupprechtcd514b62018-08-17 18:51:11 +000035#include "llvm/Support/Memory.h"
Alexander Shaposhnikov409ea282018-05-07 19:32:09 +000036#include "llvm/Support/Path.h"
Jordan Rupprecht7b455c42018-08-16 18:29:40 +000037#include "llvm/Support/Process.h"
Jordan Rupprecht024925d2018-08-09 22:52:03 +000038#include "llvm/Support/WithColor.h"
Eugene Zelenko66f724e2017-11-01 21:16:06 +000039#include "llvm/Support/raw_ostream.h"
40#include <algorithm>
41#include <cassert>
42#include <cstdlib>
Petr Hoseke5551a72017-08-01 00:33:58 +000043#include <memory>
44#include <string>
45#include <system_error>
Eugene Zelenko66f724e2017-11-01 21:16:06 +000046#include <utility>
Petr Hoseke5551a72017-08-01 00:33:58 +000047
Puyan Lotfib5b60c32018-07-18 00:10:51 +000048namespace llvm {
49namespace objcopy {
50
51// The name this program was invoked as.
52StringRef ToolName;
53
54LLVM_ATTRIBUTE_NORETURN void error(Twine Message) {
Jordan Rupprecht024925d2018-08-09 22:52:03 +000055 WithColor::error(errs(), ToolName) << Message << ".\n";
Puyan Lotfib5b60c32018-07-18 00:10:51 +000056 errs().flush();
57 exit(1);
58}
59
60LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) {
61 assert(EC);
Jordan Rupprecht024925d2018-08-09 22:52:03 +000062 WithColor::error(errs(), ToolName)
63 << "'" << File << "': " << EC.message() << ".\n";
Puyan Lotfib5b60c32018-07-18 00:10:51 +000064 exit(1);
65}
66
67LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) {
68 assert(E);
69 std::string Buf;
70 raw_string_ostream OS(Buf);
Jonas Devlieghere686dfe32018-11-11 01:46:03 +000071 logAllUnhandledErrors(std::move(E), OS);
Puyan Lotfib5b60c32018-07-18 00:10:51 +000072 OS.flush();
Jordan Rupprecht024925d2018-08-09 22:52:03 +000073 WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf;
Puyan Lotfib5b60c32018-07-18 00:10:51 +000074 exit(1);
75}
76
77} // end namespace objcopy
Puyan Lotfi41306ba2018-07-16 22:17:05 +000078} // end namespace llvm
Jake Ehrlicha8631b82017-11-03 18:58:41 +000079
Alexander Shaposhnikovbcfd1f32018-10-24 22:49:06 +000080using namespace llvm;
81using namespace llvm::object;
82using namespace llvm::objcopy;
83
Alexander Shaposhnikov80e7ea02018-07-06 17:51:03 +000084// For regular archives this function simply calls llvm::writeArchive,
85// For thin archives it writes the archive file itself as well as its members.
Puyan Lotfi41306ba2018-07-16 22:17:05 +000086static Error deepWriteArchive(StringRef ArcName,
87 ArrayRef<NewArchiveMember> NewMembers,
88 bool WriteSymtab, object::Archive::Kind Kind,
89 bool Deterministic, bool Thin) {
Alexander Shaposhnikov80e7ea02018-07-06 17:51:03 +000090 Error E =
91 writeArchive(ArcName, NewMembers, WriteSymtab, Kind, Deterministic, Thin);
92 if (!Thin || E)
93 return E;
94 for (const NewArchiveMember &Member : NewMembers) {
95 // Internally, FileBuffer will use the buffer created by
96 // FileOutputBuffer::create, for regular files (that is the case for
97 // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer.
98 // OnDiskBuffer uses a temporary file and then renames it. So in reality
99 // there is no inefficiency / duplicated in-memory buffers in this case. For
100 // now in-memory buffers can not be completely avoided since
101 // NewArchiveMember still requires them even though writeArchive does not
102 // write them on disk.
103 FileBuffer FB(Member.MemberName);
104 FB.allocate(Member.Buf->getBufferSize());
105 std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(),
106 FB.getBufferStart());
107 if (auto E = FB.commit())
108 return E;
109 }
110 return Error::success();
111}
112
Alexander Shaposhnikovbcfd1f32018-10-24 22:49:06 +0000113/// The function executeObjcopyOnRawBinary does the dispatch based on the format
114/// of the output specified by the command line options.
115static void executeObjcopyOnRawBinary(const CopyConfig &Config,
116 MemoryBuffer &In, Buffer &Out) {
117 // TODO: llvm-objcopy should parse CopyConfig.OutputFormat to recognize
118 // formats other than ELF / "binary" and invoke
119 // elf::executeObjcopyOnRawBinary, macho::executeObjcopyOnRawBinary or
120 // coff::executeObjcopyOnRawBinary accordingly.
121 return elf::executeObjcopyOnRawBinary(Config, In, Out);
122}
123
124/// The function executeObjcopyOnBinary does the dispatch based on the format
125/// of the input binary (ELF, MachO or COFF).
126static void executeObjcopyOnBinary(const CopyConfig &Config, object::Binary &In,
127 Buffer &Out) {
128 if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In))
129 return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out);
Martin Storsjoe318fd62018-12-19 07:24:38 +0000130 else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In))
131 return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out);
Alexander Shaposhnikovbcfd1f32018-10-24 22:49:06 +0000132 else
133 error("Unsupported object file format");
134}
135
136static void executeObjcopyOnArchive(const CopyConfig &Config,
137 const Archive &Ar) {
Alexander Shaposhnikov80e7ea02018-07-06 17:51:03 +0000138 std::vector<NewArchiveMember> NewArchiveMembers;
139 Error Err = Error::success();
140 for (const Archive::Child &Child : Ar.children(Err)) {
141 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
142 if (!ChildOrErr)
143 reportError(Ar.getFileName(), ChildOrErr.takeError());
Jordan Rupprechtcd514b62018-08-17 18:51:11 +0000144 Binary *Bin = ChildOrErr->get();
145
Alexander Shaposhnikov80e7ea02018-07-06 17:51:03 +0000146 Expected<StringRef> ChildNameOrErr = Child.getName();
147 if (!ChildNameOrErr)
148 reportError(Ar.getFileName(), ChildNameOrErr.takeError());
149
150 MemBuffer MB(ChildNameOrErr.get());
Alexander Shaposhnikovbcfd1f32018-10-24 22:49:06 +0000151 executeObjcopyOnBinary(Config, *Bin, MB);
Alexander Shaposhnikov80e7ea02018-07-06 17:51:03 +0000152
153 Expected<NewArchiveMember> Member =
Jordan Rupprecht167fb182018-11-01 17:36:37 +0000154 NewArchiveMember::getOldMember(Child, Config.DeterministicArchives);
Alexander Shaposhnikov80e7ea02018-07-06 17:51:03 +0000155 if (!Member)
156 reportError(Ar.getFileName(), Member.takeError());
157 Member->Buf = MB.releaseMemoryBuffer();
158 Member->MemberName = Member->Buf->getBufferIdentifier();
159 NewArchiveMembers.push_back(std::move(*Member));
160 }
161
162 if (Err)
163 reportError(Config.InputFilename, std::move(Err));
Jordan Rupprecht167fb182018-11-01 17:36:37 +0000164 if (Error E = deepWriteArchive(Config.OutputFilename, NewArchiveMembers,
165 Ar.hasSymbolTable(), Ar.kind(),
166 Config.DeterministicArchives, Ar.isThin()))
Alexander Shaposhnikov80e7ea02018-07-06 17:51:03 +0000167 reportError(Config.OutputFilename, std::move(E));
Petr Hoseke5551a72017-08-01 00:33:58 +0000168}
169
Jordan Rupprecht7b455c42018-08-16 18:29:40 +0000170static void restoreDateOnFile(StringRef Filename,
171 const sys::fs::file_status &Stat) {
172 int FD;
173
Jordan Rupprecht72a8d842018-08-29 23:21:56 +0000174 if (auto EC =
175 sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting))
Jordan Rupprecht7b455c42018-08-16 18:29:40 +0000176 reportError(Filename, EC);
177
178 if (auto EC = sys::fs::setLastAccessAndModificationTime(
179 FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime()))
180 reportError(Filename, EC);
181
182 if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
183 reportError(Filename, EC);
184}
185
Alexander Shaposhnikovbcfd1f32018-10-24 22:49:06 +0000186/// The function executeObjcopy does the higher level dispatch based on the type
187/// of input (raw binary, archive or single object file) and takes care of the
188/// format-agnostic modifications, i.e. preserving dates.
189static void executeObjcopy(const CopyConfig &Config) {
Jordan Rupprecht7b455c42018-08-16 18:29:40 +0000190 sys::fs::file_status Stat;
191 if (Config.PreserveDates)
192 if (auto EC = sys::fs::status(Config.InputFilename, Stat))
193 reportError(Config.InputFilename, EC);
194
Jordan Rupprechtcd514b62018-08-17 18:51:11 +0000195 if (Config.InputFormat == "binary") {
196 auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename);
197 if (!BufOrErr)
198 reportError(Config.InputFilename, BufOrErr.getError());
Jordan Rupprecht7b455c42018-08-16 18:29:40 +0000199 FileBuffer FB(Config.OutputFilename);
Alexander Shaposhnikovbcfd1f32018-10-24 22:49:06 +0000200 executeObjcopyOnRawBinary(Config, *BufOrErr->get(), FB);
Jordan Rupprechtcd514b62018-08-17 18:51:11 +0000201 } else {
202 Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
203 createBinary(Config.InputFilename);
204 if (!BinaryOrErr)
205 reportError(Config.InputFilename, BinaryOrErr.takeError());
206
207 if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) {
Alexander Shaposhnikovbcfd1f32018-10-24 22:49:06 +0000208 executeObjcopyOnArchive(Config, *Ar);
Jordan Rupprechtcd514b62018-08-17 18:51:11 +0000209 } else {
210 FileBuffer FB(Config.OutputFilename);
Alexander Shaposhnikovbcfd1f32018-10-24 22:49:06 +0000211 executeObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB);
Jordan Rupprechtcd514b62018-08-17 18:51:11 +0000212 }
Jordan Rupprecht7b455c42018-08-16 18:29:40 +0000213 }
Alexander Shaposhnikov80e7ea02018-07-06 17:51:03 +0000214
Jordan Rupprecht7b455c42018-08-16 18:29:40 +0000215 if (Config.PreserveDates) {
216 restoreDateOnFile(Config.OutputFilename, Stat);
217 if (!Config.SplitDWO.empty())
218 restoreDateOnFile(Config.SplitDWO, Stat);
219 }
Petr Hoseke5551a72017-08-01 00:33:58 +0000220}
Alexander Shaposhnikovae1ca022018-04-24 05:43:32 +0000221
Alexander Shaposhnikovae1ca022018-04-24 05:43:32 +0000222int main(int argc, char **argv) {
223 InitLLVM X(argc, argv);
224 ToolName = argv[0];
Jordan Rupprecht4906f3f2018-09-05 13:10:03 +0000225 DriverConfig DriverConfig;
Fangrui Song63cd3e52018-11-07 03:02:11 +0000226 if (sys::path::stem(ToolName).contains("strip"))
Jordan Rupprecht4906f3f2018-09-05 13:10:03 +0000227 DriverConfig = parseStripOptions(makeArrayRef(argv + 1, argc));
Alexander Shaposhnikov409ea282018-05-07 19:32:09 +0000228 else
Jordan Rupprecht4906f3f2018-09-05 13:10:03 +0000229 DriverConfig = parseObjcopyOptions(makeArrayRef(argv + 1, argc));
230 for (const CopyConfig &CopyConfig : DriverConfig.CopyConfigs)
Alexander Shaposhnikovbcfd1f32018-10-24 22:49:06 +0000231 executeObjcopy(CopyConfig);
Alexander Shaposhnikovae1ca022018-04-24 05:43:32 +0000232}