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