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/CommandLine.h" 33 #include "llvm/Support/Error.h" 34 #include "llvm/Support/ErrorHandling.h" 35 #include "llvm/Support/ErrorOr.h" 36 #include "llvm/Support/InitLLVM.h" 37 #include "llvm/Support/Memory.h" 38 #include "llvm/Support/Path.h" 39 #include "llvm/Support/Process.h" 40 #include "llvm/Support/StringSaver.h" 41 #include "llvm/Support/WithColor.h" 42 #include "llvm/Support/raw_ostream.h" 43 #include <algorithm> 44 #include <cassert> 45 #include <cstdlib> 46 #include <memory> 47 #include <string> 48 #include <system_error> 49 #include <utility> 50 51 namespace llvm { 52 namespace objcopy { 53 54 // The name this program was invoked as. 55 StringRef ToolName; 56 57 LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { 58 WithColor::error(errs(), ToolName) << Message << "\n"; 59 exit(1); 60 } 61 62 LLVM_ATTRIBUTE_NORETURN void error(Error E) { 63 assert(E); 64 std::string Buf; 65 raw_string_ostream OS(Buf); 66 logAllUnhandledErrors(std::move(E), OS); 67 OS.flush(); 68 WithColor::error(errs(), ToolName) << Buf; 69 exit(1); 70 } 71 72 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { 73 assert(EC); 74 error(createFileError(File, EC)); 75 } 76 77 LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { 78 assert(E); 79 std::string Buf; 80 raw_string_ostream OS(Buf); 81 logAllUnhandledErrors(std::move(E), OS); 82 OS.flush(); 83 WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; 84 exit(1); 85 } 86 87 ErrorSuccess reportWarning(Error E) { 88 assert(E); 89 WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n'; 90 return Error::success(); 91 } 92 93 } // end namespace objcopy 94 } // end namespace llvm 95 96 using namespace llvm; 97 using namespace llvm::object; 98 using namespace llvm::objcopy; 99 100 // For regular archives this function simply calls llvm::writeArchive, 101 // For thin archives it writes the archive file itself as well as its members. 102 static Error deepWriteArchive(StringRef ArcName, 103 ArrayRef<NewArchiveMember> NewMembers, 104 bool WriteSymtab, object::Archive::Kind Kind, 105 bool Deterministic, bool Thin) { 106 if (Error E = writeArchive(ArcName, NewMembers, WriteSymtab, Kind, 107 Deterministic, Thin)) 108 return createFileError(ArcName, std::move(E)); 109 110 if (!Thin) 111 return Error::success(); 112 113 for (const NewArchiveMember &Member : NewMembers) { 114 // Internally, FileBuffer will use the buffer created by 115 // FileOutputBuffer::create, for regular files (that is the case for 116 // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer. 117 // OnDiskBuffer uses a temporary file and then renames it. So in reality 118 // there is no inefficiency / duplicated in-memory buffers in this case. For 119 // now in-memory buffers can not be completely avoided since 120 // NewArchiveMember still requires them even though writeArchive does not 121 // write them on disk. 122 FileBuffer FB(Member.MemberName); 123 if (Error E = FB.allocate(Member.Buf->getBufferSize())) 124 return E; 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(CopyConfig &Config, MemoryBuffer &In, 136 Buffer &Out) { 137 // TODO: support output formats other than ELF. 138 if (Error E = Config.parseELFConfig()) 139 return E; 140 return elf::executeObjcopyOnIHex(Config, In, Out); 141 } 142 143 /// The function executeObjcopyOnRawBinary does the dispatch based on the format 144 /// of the output specified by the command line options. 145 static Error executeObjcopyOnRawBinary(CopyConfig &Config, MemoryBuffer &In, 146 Buffer &Out) { 147 switch (Config.OutputFormat) { 148 case FileFormat::ELF: 149 // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the 150 // output format is binary/ihex or it's not given. This behavior differs from 151 // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details. 152 case FileFormat::Binary: 153 case FileFormat::IHex: 154 case FileFormat::Unspecified: 155 if (Error E = Config.parseELFConfig()) 156 return E; 157 return elf::executeObjcopyOnRawBinary(Config, In, Out); 158 } 159 160 llvm_unreachable("unsupported output format"); 161 } 162 163 /// The function executeObjcopyOnBinary does the dispatch based on the format 164 /// of the input binary (ELF, MachO or COFF). 165 static Error executeObjcopyOnBinary(CopyConfig &Config, object::Binary &In, 166 Buffer &Out) { 167 if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) { 168 if (Error E = Config.parseELFConfig()) 169 return E; 170 return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); 171 } else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) 172 return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); 173 else if (auto *MachOBinary = dyn_cast<object::MachOObjectFile>(&In)) 174 return macho::executeObjcopyOnBinary(Config, *MachOBinary, Out); 175 else 176 return createStringError(object_error::invalid_file_type, 177 "unsupported object file format"); 178 } 179 180 static Error executeObjcopyOnArchive(CopyConfig &Config, const Archive &Ar) { 181 std::vector<NewArchiveMember> NewArchiveMembers; 182 Error Err = Error::success(); 183 for (const Archive::Child &Child : Ar.children(Err)) { 184 Expected<StringRef> ChildNameOrErr = Child.getName(); 185 if (!ChildNameOrErr) 186 return createFileError(Ar.getFileName(), ChildNameOrErr.takeError()); 187 188 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); 189 if (!ChildOrErr) 190 return createFileError(Ar.getFileName() + "(" + *ChildNameOrErr + ")", 191 ChildOrErr.takeError()); 192 193 MemBuffer MB(ChildNameOrErr.get()); 194 if (Error E = executeObjcopyOnBinary(Config, *ChildOrErr->get(), MB)) 195 return E; 196 197 Expected<NewArchiveMember> Member = 198 NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); 199 if (!Member) 200 return createFileError(Ar.getFileName(), Member.takeError()); 201 Member->Buf = MB.releaseMemoryBuffer(); 202 Member->MemberName = Member->Buf->getBufferIdentifier(); 203 NewArchiveMembers.push_back(std::move(*Member)); 204 } 205 if (Err) 206 return createFileError(Config.InputFilename, std::move(Err)); 207 208 return deepWriteArchive(Config.OutputFilename, NewArchiveMembers, 209 Ar.hasSymbolTable(), Ar.kind(), 210 Config.DeterministicArchives, Ar.isThin()); 211 } 212 213 static Error restoreStatOnFile(StringRef Filename, 214 const sys::fs::file_status &Stat, 215 bool PreserveDates) { 216 int FD; 217 218 // Writing to stdout should not be treated as an error here, just 219 // do not set access/modification times or permissions. 220 if (Filename == "-") 221 return Error::success(); 222 223 if (auto EC = 224 sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) 225 return createFileError(Filename, EC); 226 227 if (PreserveDates) 228 if (auto EC = sys::fs::setLastAccessAndModificationTime( 229 FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) 230 return createFileError(Filename, EC); 231 232 sys::fs::file_status OStat; 233 if (std::error_code EC = sys::fs::status(FD, OStat)) 234 return createFileError(Filename, EC); 235 if (OStat.type() == sys::fs::file_type::regular_file) 236 #ifdef _WIN32 237 if (auto EC = sys::fs::setPermissions( 238 Filename, static_cast<sys::fs::perms>(Stat.permissions() & 239 ~sys::fs::getUmask()))) 240 #else 241 if (auto EC = sys::fs::setPermissions( 242 FD, static_cast<sys::fs::perms>(Stat.permissions() & 243 ~sys::fs::getUmask()))) 244 #endif 245 return createFileError(Filename, EC); 246 247 if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) 248 return createFileError(Filename, EC); 249 250 return Error::success(); 251 } 252 253 /// The function executeObjcopy does the higher level dispatch based on the type 254 /// of input (raw binary, archive or single object file) and takes care of the 255 /// format-agnostic modifications, i.e. preserving dates. 256 static Error executeObjcopy(CopyConfig &Config) { 257 sys::fs::file_status Stat; 258 if (Config.InputFilename != "-") { 259 if (auto EC = sys::fs::status(Config.InputFilename, Stat)) 260 return createFileError(Config.InputFilename, EC); 261 } else { 262 Stat.permissions(static_cast<sys::fs::perms>(0777)); 263 } 264 265 using ProcessRawFn = Error (*)(CopyConfig &, MemoryBuffer &, Buffer &); 266 ProcessRawFn ProcessRaw; 267 switch (Config.InputFormat) { 268 case FileFormat::Binary: 269 ProcessRaw = executeObjcopyOnRawBinary; 270 break; 271 case FileFormat::IHex: 272 ProcessRaw = executeObjcopyOnIHex; 273 break; 274 default: 275 ProcessRaw = nullptr; 276 } 277 278 if (ProcessRaw) { 279 auto BufOrErr = MemoryBuffer::getFileOrSTDIN(Config.InputFilename); 280 if (!BufOrErr) 281 return createFileError(Config.InputFilename, BufOrErr.getError()); 282 FileBuffer FB(Config.OutputFilename); 283 if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) 284 return E; 285 } else { 286 Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = 287 createBinary(Config.InputFilename); 288 if (!BinaryOrErr) 289 return createFileError(Config.InputFilename, BinaryOrErr.takeError()); 290 291 if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { 292 if (Error E = executeObjcopyOnArchive(Config, *Ar)) 293 return E; 294 } else { 295 FileBuffer FB(Config.OutputFilename); 296 if (Error E = executeObjcopyOnBinary(Config, 297 *BinaryOrErr.get().getBinary(), FB)) 298 return E; 299 } 300 } 301 302 if (Error E = 303 restoreStatOnFile(Config.OutputFilename, Stat, Config.PreserveDates)) 304 return E; 305 306 if (!Config.SplitDWO.empty()) { 307 Stat.permissions(static_cast<sys::fs::perms>(0666)); 308 if (Error E = 309 restoreStatOnFile(Config.SplitDWO, Stat, Config.PreserveDates)) 310 return E; 311 } 312 313 return Error::success(); 314 } 315 316 int main(int argc, char **argv) { 317 InitLLVM X(argc, argv); 318 ToolName = argv[0]; 319 bool IsStrip = sys::path::stem(ToolName).contains("strip"); 320 321 // Expand response files. 322 // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp, 323 // into a separate function in the CommandLine library and call that function 324 // here. This is duplicated code. 325 SmallVector<const char *, 20> NewArgv(argv, argv + argc); 326 BumpPtrAllocator A; 327 StringSaver Saver(A); 328 cl::ExpandResponseFiles(Saver, 329 Triple(sys::getProcessTriple()).isOSWindows() 330 ? cl::TokenizeWindowsCommandLine 331 : cl::TokenizeGNUCommandLine, 332 NewArgv); 333 334 auto Args = makeArrayRef(NewArgv).drop_front(); 335 336 Expected<DriverConfig> DriverConfig = 337 IsStrip ? parseStripOptions(Args, reportWarning) 338 : parseObjcopyOptions(Args, reportWarning); 339 if (!DriverConfig) { 340 logAllUnhandledErrors(DriverConfig.takeError(), 341 WithColor::error(errs(), ToolName)); 342 return 1; 343 } 344 for (CopyConfig &CopyConfig : DriverConfig->CopyConfigs) { 345 if (Error E = executeObjcopy(CopyConfig)) { 346 logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); 347 return 1; 348 } 349 } 350 351 return 0; 352 } 353