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 "ObjcopyOptions.h" 10 #include "llvm/ADT/STLExtras.h" 11 #include "llvm/ADT/SmallVector.h" 12 #include "llvm/ADT/StringRef.h" 13 #include "llvm/ADT/Twine.h" 14 #include "llvm/BinaryFormat/ELF.h" 15 #include "llvm/ObjCopy/COFF/COFFConfig.h" 16 #include "llvm/ObjCopy/COFF/COFFObjcopy.h" 17 #include "llvm/ObjCopy/CommonConfig.h" 18 #include "llvm/ObjCopy/ELF/ELFConfig.h" 19 #include "llvm/ObjCopy/ELF/ELFObjcopy.h" 20 #include "llvm/ObjCopy/MachO/MachOConfig.h" 21 #include "llvm/ObjCopy/MachO/MachOObjcopy.h" 22 #include "llvm/ObjCopy/ObjCopy.h" 23 #include "llvm/ObjCopy/wasm/WasmConfig.h" 24 #include "llvm/ObjCopy/wasm/WasmObjcopy.h" 25 #include "llvm/Object/Archive.h" 26 #include "llvm/Object/ArchiveWriter.h" 27 #include "llvm/Object/Binary.h" 28 #include "llvm/Object/COFF.h" 29 #include "llvm/Object/ELFObjectFile.h" 30 #include "llvm/Object/ELFTypes.h" 31 #include "llvm/Object/Error.h" 32 #include "llvm/Object/MachO.h" 33 #include "llvm/Object/MachOUniversal.h" 34 #include "llvm/Object/Wasm.h" 35 #include "llvm/Option/Arg.h" 36 #include "llvm/Option/ArgList.h" 37 #include "llvm/Option/Option.h" 38 #include "llvm/Support/Casting.h" 39 #include "llvm/Support/CommandLine.h" 40 #include "llvm/Support/Errc.h" 41 #include "llvm/Support/Error.h" 42 #include "llvm/Support/ErrorHandling.h" 43 #include "llvm/Support/ErrorOr.h" 44 #include "llvm/Support/Host.h" 45 #include "llvm/Support/InitLLVM.h" 46 #include "llvm/Support/Memory.h" 47 #include "llvm/Support/Path.h" 48 #include "llvm/Support/Process.h" 49 #include "llvm/Support/SmallVectorMemoryBuffer.h" 50 #include "llvm/Support/StringSaver.h" 51 #include "llvm/Support/WithColor.h" 52 #include "llvm/Support/raw_ostream.h" 53 #include <algorithm> 54 #include <cassert> 55 #include <cstdlib> 56 #include <memory> 57 #include <string> 58 #include <system_error> 59 #include <utility> 60 61 using namespace llvm; 62 using namespace llvm::objcopy; 63 using namespace llvm::object; 64 65 // The name this program was invoked as. 66 static StringRef ToolName; 67 68 static ErrorSuccess reportWarning(Error E) { 69 assert(E); 70 WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n'; 71 return Error::success(); 72 } 73 74 static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) { 75 StringRef Stem = sys::path::stem(ToolName); 76 auto Is = [=](StringRef Tool) { 77 // We need to recognize the following filenames: 78 // 79 // llvm-objcopy -> objcopy 80 // strip-10.exe -> strip 81 // powerpc64-unknown-freebsd13-objcopy -> objcopy 82 // llvm-install-name-tool -> install-name-tool 83 auto I = Stem.rfind_insensitive(Tool); 84 return I != StringRef::npos && 85 (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()])); 86 }; 87 88 if (Is("bitcode-strip") || Is("bitcode_strip")) 89 return parseBitcodeStripOptions(Args, reportWarning); 90 else if (Is("strip")) 91 return parseStripOptions(Args, reportWarning); 92 else if (Is("install-name-tool") || Is("install_name_tool")) 93 return parseInstallNameToolOptions(Args); 94 else 95 return parseObjcopyOptions(Args, reportWarning); 96 } 97 98 /// The function executeObjcopyOnIHex does the dispatch based on the format 99 /// of the output specified by the command line options. 100 static Error executeObjcopyOnIHex(ConfigManager &ConfigMgr, MemoryBuffer &In, 101 raw_ostream &Out) { 102 // TODO: support output formats other than ELF. 103 Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig(); 104 if (!ELFConfig) 105 return ELFConfig.takeError(); 106 107 return elf::executeObjcopyOnIHex(ConfigMgr.getCommonConfig(), *ELFConfig, In, 108 Out); 109 } 110 111 /// The function executeObjcopyOnRawBinary does the dispatch based on the format 112 /// of the output specified by the command line options. 113 static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr, 114 MemoryBuffer &In, raw_ostream &Out) { 115 const CommonConfig &Config = ConfigMgr.getCommonConfig(); 116 switch (Config.OutputFormat) { 117 case FileFormat::ELF: 118 // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the 119 // output format is binary/ihex or it's not given. This behavior differs from 120 // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details. 121 case FileFormat::Binary: 122 case FileFormat::IHex: 123 case FileFormat::Unspecified: 124 Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig(); 125 if (!ELFConfig) 126 return ELFConfig.takeError(); 127 128 return elf::executeObjcopyOnRawBinary(Config, *ELFConfig, In, Out); 129 } 130 131 llvm_unreachable("unsupported output format"); 132 } 133 134 static Error restoreStatOnFile(StringRef Filename, 135 const sys::fs::file_status &Stat, 136 const ConfigManager &ConfigMgr) { 137 int FD; 138 const CommonConfig &Config = ConfigMgr.getCommonConfig(); 139 140 // Writing to stdout should not be treated as an error here, just 141 // do not set access/modification times or permissions. 142 if (Filename == "-") 143 return Error::success(); 144 145 if (auto EC = 146 sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) 147 return createFileError(Filename, EC); 148 149 if (Config.PreserveDates) 150 if (auto EC = sys::fs::setLastAccessAndModificationTime( 151 FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) 152 return createFileError(Filename, EC); 153 154 sys::fs::file_status OStat; 155 if (std::error_code EC = sys::fs::status(FD, OStat)) 156 return createFileError(Filename, EC); 157 if (OStat.type() == sys::fs::file_type::regular_file) { 158 #ifndef _WIN32 159 // Keep ownership if llvm-objcopy is called under root. 160 if (Config.InputFilename == Config.OutputFilename && OStat.getUser() == 0) 161 sys::fs::changeFileOwnership(FD, Stat.getUser(), Stat.getGroup()); 162 #endif 163 164 sys::fs::perms Perm = Stat.permissions(); 165 if (Config.InputFilename != Config.OutputFilename) 166 Perm = static_cast<sys::fs::perms>(Perm & ~sys::fs::getUmask() & ~06000); 167 #ifdef _WIN32 168 if (auto EC = sys::fs::setPermissions(Filename, Perm)) 169 #else 170 if (auto EC = sys::fs::setPermissions(FD, Perm)) 171 #endif 172 return createFileError(Filename, EC); 173 } 174 175 if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) 176 return createFileError(Filename, EC); 177 178 return Error::success(); 179 } 180 181 /// The function executeObjcopy does the higher level dispatch based on the type 182 /// of input (raw binary, archive or single object file) and takes care of the 183 /// format-agnostic modifications, i.e. preserving dates. 184 static Error executeObjcopy(ConfigManager &ConfigMgr) { 185 CommonConfig &Config = ConfigMgr.Common; 186 187 sys::fs::file_status Stat; 188 if (Config.InputFilename != "-") { 189 if (auto EC = sys::fs::status(Config.InputFilename, Stat)) 190 return createFileError(Config.InputFilename, EC); 191 } else { 192 Stat.permissions(static_cast<sys::fs::perms>(0777)); 193 } 194 195 std::function<Error(raw_ostream & OutFile)> ObjcopyFunc; 196 197 OwningBinary<llvm::object::Binary> BinaryHolder; 198 std::unique_ptr<MemoryBuffer> MemoryBufferHolder; 199 200 if (Config.InputFormat == FileFormat::Binary || 201 Config.InputFormat == FileFormat::IHex) { 202 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 203 MemoryBuffer::getFileOrSTDIN(Config.InputFilename); 204 if (!BufOrErr) 205 return createFileError(Config.InputFilename, BufOrErr.getError()); 206 MemoryBufferHolder = std::move(*BufOrErr); 207 208 if (Config.InputFormat == FileFormat::Binary) 209 ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { 210 // Handle FileFormat::Binary. 211 return executeObjcopyOnRawBinary(ConfigMgr, *MemoryBufferHolder, 212 OutFile); 213 }; 214 else 215 ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { 216 // Handle FileFormat::IHex. 217 return executeObjcopyOnIHex(ConfigMgr, *MemoryBufferHolder, OutFile); 218 }; 219 } else { 220 Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = 221 createBinary(Config.InputFilename); 222 if (!BinaryOrErr) 223 return createFileError(Config.InputFilename, BinaryOrErr.takeError()); 224 BinaryHolder = std::move(*BinaryOrErr); 225 226 if (Archive *Ar = dyn_cast<Archive>(BinaryHolder.getBinary())) { 227 // Handle Archive. 228 if (Error E = executeObjcopyOnArchive(ConfigMgr, *Ar)) 229 return E; 230 } else { 231 // Handle llvm::object::Binary. 232 ObjcopyFunc = [&](raw_ostream &OutFile) -> Error { 233 return executeObjcopyOnBinary(ConfigMgr, *BinaryHolder.getBinary(), 234 OutFile); 235 }; 236 } 237 } 238 239 if (ObjcopyFunc) { 240 if (Config.SplitDWO.empty()) { 241 // Apply transformations described by Config and store result into 242 // Config.OutputFilename using specified ObjcopyFunc function. 243 if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc)) 244 return E; 245 } else { 246 Config.ExtractDWO = true; 247 Config.StripDWO = false; 248 // Copy .dwo tables from the Config.InputFilename into Config.SplitDWO 249 // file using specified ObjcopyFunc function. 250 if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc)) 251 return E; 252 Config.ExtractDWO = false; 253 Config.StripDWO = true; 254 // Apply transformations described by Config, remove .dwo tables and 255 // store result into Config.OutputFilename using specified ObjcopyFunc 256 // function. 257 if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc)) 258 return E; 259 } 260 } 261 262 if (Error E = restoreStatOnFile(Config.OutputFilename, Stat, ConfigMgr)) 263 return E; 264 265 if (!Config.SplitDWO.empty()) { 266 Stat.permissions(static_cast<sys::fs::perms>(0666)); 267 if (Error E = restoreStatOnFile(Config.SplitDWO, Stat, ConfigMgr)) 268 return E; 269 } 270 271 return Error::success(); 272 } 273 274 int main(int argc, char **argv) { 275 InitLLVM X(argc, argv); 276 ToolName = argv[0]; 277 278 // Expand response files. 279 // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp, 280 // into a separate function in the CommandLine library and call that function 281 // here. This is duplicated code. 282 SmallVector<const char *, 20> NewArgv(argv, argv + argc); 283 BumpPtrAllocator A; 284 StringSaver Saver(A); 285 cl::ExpandResponseFiles(Saver, 286 Triple(sys::getProcessTriple()).isOSWindows() 287 ? cl::TokenizeWindowsCommandLine 288 : cl::TokenizeGNUCommandLine, 289 NewArgv); 290 291 auto Args = makeArrayRef(NewArgv).drop_front(); 292 Expected<DriverConfig> DriverConfig = getDriverConfig(Args); 293 294 if (!DriverConfig) { 295 logAllUnhandledErrors(DriverConfig.takeError(), 296 WithColor::error(errs(), ToolName)); 297 return 1; 298 } 299 for (ConfigManager &ConfigMgr : DriverConfig->CopyConfigs) { 300 if (Error E = executeObjcopy(ConfigMgr)) { 301 logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName)); 302 return 1; 303 } 304 } 305 306 return 0; 307 } 308