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