brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.0 KiB · cebcb82 Raw
350 lines · cpp
1//===- COFFObjcopy.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 "llvm/ObjCopy/COFF/COFFObjcopy.h"10#include "COFFObject.h"11#include "COFFReader.h"12#include "COFFWriter.h"13#include "llvm/ObjCopy/COFF/COFFConfig.h"14#include "llvm/ObjCopy/CommonConfig.h"15 16#include "llvm/ADT/StringExtras.h"17#include "llvm/Object/Binary.h"18#include "llvm/Object/COFF.h"19#include "llvm/Support/CRC.h"20#include "llvm/Support/Errc.h"21#include "llvm/Support/Path.h"22#include <cassert>23 24namespace llvm {25namespace objcopy {26namespace coff {27 28using namespace object;29using namespace COFF;30 31static bool isDebugSection(const Section &Sec) {32  return Sec.Name.starts_with(".debug");33}34 35static uint64_t getNextRVA(const Object &Obj) {36  if (Obj.getSections().empty())37    return 0;38  const Section &Last = Obj.getSections().back();39  return alignTo(Last.Header.VirtualAddress + Last.Header.VirtualSize,40                 Obj.IsPE ? Obj.PeHeader.SectionAlignment : 1);41}42 43static Expected<std::vector<uint8_t>>44createGnuDebugLinkSectionContents(StringRef File) {45  ErrorOr<std::unique_ptr<MemoryBuffer>> LinkTargetOrErr =46      MemoryBuffer::getFile(File);47  if (!LinkTargetOrErr)48    return createFileError(File, LinkTargetOrErr.getError());49  auto LinkTarget = std::move(*LinkTargetOrErr);50  uint32_t CRC32 = llvm::crc32(arrayRefFromStringRef(LinkTarget->getBuffer()));51 52  StringRef FileName = sys::path::filename(File);53  size_t CRCPos = alignTo(FileName.size() + 1, 4);54  std::vector<uint8_t> Data(CRCPos + 4);55  memcpy(Data.data(), FileName.data(), FileName.size());56  support::endian::write32le(Data.data() + CRCPos, CRC32);57  return Data;58}59 60// Adds named section with given contents to the object.61static void addSection(Object &Obj, StringRef Name, ArrayRef<uint8_t> Contents,62                       uint32_t Characteristics) {63  bool NeedVA = Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ |64                                   IMAGE_SCN_MEM_WRITE);65 66  Section Sec;67  Sec.setOwnedContents(Contents);68  Sec.Name = Name;69  Sec.Header.VirtualSize = NeedVA ? Sec.getContents().size() : 0u;70  Sec.Header.VirtualAddress = NeedVA ? getNextRVA(Obj) : 0u;71  Sec.Header.SizeOfRawData =72      NeedVA ? alignTo(Sec.Header.VirtualSize,73                       Obj.IsPE ? Obj.PeHeader.FileAlignment : 1)74             : Sec.getContents().size();75  // Sec.Header.PointerToRawData is filled in by the writer.76  Sec.Header.PointerToRelocations = 0;77  Sec.Header.PointerToLinenumbers = 0;78  // Sec.Header.NumberOfRelocations is filled in by the writer.79  Sec.Header.NumberOfLinenumbers = 0;80  Sec.Header.Characteristics = Characteristics;81 82  Obj.addSections(Sec);83}84 85static Error addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) {86  Expected<std::vector<uint8_t>> Contents =87      createGnuDebugLinkSectionContents(DebugLinkFile);88  if (!Contents)89    return Contents.takeError();90 91  addSection(Obj, ".gnu_debuglink", *Contents,92             IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |93                 IMAGE_SCN_MEM_DISCARDABLE);94 95  return Error::success();96}97 98static uint32_t flagsToCharacteristics(SectionFlag AllFlags, uint32_t OldChar) {99  // Need to preserve alignment flags.100  const uint32_t PreserveMask =101      IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_ALIGN_4BYTES |102      IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_ALIGN_16BYTES |103      IMAGE_SCN_ALIGN_32BYTES | IMAGE_SCN_ALIGN_64BYTES |104      IMAGE_SCN_ALIGN_128BYTES | IMAGE_SCN_ALIGN_256BYTES |105      IMAGE_SCN_ALIGN_512BYTES | IMAGE_SCN_ALIGN_1024BYTES |106      IMAGE_SCN_ALIGN_2048BYTES | IMAGE_SCN_ALIGN_4096BYTES |107      IMAGE_SCN_ALIGN_8192BYTES;108 109  // Setup new section characteristics based on the flags provided in command110  // line.111  uint32_t NewCharacteristics = (OldChar & PreserveMask) | IMAGE_SCN_MEM_READ;112 113  if ((AllFlags & SectionFlag::SecAlloc) && !(AllFlags & SectionFlag::SecLoad))114    NewCharacteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;115  if (AllFlags & SectionFlag::SecNoload)116    NewCharacteristics |= IMAGE_SCN_LNK_REMOVE;117  if (!(AllFlags & SectionFlag::SecReadonly))118    NewCharacteristics |= IMAGE_SCN_MEM_WRITE;119  if (AllFlags & SectionFlag::SecDebug)120    NewCharacteristics |=121        IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE;122  if (AllFlags & SectionFlag::SecCode)123    NewCharacteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE;124  if (AllFlags & SectionFlag::SecData)125    NewCharacteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA;126  if (AllFlags & SectionFlag::SecShare)127    NewCharacteristics |= IMAGE_SCN_MEM_SHARED;128  if (AllFlags & SectionFlag::SecExclude)129    NewCharacteristics |= IMAGE_SCN_LNK_REMOVE;130 131  return NewCharacteristics;132}133 134static Error dumpSection(Object &O, StringRef SectionName, StringRef FileName) {135  for (const coff::Section &Section : O.getSections()) {136    if (Section.Name != SectionName)137      continue;138 139    ArrayRef<uint8_t> Contents = Section.getContents();140 141    std::unique_ptr<FileOutputBuffer> Buffer;142    if (auto B = FileOutputBuffer::create(FileName, Contents.size()))143      Buffer = std::move(*B);144    else145      return B.takeError();146 147    llvm::copy(Contents, Buffer->getBufferStart());148    if (Error E = Buffer->commit())149      return E;150 151    return Error::success();152  }153  return createStringError(object_error::parse_failed, "section '%s' not found",154                           SectionName.str().c_str());155}156 157static Error handleArgs(const CommonConfig &Config,158                        const COFFConfig &COFFConfig, Object &Obj) {159  for (StringRef Op : Config.DumpSection) {160    auto [Section, File] = Op.split('=');161    if (Error E = dumpSection(Obj, Section, File))162      return E;163  }164 165  // Perform the actual section removals.166  Obj.removeSections([&Config](const Section &Sec) {167    // Contrary to --only-keep-debug, --only-section fully removes sections that168    // aren't mentioned.169    if (!Config.OnlySection.empty() && !Config.OnlySection.matches(Sec.Name))170      return true;171 172    if (Config.StripDebug || Config.StripAll || Config.StripAllGNU ||173        Config.DiscardMode == DiscardType::All || Config.StripUnneeded) {174      if (isDebugSection(Sec) &&175          (Sec.Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0)176        return true;177    }178 179    if (Config.ToRemove.matches(Sec.Name))180      return true;181 182    return false;183  });184 185  if (Config.OnlyKeepDebug) {186    const data_directory *DebugDir =187        Obj.DataDirectories.size() > DEBUG_DIRECTORY188            ? &Obj.DataDirectories[DEBUG_DIRECTORY]189            : nullptr;190    // For --only-keep-debug, we keep all other sections, but remove their191    // content. The VirtualSize field in the section header is kept intact.192    Obj.truncateSections([DebugDir](const Section &Sec) {193      return !isDebugSection(Sec) && Sec.Name != ".buildid" &&194             !(DebugDir && DebugDir->Size > 0 &&195               DebugDir->RelativeVirtualAddress >= Sec.Header.VirtualAddress &&196               DebugDir->RelativeVirtualAddress <197                   Sec.Header.VirtualAddress + Sec.Header.SizeOfRawData) &&198             ((Sec.Header.Characteristics &199               (IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA)) != 0);200    });201  }202 203  // StripAll removes all symbols and thus also removes all relocations.204  if (Config.StripAll || Config.StripAllGNU)205    for (Section &Sec : Obj.getMutableSections())206      Sec.Relocs.clear();207 208  // If we need to do per-symbol removals, initialize the Referenced field.209  if (Config.StripUnneeded || Config.DiscardMode == DiscardType::All ||210      !Config.SymbolsToRemove.empty())211    if (Error E = Obj.markSymbols())212      return E;213 214  for (Symbol &Sym : Obj.getMutableSymbols()) {215    auto I = Config.SymbolsToRename.find(Sym.Name);216    if (I != Config.SymbolsToRename.end())217      Sym.Name = I->getValue();218  }219 220  auto ToRemove = [&](const Symbol &Sym) -> Expected<bool> {221    // For StripAll, all relocations have been stripped and we remove all222    // symbols.223    if (Config.StripAll || Config.StripAllGNU)224      return true;225 226    if (Config.SymbolsToRemove.matches(Sym.Name)) {227      // Explicitly removing a referenced symbol is an error.228      if (Sym.Referenced)229        return createStringError(230            llvm::errc::invalid_argument,231            "'" + Config.OutputFilename + "': not stripping symbol '" +232                Sym.Name.str() + "' because it is named in a relocation");233      return true;234    }235 236    if (!Sym.Referenced) {237      // With --strip-unneeded, GNU objcopy removes all unreferenced local238      // symbols, and any unreferenced undefined external.239      // With --strip-unneeded-symbol we strip only specific unreferenced240      // local symbol instead of removing all of such.241      if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC ||242          Sym.Sym.SectionNumber == 0)243        if (Config.StripUnneeded ||244            Config.UnneededSymbolsToRemove.matches(Sym.Name))245          return true;246 247      // GNU objcopy keeps referenced local symbols and external symbols248      // if --discard-all is set, similar to what --strip-unneeded does,249      // but undefined local symbols are kept when --discard-all is set.250      if (Config.DiscardMode == DiscardType::All &&251          Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC &&252          Sym.Sym.SectionNumber != 0)253        return true;254    }255 256    return false;257  };258 259  // Actually do removals of symbols.260  if (Error Err = Obj.removeSymbols(ToRemove))261    return Err;262 263  if (!Config.SetSectionFlags.empty())264    for (Section &Sec : Obj.getMutableSections()) {265      const auto It = Config.SetSectionFlags.find(Sec.Name);266      if (It != Config.SetSectionFlags.end())267        Sec.Header.Characteristics = flagsToCharacteristics(268            It->second.NewFlags, Sec.Header.Characteristics);269    }270 271  for (const NewSectionInfo &NewSection : Config.AddSection) {272    uint32_t Characteristics;273    const auto It = Config.SetSectionFlags.find(NewSection.SectionName);274    if (It != Config.SetSectionFlags.end())275      Characteristics = flagsToCharacteristics(It->second.NewFlags, 0);276    else277      Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES;278 279    addSection(Obj, NewSection.SectionName,280               ArrayRef(reinterpret_cast<const uint8_t *>(281                            NewSection.SectionData->getBufferStart()),282                        NewSection.SectionData->getBufferSize()),283               Characteristics);284  }285 286  for (const NewSectionInfo &NewSection : Config.UpdateSection) {287    auto It = llvm::find_if(Obj.getMutableSections(), [&](auto &Sec) {288      return Sec.Name == NewSection.SectionName;289    });290    if (It == Obj.getMutableSections().end())291      return createStringError(errc::invalid_argument,292                               "could not find section with name '%s'",293                               NewSection.SectionName.str().c_str());294    size_t ContentSize = It->getContents().size();295    if (!ContentSize)296      return createStringError(297          errc::invalid_argument,298          "section '%s' cannot be updated because it does not have contents",299          NewSection.SectionName.str().c_str());300    if (ContentSize < NewSection.SectionData->getBufferSize())301      return createStringError(302          errc::invalid_argument,303          "new section cannot be larger than previous section");304    It->setOwnedContents({NewSection.SectionData->getBufferStart(),305                          NewSection.SectionData->getBufferEnd()});306  }307 308  if (!Config.AddGnuDebugLink.empty())309    if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink))310      return E;311 312  if (COFFConfig.Subsystem || COFFConfig.MajorSubsystemVersion ||313      COFFConfig.MinorSubsystemVersion) {314    if (!Obj.IsPE)315      return createStringError(316          errc::invalid_argument,317          "'" + Config.OutputFilename +318              "': unable to set subsystem on a relocatable object file");319    if (COFFConfig.Subsystem)320      Obj.PeHeader.Subsystem = *COFFConfig.Subsystem;321    if (COFFConfig.MajorSubsystemVersion)322      Obj.PeHeader.MajorSubsystemVersion = *COFFConfig.MajorSubsystemVersion;323    if (COFFConfig.MinorSubsystemVersion)324      Obj.PeHeader.MinorSubsystemVersion = *COFFConfig.MinorSubsystemVersion;325  }326 327  return Error::success();328}329 330Error executeObjcopyOnBinary(const CommonConfig &Config,331                             const COFFConfig &COFFConfig, COFFObjectFile &In,332                             raw_ostream &Out) {333  COFFReader Reader(In);334  Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create();335  if (!ObjOrErr)336    return createFileError(Config.InputFilename, ObjOrErr.takeError());337  Object *Obj = ObjOrErr->get();338  assert(Obj && "Unable to deserialize COFF object");339  if (Error E = handleArgs(Config, COFFConfig, *Obj))340    return createFileError(Config.InputFilename, std::move(E));341  COFFWriter Writer(*Obj, Out);342  if (Error E = Writer.write())343    return createFileError(Config.OutputFilename, std::move(E));344  return Error::success();345}346 347} // end namespace coff348} // end namespace objcopy349} // end namespace llvm350