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