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