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