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 "Buffer.h" 11 #include "CopyConfig.h" 12 #include "ELF/ELFObjcopy.h" 13 #include "COFF/COFFObjcopy.h" 14 #include "MachO/MachOObjcopy.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/Object/MachO.h" 28 #include "llvm/Option/Arg.h" 29 #include "llvm/Option/ArgList.h" 30 #include "llvm/Option/Option.h" 31 #include "llvm/Support/Casting.h" 32 #include "llvm/Support/Error.h" 33 #include "llvm/Support/ErrorHandling.h" 34 #include "llvm/Support/ErrorOr.h" 35 #include "llvm/Support/InitLLVM.h" 36 #include "llvm/Support/Memory.h" 37 #include "llvm/Support/Path.h" 38 #include "llvm/Support/Process.h" 39 #include "llvm/Support/WithColor.h" 40 #include "llvm/Support/raw_ostream.h" 41 #include <algorithm> 42 #include <cassert> 43 #include <cstdlib> 44 #include <memory> 45 #include <string> 46 #include <system_error> 47 #include <utility> 48 49 namespace llvm { 50 namespace objcopy { 51 52 // The name this program was invoked as. 53 StringRef ToolName; 54 55 LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { 56 WithColor::error(errs(), ToolName) << Message << ".\n"; 57 errs().flush(); 58 exit(1); 59 } 60 61 LLVM_ATTRIBUTE_NORETURN void error(Error E) { 62 assert(E); 63 std::string Buf; 64 raw_string_ostream OS(Buf); 65 logAllUnhandledErrors(std::move(E), OS); 66 OS.flush(); 67 WithColor::error(errs(), ToolName) << Buf; 68 exit(1); 69 } 70 71 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { 72 assert(EC); 73 error(createFileError(File, EC)); 74 } 75 76 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { 77 assert(E); 78 std::string Buf; 79 raw_string_ostream OS(Buf); 80 logAllUnhandledErrors(std::move(E), OS); 81 OS.flush(); 82 WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; 83 exit(1); 84 } 85 86 } // end namespace objcopy 87 } // end namespace llvm 88 89 using namespace llvm; 90 using namespace llvm::object; 91 using namespace llvm::objcopy; 92 93 // For regular archives this function simply calls llvm::writeArchive, 94 // For thin archives it writes the archive file itself as well as its members. 95 static Error deepWriteArchive(StringRef ArcName, 96 ArrayRef<NewArchiveMember> NewMembers, 97 bool WriteSymtab, object::Archive::Kind Kind, 98 bool Deterministic, bool Thin) { 99 if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind, 100 Deterministic, Thin)) 101 return createFileError(ArcName, std::move(E)); 102 103 if (!Thin) 104 return Error::success(); 105 106 for (const NewArchiveMember &Member : NewMembers) { 107 // Internally, FileBuffer will use the buffer created by 108 // FileOutputBuffer::create, for regular files (that is the case for 109 // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer. 110 // OnDiskBuffer uses a temporary file and then renames it. So in reality 111 // there is no inefficiency / duplicated in-memory buffers in this case. For 112 // now in-memory buffers can not be completely avoided since 113 // NewArchiveMember still requires them even though writeArchive does not 114 // write them on disk. 115 FileBuffer FB(Member.MemberName); 116 if (Error E = FB.allocate(Member.Buf->getBufferSize())) 117 return E; 118 std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(), 119 FB.getBufferStart()); 120 if (Error E = FB.commit()) 121 return E; 122 } 123 return Error::success(); 124 } 125 126 /// The function executeObjcopyOnIHex does the dispatch based on the format 127 /// of the output specified by the command line options. 128 static Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, 129 Buffer &Out) { 130 // TODO: support output formats other than ELF. 131 return elf::executeObjcopyOnIHex(Config, In, Out); 132 } 133 134 /// The function executeObjcopyOnRawBinary does the dispatch based on the format 135 /// of the output specified by the command line options. 136 static Error executeObjcopyOnRawBinary(const CopyConfig &Config, 137 MemoryBuffer &In, Buffer &Out) { 138 // TODO: llvm-objcopy should parse CopyConfig.OutputFormat to recognize 139 // formats other than ELF / "binary" and invoke 140 // elf::executeObjcopyOnRawBinary, macho::executeObjcopyOnRawBinary or 141 // coff::executeObjcopyOnRawBinary accordingly. 142 return elf::executeObjcopyOnRawBinary(Config, In, Out); 143 } 144 145 /// The function executeObjcopyOnBinary does the dispatch based on the format 146 /// of the input binary (ELF, MachO or COFF). 147 static Error executeObjcopyOnBinary(const CopyConfig &Config, 148 object::Binary &In, Buffer &Out) { 149 if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) 150 return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); 151 else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) 152 return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); 153 else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) 154 return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); 155 else 156 return createStringError(object_error::invalid_file_type, 157 "Unsupported object file format"); 158 } 159 160 static Error executeObjcopyOnArchive(const CopyConfig &Config, 161 const Archive &Ar) { 162 std::vector<NewArchiveMember> NewArchiveMembers; 163 Error Err = Error::success(); 164 for (const Archive::Child &Child : Ar.children(Err)) { 165 Expected<StringRef> ChildNameOrErr = Child.getName(); 166 if (!ChildNameOrErr) 167 return createFileError(Ar.getFileName(), ChildNameOrErr.takeError()); 168 169 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); 170 if (!ChildOrErr) 171 return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", 172 ChildOrErr.takeError()); 173 174 MemBuffer MB(ChildNameOrErr.get()); 175 if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) 176 return E; 177 178 Expected<NewArchiveMember> Member = 179 NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); 180 if (!Member) 181 return createFileError(Ar.getFileName(), Member.takeError()); 182 Member->Buf = MB.releaseMemoryBuffer(); 183 Member->MemberName = Member->Buf->getBufferIdentifier(); 184 NewArchiveMembers.push_back(std::move(*Member)); 185 } 186 if (Err) 187 return createFileError(Config.InputFilename, std::move(Err)); 188 189 return deepWriteArchive(Config.OutputFilename, NewArchiveMembers, 190 Ar.hasSymbolTable(), Ar.kind(), 191 Config.DeterministicArchives, Ar.isThin()); 192 } 193 194 static Error restoreDateOnFile(StringRef Filename, 195 const sys::fs::file_status &Stat) { 196 int FD; 197 198 if (auto EC = 199 sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) 200 return createFileError(Filename, EC); 201 202 if (auto EC = sys::fs::setLastAccessAndModificationTime( 203 FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) 204 return createFileError(Filename, EC); 205 206 if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) 207 return createFileError(Filename, EC); 208 209 return Error::success(); 210 } 211 212 /// The function executeObjcopy does the higher level dispatch based on the type 213 /// of input (raw binary, archive or single object file) and takes care of the 214 /// format-agnostic modifications, i.e. preserving dates. 215 static Error executeObjcopy(const CopyConfig &Config) { 216 sys::fs::file_status Stat; 217 if (Config.PreserveDates) 218 if (auto EC = sys::fs::status(Config.InputFilename, Stat)) 219 return createFileError(Config.InputFilename, EC); 220 221 typedef Error (*ProcessRawFn)(const CopyConfig &, MemoryBuffer &, Buffer &); 222 auto ProcessRaw = StringSwitch<ProcessRawFn>(Config.InputFormat) 223 .Case("binary", executeObjcopyOnRawBinary) 224 .Case("ihex", executeObjcopyOnIHex) 225 .Default(nullptr); 226 227 if (ProcessRaw) { 228 auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); 229 if (!BufOrErr) 230 return createFileError(Config.InputFilename, BufOrErr.getError()); 231 FileBuffer FB(Config.OutputFilename); 232 if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) 233 return E; 234 } else { 235 Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = 236 createBinary(Config.InputFilename); 237 if (!BinaryOrErr) 238 return createFileError(Config.InputFilename, BinaryOrErr.takeError()); 239 240 if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { 241 if (Error E = executeObjcopyOnArchive(Config, *Ar)) 242 return E; 243 } else { 244 FileBuffer FB(Config.OutputFilename); 245 if (Error E = executeObjcopyOnBinary(Config, 246 *BinaryOrErr.get().getBinary(), FB)) 247 return E; 248 } 249 } 250 251 if (Config.PreserveDates) { 252 if (Error E = restoreDateOnFile(Config.OutputFilename, Stat)) 253 return E; 254 if (!Config.SplitDWO.empty()) 255 if (Error E = restoreDateOnFile(Config.SplitDWO, Stat)) 256 return E; 257 } 258 259 return Error::success(); 260 } 261 262 int main(int argc, char **argv) { 263 InitLLVM X(argc, argv); 264 ToolName = argv[0]; 265 bool IsStrip = sys::path::stem(ToolName).contains("strip"); 266 Expected<DriverConfig> DriverConfig = 267 IsStrip ? parseStripOptions(makeArrayRef(argv + 1, argc)) 268 : parseObjcopyOptions(makeArrayRef(argv + 1, argc)); 269 if (!DriverConfig) { 270 logAllUnhandledErrors(DriverConfig.takeError(), 271 WithColor::error(errs(), ToolName)); 272 return 1; 273 } 274 for (const CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { 275 if (Error E = executeObjcopy(CopyConfig)) { 276 logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); 277 return 1; 278 } 279 } 280 281 return 0; 282 } 283