249 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "ObjcopyOptions.h"10#include "llvm/ADT/SmallVector.h"11#include "llvm/ADT/StringRef.h"12#include "llvm/ADT/Twine.h"13#include "llvm/ObjCopy/COFF/COFFObjcopy.h"14#include "llvm/ObjCopy/CommonConfig.h"15#include "llvm/ObjCopy/ELF/ELFConfig.h"16#include "llvm/ObjCopy/ELF/ELFObjcopy.h"17#include "llvm/ObjCopy/MachO/MachOObjcopy.h"18#include "llvm/ObjCopy/ObjCopy.h"19#include "llvm/ObjCopy/wasm/WasmObjcopy.h"20#include "llvm/Object/Archive.h"21#include "llvm/Object/Binary.h"22#include "llvm/Object/COFF.h"23#include "llvm/Object/Error.h"24#include "llvm/Object/MachOUniversal.h"25#include "llvm/Option/Arg.h"26#include "llvm/Option/ArgList.h"27#include "llvm/Option/Option.h"28#include "llvm/Support/Casting.h"29#include "llvm/Support/CommandLine.h"30#include "llvm/Support/Error.h"31#include "llvm/Support/ErrorHandling.h"32#include "llvm/Support/ErrorOr.h"33#include "llvm/Support/FileUtilities.h"34#include "llvm/Support/LLVMDriver.h"35#include "llvm/Support/Memory.h"36#include "llvm/Support/Path.h"37#include "llvm/Support/Process.h"38#include "llvm/Support/StringSaver.h"39#include "llvm/Support/WithColor.h"40#include "llvm/Support/raw_ostream.h"41#include "llvm/TargetParser/Host.h"42#include <cassert>43#include <cstdlib>44#include <memory>45#include <utility>46 47using namespace llvm;48using namespace llvm::objcopy;49using namespace llvm::object;50 51// The name this program was invoked as.52static StringRef ToolName;53 54static ErrorSuccess reportWarning(Error E) {55 assert(E);56 WithColor::warning(errs(), ToolName) << toString(std::move(E)) << '\n';57 return Error::success();58}59 60static Expected<DriverConfig> getDriverConfig(ArrayRef<const char *> Args) {61 StringRef Stem = sys::path::stem(ToolName);62 auto Is = [=](StringRef Tool) {63 // We need to recognize the following filenames:64 //65 // llvm-objcopy -> objcopy66 // strip-10.exe -> strip67 // powerpc64-unknown-freebsd13-objcopy -> objcopy68 // llvm-install-name-tool -> install-name-tool69 auto I = Stem.rfind_insensitive(Tool);70 return I != StringRef::npos &&71 (I + Tool.size() == Stem.size() || !isAlnum(Stem[I + Tool.size()]));72 };73 74 if (Is("bitcode-strip") || Is("bitcode_strip"))75 return parseBitcodeStripOptions(Args, reportWarning);76 else if (Is("strip"))77 return parseStripOptions(Args, reportWarning);78 else if (Is("install-name-tool") || Is("install_name_tool"))79 return parseInstallNameToolOptions(Args);80 else81 return parseObjcopyOptions(Args, reportWarning);82}83 84/// The function executeObjcopyOnIHex does the dispatch based on the format85/// of the output specified by the command line options.86static Error executeObjcopyOnIHex(ConfigManager &ConfigMgr, MemoryBuffer &In,87 raw_ostream &Out) {88 // TODO: support output formats other than ELF.89 Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig();90 if (!ELFConfig)91 return ELFConfig.takeError();92 93 return elf::executeObjcopyOnIHex(ConfigMgr.getCommonConfig(), *ELFConfig, In,94 Out);95}96 97/// The function executeObjcopyOnRawBinary does the dispatch based on the format98/// of the output specified by the command line options.99static Error executeObjcopyOnRawBinary(ConfigManager &ConfigMgr,100 MemoryBuffer &In, raw_ostream &Out) {101 const CommonConfig &Config = ConfigMgr.getCommonConfig();102 switch (Config.OutputFormat) {103 case FileFormat::ELF:104 // FIXME: Currently, we call elf::executeObjcopyOnRawBinary even if the105 // output format is binary/ihex or it's not given. This behavior differs from106 // GNU objcopy. See https://bugs.llvm.org/show_bug.cgi?id=42171 for details.107 case FileFormat::Binary:108 case FileFormat::IHex:109 case FileFormat::Unspecified:110 case FileFormat::SREC:111 Expected<const ELFConfig &> ELFConfig = ConfigMgr.getELFConfig();112 if (!ELFConfig)113 return ELFConfig.takeError();114 115 return elf::executeObjcopyOnRawBinary(Config, *ELFConfig, In, Out);116 }117 118 llvm_unreachable("unsupported output format");119}120 121/// The function executeObjcopy does the higher level dispatch based on the type122/// of input (raw binary, archive or single object file) and takes care of the123/// format-agnostic modifications, i.e. preserving dates.124static Error executeObjcopy(ConfigManager &ConfigMgr) {125 CommonConfig &Config = ConfigMgr.Common;126 127 Expected<FilePermissionsApplier> PermsApplierOrErr =128 FilePermissionsApplier::create(Config.InputFilename);129 if (!PermsApplierOrErr)130 return PermsApplierOrErr.takeError();131 132 std::function<Error(raw_ostream & OutFile)> ObjcopyFunc;133 134 OwningBinary<llvm::object::Binary> BinaryHolder;135 std::unique_ptr<MemoryBuffer> MemoryBufferHolder;136 137 if (Config.InputFormat == FileFormat::Binary ||138 Config.InputFormat == FileFormat::IHex) {139 ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =140 MemoryBuffer::getFileOrSTDIN(Config.InputFilename);141 if (!BufOrErr)142 return createFileError(Config.InputFilename, BufOrErr.getError());143 MemoryBufferHolder = std::move(*BufOrErr);144 145 if (Config.InputFormat == FileFormat::Binary)146 ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {147 // Handle FileFormat::Binary.148 return executeObjcopyOnRawBinary(ConfigMgr, *MemoryBufferHolder,149 OutFile);150 };151 else152 ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {153 // Handle FileFormat::IHex.154 return executeObjcopyOnIHex(ConfigMgr, *MemoryBufferHolder, OutFile);155 };156 } else {157 Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =158 createBinary(Config.InputFilename);159 if (!BinaryOrErr)160 return createFileError(Config.InputFilename, BinaryOrErr.takeError());161 BinaryHolder = std::move(*BinaryOrErr);162 163 if (Archive *Ar = dyn_cast<Archive>(BinaryHolder.getBinary())) {164 // Handle Archive.165 if (Error E = executeObjcopyOnArchive(ConfigMgr, *Ar))166 return E;167 } else {168 // Handle llvm::object::Binary.169 ObjcopyFunc = [&](raw_ostream &OutFile) -> Error {170 return executeObjcopyOnBinary(ConfigMgr, *BinaryHolder.getBinary(),171 OutFile);172 };173 }174 }175 176 if (ObjcopyFunc) {177 if (Config.SplitDWO.empty()) {178 // Apply transformations described by Config and store result into179 // Config.OutputFilename using specified ObjcopyFunc function.180 if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))181 return E;182 } else {183 Config.ExtractDWO = true;184 Config.StripDWO = false;185 // Copy .dwo tables from the Config.InputFilename into Config.SplitDWO186 // file using specified ObjcopyFunc function.187 if (Error E = writeToOutput(Config.SplitDWO, ObjcopyFunc))188 return E;189 Config.ExtractDWO = false;190 Config.StripDWO = true;191 // Apply transformations described by Config, remove .dwo tables and192 // store result into Config.OutputFilename using specified ObjcopyFunc193 // function.194 if (Error E = writeToOutput(Config.OutputFilename, ObjcopyFunc))195 return E;196 }197 }198 199 if (Error E =200 PermsApplierOrErr->apply(Config.OutputFilename, Config.PreserveDates))201 return E;202 203 if (!Config.SplitDWO.empty())204 if (Error E =205 PermsApplierOrErr->apply(Config.SplitDWO, Config.PreserveDates,206 static_cast<sys::fs::perms>(0666)))207 return E;208 209 return Error::success();210}211 212int llvm_objcopy_main(int argc, char **argv, const llvm::ToolContext &) {213 ToolName = argv[0];214 215 // Expand response files.216 // TODO: Move these lines, which are copied from lib/Support/CommandLine.cpp,217 // into a separate function in the CommandLine library and call that function218 // here. This is duplicated code.219 SmallVector<const char *, 20> NewArgv(argv, argv + argc);220 BumpPtrAllocator A;221 StringSaver Saver(A);222 cl::ExpandResponseFiles(Saver,223 Triple(sys::getProcessTriple()).isOSWindows()224 ? cl::TokenizeWindowsCommandLine225 : cl::TokenizeGNUCommandLine,226 NewArgv);227 228 auto Args = ArrayRef(NewArgv).drop_front();229 Expected<DriverConfig> DriverConfig = getDriverConfig(Args);230 231 if (!DriverConfig) {232 logAllUnhandledErrors(DriverConfig.takeError(),233 WithColor::error(errs(), ToolName));234 return 1;235 }236 237 int ret = 0;238 for (ConfigManager &ConfigMgr : DriverConfig->CopyConfigs) {239 assert(!ConfigMgr.Common.ErrorCallback);240 ConfigMgr.Common.ErrorCallback = reportWarning;241 if (Error E = executeObjcopy(ConfigMgr)) {242 logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolName));243 ret = 1;244 }245 }246 247 return ret;248}249