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