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