llvm-objcopy.cpp revision 344779
1//===- llvm-objcopy.cpp ---------------------------------------------------===// 2// 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//===----------------------------------------------------------------------===// 9 10#include "llvm-objcopy.h" 11#include "Buffer.h" 12#include "COFF/COFFObjcopy.h" 13#include "CopyConfig.h" 14#include "ELF/ELFObjcopy.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/Object/Archive.h" 21#include "llvm/Object/ArchiveWriter.h" 22#include "llvm/Object/Binary.h" 23#include "llvm/Object/COFF.h" 24#include "llvm/Object/ELFObjectFile.h" 25#include "llvm/Object/ELFTypes.h" 26#include "llvm/Object/Error.h" 27#include "llvm/Option/Arg.h" 28#include "llvm/Option/ArgList.h" 29#include "llvm/Option/Option.h" 30#include "llvm/Support/Casting.h" 31#include "llvm/Support/Error.h" 32#include "llvm/Support/ErrorHandling.h" 33#include "llvm/Support/ErrorOr.h" 34#include "llvm/Support/InitLLVM.h" 35#include "llvm/Support/Memory.h" 36#include "llvm/Support/Path.h" 37#include "llvm/Support/Process.h" 38#include "llvm/Support/WithColor.h" 39#include "llvm/Support/raw_ostream.h" 40#include <algorithm> 41#include <cassert> 42#include <cstdlib> 43#include <memory> 44#include <string> 45#include <system_error> 46#include <utility> 47 48namespace llvm { 49namespace objcopy { 50 51// The name this program was invoked as. 52StringRef ToolName; 53 54LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { 55 WithColor::error(errs(), ToolName) << Message << ".\n"; 56 errs().flush(); 57 exit(1); 58} 59 60LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { 61 assert(EC); 62 WithColor::error(errs(), ToolName) 63 << "'" << File << "': " << EC.message() << ".\n"; 64 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); 71 logAllUnhandledErrors(std::move(E), OS); 72 OS.flush(); 73 WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; 74 exit(1); 75} 76 77} // end namespace objcopy 78} // end namespace llvm 79 80using namespace llvm; 81using namespace llvm::object; 82using namespace llvm::objcopy; 83 84// For regular archives this function simply calls llvm::writeArchive, 85// For thin archives it writes the archive file itself as well as its members. 86static Error deepWriteArchive(StringRef ArcName, 87 ArrayRef<NewArchiveMember> NewMembers, 88 bool WriteSymtab, object::Archive::Kind Kind, 89 bool Deterministic, bool Thin) { 90 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 113/// 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); 130 else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) 131 return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); 132 else 133 error("Unsupported object file format"); 134} 135 136static void executeObjcopyOnArchive(const CopyConfig &Config, 137 const Archive &Ar) { 138 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()); 144 Binary *Bin = ChildOrErr->get(); 145 146 Expected<StringRef> ChildNameOrErr = Child.getName(); 147 if (!ChildNameOrErr) 148 reportError(Ar.getFileName(), ChildNameOrErr.takeError()); 149 150 MemBuffer MB(ChildNameOrErr.get()); 151 executeObjcopyOnBinary(Config, *Bin, MB); 152 153 Expected<NewArchiveMember> Member = 154 NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); 155 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)); 164 if (Error E = deepWriteArchive(Config.OutputFilename, NewArchiveMembers, 165 Ar.hasSymbolTable(), Ar.kind(), 166 Config.DeterministicArchives, Ar.isThin())) 167 reportError(Config.OutputFilename, std::move(E)); 168} 169 170static void restoreDateOnFile(StringRef Filename, 171 const sys::fs::file_status &Stat) { 172 int FD; 173 174 if (auto EC = 175 sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) 176 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 186/// 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) { 190 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 195 if (Config.InputFormat == "binary") { 196 auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename); 197 if (!BufOrErr) 198 reportError(Config.InputFilename, BufOrErr.getError()); 199 FileBuffer FB(Config.OutputFilename); 200 executeObjcopyOnRawBinary(Config, *BufOrErr->get(), FB); 201 } 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())) { 208 executeObjcopyOnArchive(Config, *Ar); 209 } else { 210 FileBuffer FB(Config.OutputFilename); 211 executeObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB); 212 } 213 } 214 215 if (Config.PreserveDates) { 216 restoreDateOnFile(Config.OutputFilename, Stat); 217 if (!Config.SplitDWO.empty()) 218 restoreDateOnFile(Config.SplitDWO, Stat); 219 } 220} 221 222int main(int argc, char **argv) { 223 InitLLVM X(argc, argv); 224 ToolName = argv[0]; 225 DriverConfig DriverConfig; 226 if (sys::path::stem(ToolName).contains("strip")) 227 DriverConfig = parseStripOptions(makeArrayRef(argv + 1, argc)); 228 else 229 DriverConfig = parseObjcopyOptions(makeArrayRef(argv + 1, argc)); 230 for (const CopyConfig &CopyConfig : DriverConfig.CopyConfigs) 231 executeObjcopy(CopyConfig); 232} 233