xref: /freebsd-src/contrib/llvm-project/llvm/lib/ObjCopy/COFF/COFFObjcopy.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
181ad6265SDimitry Andric //===- COFFObjcopy.cpp ----------------------------------------------------===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric 
981ad6265SDimitry Andric #include "llvm/ObjCopy/COFF/COFFObjcopy.h"
1081ad6265SDimitry Andric #include "COFFObject.h"
1181ad6265SDimitry Andric #include "COFFReader.h"
1281ad6265SDimitry Andric #include "COFFWriter.h"
1381ad6265SDimitry Andric #include "llvm/ObjCopy/COFF/COFFConfig.h"
1481ad6265SDimitry Andric #include "llvm/ObjCopy/CommonConfig.h"
1581ad6265SDimitry Andric 
1681ad6265SDimitry Andric #include "llvm/Object/Binary.h"
1781ad6265SDimitry Andric #include "llvm/Object/COFF.h"
1881ad6265SDimitry Andric #include "llvm/Support/CRC.h"
1981ad6265SDimitry Andric #include "llvm/Support/Errc.h"
2081ad6265SDimitry Andric #include "llvm/Support/Path.h"
2181ad6265SDimitry Andric #include <cassert>
2281ad6265SDimitry Andric 
2381ad6265SDimitry Andric namespace llvm {
2481ad6265SDimitry Andric namespace objcopy {
2581ad6265SDimitry Andric namespace coff {
2681ad6265SDimitry Andric 
2781ad6265SDimitry Andric using namespace object;
2881ad6265SDimitry Andric using namespace COFF;
2981ad6265SDimitry Andric 
3081ad6265SDimitry Andric static bool isDebugSection(const Section &Sec) {
3181ad6265SDimitry Andric   return Sec.Name.startswith(".debug");
3281ad6265SDimitry Andric }
3381ad6265SDimitry Andric 
3481ad6265SDimitry Andric static uint64_t getNextRVA(const Object &Obj) {
3581ad6265SDimitry Andric   if (Obj.getSections().empty())
3681ad6265SDimitry Andric     return 0;
3781ad6265SDimitry Andric   const Section &Last = Obj.getSections().back();
3881ad6265SDimitry Andric   return alignTo(Last.Header.VirtualAddress + Last.Header.VirtualSize,
3981ad6265SDimitry Andric                  Obj.IsPE ? Obj.PeHeader.SectionAlignment : 1);
4081ad6265SDimitry Andric }
4181ad6265SDimitry Andric 
4281ad6265SDimitry Andric static Expected<std::vector<uint8_t>>
4381ad6265SDimitry Andric createGnuDebugLinkSectionContents(StringRef File) {
4481ad6265SDimitry Andric   ErrorOr<std::unique_ptr<MemoryBuffer>> LinkTargetOrErr =
4581ad6265SDimitry Andric       MemoryBuffer::getFile(File);
4681ad6265SDimitry Andric   if (!LinkTargetOrErr)
4781ad6265SDimitry Andric     return createFileError(File, LinkTargetOrErr.getError());
4881ad6265SDimitry Andric   auto LinkTarget = std::move(*LinkTargetOrErr);
4981ad6265SDimitry Andric   uint32_t CRC32 = llvm::crc32(arrayRefFromStringRef(LinkTarget->getBuffer()));
5081ad6265SDimitry Andric 
5181ad6265SDimitry Andric   StringRef FileName = sys::path::filename(File);
5281ad6265SDimitry Andric   size_t CRCPos = alignTo(FileName.size() + 1, 4);
5381ad6265SDimitry Andric   std::vector<uint8_t> Data(CRCPos + 4);
5481ad6265SDimitry Andric   memcpy(Data.data(), FileName.data(), FileName.size());
5581ad6265SDimitry Andric   support::endian::write32le(Data.data() + CRCPos, CRC32);
5681ad6265SDimitry Andric   return Data;
5781ad6265SDimitry Andric }
5881ad6265SDimitry Andric 
5981ad6265SDimitry Andric // Adds named section with given contents to the object.
6081ad6265SDimitry Andric static void addSection(Object &Obj, StringRef Name, ArrayRef<uint8_t> Contents,
6181ad6265SDimitry Andric                        uint32_t Characteristics) {
6281ad6265SDimitry Andric   bool NeedVA = Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ |
6381ad6265SDimitry Andric                                    IMAGE_SCN_MEM_WRITE);
6481ad6265SDimitry Andric 
6581ad6265SDimitry Andric   Section Sec;
6681ad6265SDimitry Andric   Sec.setOwnedContents(Contents);
6781ad6265SDimitry Andric   Sec.Name = Name;
6881ad6265SDimitry Andric   Sec.Header.VirtualSize = NeedVA ? Sec.getContents().size() : 0u;
6981ad6265SDimitry Andric   Sec.Header.VirtualAddress = NeedVA ? getNextRVA(Obj) : 0u;
7081ad6265SDimitry Andric   Sec.Header.SizeOfRawData =
7181ad6265SDimitry Andric       NeedVA ? alignTo(Sec.Header.VirtualSize,
7281ad6265SDimitry Andric                        Obj.IsPE ? Obj.PeHeader.FileAlignment : 1)
7381ad6265SDimitry Andric              : Sec.getContents().size();
7481ad6265SDimitry Andric   // Sec.Header.PointerToRawData is filled in by the writer.
7581ad6265SDimitry Andric   Sec.Header.PointerToRelocations = 0;
7681ad6265SDimitry Andric   Sec.Header.PointerToLinenumbers = 0;
7781ad6265SDimitry Andric   // Sec.Header.NumberOfRelocations is filled in by the writer.
7881ad6265SDimitry Andric   Sec.Header.NumberOfLinenumbers = 0;
7981ad6265SDimitry Andric   Sec.Header.Characteristics = Characteristics;
8081ad6265SDimitry Andric 
8181ad6265SDimitry Andric   Obj.addSections(Sec);
8281ad6265SDimitry Andric }
8381ad6265SDimitry Andric 
8481ad6265SDimitry Andric static Error addGnuDebugLink(Object &Obj, StringRef DebugLinkFile) {
8581ad6265SDimitry Andric   Expected<std::vector<uint8_t>> Contents =
8681ad6265SDimitry Andric       createGnuDebugLinkSectionContents(DebugLinkFile);
8781ad6265SDimitry Andric   if (!Contents)
8881ad6265SDimitry Andric     return Contents.takeError();
8981ad6265SDimitry Andric 
9081ad6265SDimitry Andric   addSection(Obj, ".gnu_debuglink", *Contents,
9181ad6265SDimitry Andric              IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
9281ad6265SDimitry Andric                  IMAGE_SCN_MEM_DISCARDABLE);
9381ad6265SDimitry Andric 
9481ad6265SDimitry Andric   return Error::success();
9581ad6265SDimitry Andric }
9681ad6265SDimitry Andric 
9781ad6265SDimitry Andric static uint32_t flagsToCharacteristics(SectionFlag AllFlags, uint32_t OldChar) {
9881ad6265SDimitry Andric   // Need to preserve alignment flags.
9981ad6265SDimitry Andric   const uint32_t PreserveMask =
10081ad6265SDimitry Andric       IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_ALIGN_4BYTES |
10181ad6265SDimitry Andric       IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_ALIGN_16BYTES |
10281ad6265SDimitry Andric       IMAGE_SCN_ALIGN_32BYTES | IMAGE_SCN_ALIGN_64BYTES |
10381ad6265SDimitry Andric       IMAGE_SCN_ALIGN_128BYTES | IMAGE_SCN_ALIGN_256BYTES |
10481ad6265SDimitry Andric       IMAGE_SCN_ALIGN_512BYTES | IMAGE_SCN_ALIGN_1024BYTES |
10581ad6265SDimitry Andric       IMAGE_SCN_ALIGN_2048BYTES | IMAGE_SCN_ALIGN_4096BYTES |
10681ad6265SDimitry Andric       IMAGE_SCN_ALIGN_8192BYTES;
10781ad6265SDimitry Andric 
10881ad6265SDimitry Andric   // Setup new section characteristics based on the flags provided in command
10981ad6265SDimitry Andric   // line.
11081ad6265SDimitry Andric   uint32_t NewCharacteristics = (OldChar & PreserveMask) | IMAGE_SCN_MEM_READ;
11181ad6265SDimitry Andric 
11281ad6265SDimitry Andric   if ((AllFlags & SectionFlag::SecAlloc) && !(AllFlags & SectionFlag::SecLoad))
11381ad6265SDimitry Andric     NewCharacteristics |= IMAGE_SCN_CNT_UNINITIALIZED_DATA;
11481ad6265SDimitry Andric   if (AllFlags & SectionFlag::SecNoload)
11581ad6265SDimitry Andric     NewCharacteristics |= IMAGE_SCN_LNK_REMOVE;
11681ad6265SDimitry Andric   if (!(AllFlags & SectionFlag::SecReadonly))
11781ad6265SDimitry Andric     NewCharacteristics |= IMAGE_SCN_MEM_WRITE;
11881ad6265SDimitry Andric   if (AllFlags & SectionFlag::SecDebug)
11981ad6265SDimitry Andric     NewCharacteristics |=
12081ad6265SDimitry Andric         IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE;
12181ad6265SDimitry Andric   if (AllFlags & SectionFlag::SecCode)
12281ad6265SDimitry Andric     NewCharacteristics |= IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE;
12381ad6265SDimitry Andric   if (AllFlags & SectionFlag::SecData)
12481ad6265SDimitry Andric     NewCharacteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA;
12581ad6265SDimitry Andric   if (AllFlags & SectionFlag::SecShare)
12681ad6265SDimitry Andric     NewCharacteristics |= IMAGE_SCN_MEM_SHARED;
12781ad6265SDimitry Andric   if (AllFlags & SectionFlag::SecExclude)
12881ad6265SDimitry Andric     NewCharacteristics |= IMAGE_SCN_LNK_REMOVE;
12981ad6265SDimitry Andric 
13081ad6265SDimitry Andric   return NewCharacteristics;
13181ad6265SDimitry Andric }
13281ad6265SDimitry Andric 
13381ad6265SDimitry Andric static Error handleArgs(const CommonConfig &Config,
13481ad6265SDimitry Andric                         const COFFConfig &COFFConfig, Object &Obj) {
13581ad6265SDimitry Andric   // Perform the actual section removals.
13681ad6265SDimitry Andric   Obj.removeSections([&Config](const Section &Sec) {
13781ad6265SDimitry Andric     // Contrary to --only-keep-debug, --only-section fully removes sections that
13881ad6265SDimitry Andric     // aren't mentioned.
13981ad6265SDimitry Andric     if (!Config.OnlySection.empty() && !Config.OnlySection.matches(Sec.Name))
14081ad6265SDimitry Andric       return true;
14181ad6265SDimitry Andric 
14281ad6265SDimitry Andric     if (Config.StripDebug || Config.StripAll || Config.StripAllGNU ||
14381ad6265SDimitry Andric         Config.DiscardMode == DiscardType::All || Config.StripUnneeded) {
14481ad6265SDimitry Andric       if (isDebugSection(Sec) &&
14581ad6265SDimitry Andric           (Sec.Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0)
14681ad6265SDimitry Andric         return true;
14781ad6265SDimitry Andric     }
14881ad6265SDimitry Andric 
14981ad6265SDimitry Andric     if (Config.ToRemove.matches(Sec.Name))
15081ad6265SDimitry Andric       return true;
15181ad6265SDimitry Andric 
15281ad6265SDimitry Andric     return false;
15381ad6265SDimitry Andric   });
15481ad6265SDimitry Andric 
15581ad6265SDimitry Andric   if (Config.OnlyKeepDebug) {
15681ad6265SDimitry Andric     // For --only-keep-debug, we keep all other sections, but remove their
15781ad6265SDimitry Andric     // content. The VirtualSize field in the section header is kept intact.
15881ad6265SDimitry Andric     Obj.truncateSections([](const Section &Sec) {
15981ad6265SDimitry Andric       return !isDebugSection(Sec) && Sec.Name != ".buildid" &&
16081ad6265SDimitry Andric              ((Sec.Header.Characteristics &
16181ad6265SDimitry Andric                (IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA)) != 0);
16281ad6265SDimitry Andric     });
16381ad6265SDimitry Andric   }
16481ad6265SDimitry Andric 
16581ad6265SDimitry Andric   // StripAll removes all symbols and thus also removes all relocations.
16681ad6265SDimitry Andric   if (Config.StripAll || Config.StripAllGNU)
16781ad6265SDimitry Andric     for (Section &Sec : Obj.getMutableSections())
16881ad6265SDimitry Andric       Sec.Relocs.clear();
16981ad6265SDimitry Andric 
17081ad6265SDimitry Andric   // If we need to do per-symbol removals, initialize the Referenced field.
17181ad6265SDimitry Andric   if (Config.StripUnneeded || Config.DiscardMode == DiscardType::All ||
17281ad6265SDimitry Andric       !Config.SymbolsToRemove.empty())
17381ad6265SDimitry Andric     if (Error E = Obj.markSymbols())
17481ad6265SDimitry Andric       return E;
17581ad6265SDimitry Andric 
17681ad6265SDimitry Andric   for (Symbol &Sym : Obj.getMutableSymbols()) {
17781ad6265SDimitry Andric     auto I = Config.SymbolsToRename.find(Sym.Name);
17881ad6265SDimitry Andric     if (I != Config.SymbolsToRename.end())
17981ad6265SDimitry Andric       Sym.Name = I->getValue();
18081ad6265SDimitry Andric   }
18181ad6265SDimitry Andric 
18281ad6265SDimitry Andric   auto ToRemove = [&](const Symbol &Sym) -> Expected<bool> {
18381ad6265SDimitry Andric     // For StripAll, all relocations have been stripped and we remove all
18481ad6265SDimitry Andric     // symbols.
18581ad6265SDimitry Andric     if (Config.StripAll || Config.StripAllGNU)
18681ad6265SDimitry Andric       return true;
18781ad6265SDimitry Andric 
18881ad6265SDimitry Andric     if (Config.SymbolsToRemove.matches(Sym.Name)) {
18981ad6265SDimitry Andric       // Explicitly removing a referenced symbol is an error.
19081ad6265SDimitry Andric       if (Sym.Referenced)
19181ad6265SDimitry Andric         return createStringError(
19281ad6265SDimitry Andric             llvm::errc::invalid_argument,
19381ad6265SDimitry Andric             "'" + Config.OutputFilename + "': not stripping symbol '" +
19481ad6265SDimitry Andric                 Sym.Name.str() + "' because it is named in a relocation");
19581ad6265SDimitry Andric       return true;
19681ad6265SDimitry Andric     }
19781ad6265SDimitry Andric 
19881ad6265SDimitry Andric     if (!Sym.Referenced) {
19981ad6265SDimitry Andric       // With --strip-unneeded, GNU objcopy removes all unreferenced local
20081ad6265SDimitry Andric       // symbols, and any unreferenced undefined external.
20181ad6265SDimitry Andric       // With --strip-unneeded-symbol we strip only specific unreferenced
20281ad6265SDimitry Andric       // local symbol instead of removing all of such.
20381ad6265SDimitry Andric       if (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC ||
20481ad6265SDimitry Andric           Sym.Sym.SectionNumber == 0)
20581ad6265SDimitry Andric         if (Config.StripUnneeded ||
20681ad6265SDimitry Andric             Config.UnneededSymbolsToRemove.matches(Sym.Name))
20781ad6265SDimitry Andric           return true;
20881ad6265SDimitry Andric 
20981ad6265SDimitry Andric       // GNU objcopy keeps referenced local symbols and external symbols
21081ad6265SDimitry Andric       // if --discard-all is set, similar to what --strip-unneeded does,
21181ad6265SDimitry Andric       // but undefined local symbols are kept when --discard-all is set.
21281ad6265SDimitry Andric       if (Config.DiscardMode == DiscardType::All &&
21381ad6265SDimitry Andric           Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC &&
21481ad6265SDimitry Andric           Sym.Sym.SectionNumber != 0)
21581ad6265SDimitry Andric         return true;
21681ad6265SDimitry Andric     }
21781ad6265SDimitry Andric 
21881ad6265SDimitry Andric     return false;
21981ad6265SDimitry Andric   };
22081ad6265SDimitry Andric 
22181ad6265SDimitry Andric   // Actually do removals of symbols.
22281ad6265SDimitry Andric   if (Error Err = Obj.removeSymbols(ToRemove))
22381ad6265SDimitry Andric     return Err;
22481ad6265SDimitry Andric 
22581ad6265SDimitry Andric   if (!Config.SetSectionFlags.empty())
22681ad6265SDimitry Andric     for (Section &Sec : Obj.getMutableSections()) {
22781ad6265SDimitry Andric       const auto It = Config.SetSectionFlags.find(Sec.Name);
22881ad6265SDimitry Andric       if (It != Config.SetSectionFlags.end())
22981ad6265SDimitry Andric         Sec.Header.Characteristics = flagsToCharacteristics(
23081ad6265SDimitry Andric             It->second.NewFlags, Sec.Header.Characteristics);
23181ad6265SDimitry Andric     }
23281ad6265SDimitry Andric 
23381ad6265SDimitry Andric   for (const NewSectionInfo &NewSection : Config.AddSection) {
23481ad6265SDimitry Andric     uint32_t Characteristics;
23581ad6265SDimitry Andric     const auto It = Config.SetSectionFlags.find(NewSection.SectionName);
23681ad6265SDimitry Andric     if (It != Config.SetSectionFlags.end())
23781ad6265SDimitry Andric       Characteristics = flagsToCharacteristics(It->second.NewFlags, 0);
23881ad6265SDimitry Andric     else
23981ad6265SDimitry Andric       Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES;
24081ad6265SDimitry Andric 
24181ad6265SDimitry Andric     addSection(Obj, NewSection.SectionName,
242*bdd1243dSDimitry Andric                ArrayRef(reinterpret_cast<const uint8_t *>(
24381ad6265SDimitry Andric                             NewSection.SectionData->getBufferStart()),
24481ad6265SDimitry Andric                         NewSection.SectionData->getBufferSize()),
24581ad6265SDimitry Andric                Characteristics);
24681ad6265SDimitry Andric   }
24781ad6265SDimitry Andric 
24881ad6265SDimitry Andric   for (const NewSectionInfo &NewSection : Config.UpdateSection) {
24981ad6265SDimitry Andric     auto It = llvm::find_if(Obj.getMutableSections(), [&](auto &Sec) {
25081ad6265SDimitry Andric       return Sec.Name == NewSection.SectionName;
25181ad6265SDimitry Andric     });
25281ad6265SDimitry Andric     if (It == Obj.getMutableSections().end())
25381ad6265SDimitry Andric       return createStringError(errc::invalid_argument,
25481ad6265SDimitry Andric                                "could not find section with name '%s'",
25581ad6265SDimitry Andric                                NewSection.SectionName.str().c_str());
25681ad6265SDimitry Andric     size_t ContentSize = It->getContents().size();
25781ad6265SDimitry Andric     if (!ContentSize)
25881ad6265SDimitry Andric       return createStringError(
25981ad6265SDimitry Andric           errc::invalid_argument,
26081ad6265SDimitry Andric           "section '%s' cannot be updated because it does not have contents",
26181ad6265SDimitry Andric           NewSection.SectionName.str().c_str());
26281ad6265SDimitry Andric     if (ContentSize < NewSection.SectionData->getBufferSize())
26381ad6265SDimitry Andric       return createStringError(
26481ad6265SDimitry Andric           errc::invalid_argument,
26581ad6265SDimitry Andric           "new section cannot be larger than previous section");
26681ad6265SDimitry Andric     It->setOwnedContents({NewSection.SectionData->getBufferStart(),
26781ad6265SDimitry Andric                           NewSection.SectionData->getBufferEnd()});
26881ad6265SDimitry Andric   }
26981ad6265SDimitry Andric 
27081ad6265SDimitry Andric   if (!Config.AddGnuDebugLink.empty())
27181ad6265SDimitry Andric     if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink))
27281ad6265SDimitry Andric       return E;
27381ad6265SDimitry Andric 
27481ad6265SDimitry Andric   if (COFFConfig.Subsystem || COFFConfig.MajorSubsystemVersion ||
27581ad6265SDimitry Andric       COFFConfig.MinorSubsystemVersion) {
27681ad6265SDimitry Andric     if (!Obj.IsPE)
27781ad6265SDimitry Andric       return createStringError(
27881ad6265SDimitry Andric           errc::invalid_argument,
27981ad6265SDimitry Andric           "'" + Config.OutputFilename +
28081ad6265SDimitry Andric               "': unable to set subsystem on a relocatable object file");
28181ad6265SDimitry Andric     if (COFFConfig.Subsystem)
28281ad6265SDimitry Andric       Obj.PeHeader.Subsystem = *COFFConfig.Subsystem;
28381ad6265SDimitry Andric     if (COFFConfig.MajorSubsystemVersion)
28481ad6265SDimitry Andric       Obj.PeHeader.MajorSubsystemVersion = *COFFConfig.MajorSubsystemVersion;
28581ad6265SDimitry Andric     if (COFFConfig.MinorSubsystemVersion)
28681ad6265SDimitry Andric       Obj.PeHeader.MinorSubsystemVersion = *COFFConfig.MinorSubsystemVersion;
28781ad6265SDimitry Andric   }
28881ad6265SDimitry Andric 
28981ad6265SDimitry Andric   return Error::success();
29081ad6265SDimitry Andric }
29181ad6265SDimitry Andric 
29281ad6265SDimitry Andric Error executeObjcopyOnBinary(const CommonConfig &Config,
29381ad6265SDimitry Andric                              const COFFConfig &COFFConfig, COFFObjectFile &In,
29481ad6265SDimitry Andric                              raw_ostream &Out) {
29581ad6265SDimitry Andric   COFFReader Reader(In);
29681ad6265SDimitry Andric   Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create();
29781ad6265SDimitry Andric   if (!ObjOrErr)
29881ad6265SDimitry Andric     return createFileError(Config.InputFilename, ObjOrErr.takeError());
29981ad6265SDimitry Andric   Object *Obj = ObjOrErr->get();
30081ad6265SDimitry Andric   assert(Obj && "Unable to deserialize COFF object");
30181ad6265SDimitry Andric   if (Error E = handleArgs(Config, COFFConfig, *Obj))
30281ad6265SDimitry Andric     return createFileError(Config.InputFilename, std::move(E));
30381ad6265SDimitry Andric   COFFWriter Writer(*Obj, Out);
30481ad6265SDimitry Andric   if (Error E = Writer.write())
30581ad6265SDimitry Andric     return createFileError(Config.OutputFilename, std::move(E));
30681ad6265SDimitry Andric   return Error::success();
30781ad6265SDimitry Andric }
30881ad6265SDimitry Andric 
30981ad6265SDimitry Andric } // end namespace coff
31081ad6265SDimitry Andric } // end namespace objcopy
31181ad6265SDimitry Andric } // end namespace llvm
312