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 "COFFObjcopy.h"
10 #include "Buffer.h"
11 #include "CopyConfig.h"
12 #include "Object.h"
13 #include "Reader.h"
14 #include "Writer.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 
isDebugSection(const Section & Sec)30 static bool isDebugSection(const Section &Sec) {
31   return Sec.Name.startswith(".debug");
32 }
33 
getNextRVA(const Object & Obj)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>>
createGnuDebugLinkSectionContents(StringRef File)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.
addSection(Object & Obj,StringRef Name,ArrayRef<uint8_t> Contents,uint32_t Characteristics)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 
addGnuDebugLink(Object & Obj,StringRef DebugLinkFile)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 
setSectionFlags(Section & Sec,SectionFlag AllFlags)97 static void setSectionFlags(Section &Sec, SectionFlag AllFlags) {
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 =
111       (Sec.Header.Characteristics & 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   Sec.Header.Characteristics = NewCharacteristics;
132 }
133 
handleArgs(const CopyConfig & Config,Object & Obj)134 static Error handleArgs(const CopyConfig &Config, 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         setSectionFlags(Sec, It->second.NewFlags);
230     }
231 
232   for (const auto &Flag : Config.AddSection) {
233     StringRef SecName, FileName;
234     std::tie(SecName, FileName) = Flag.split("=");
235 
236     auto BufOrErr = MemoryBuffer::getFile(FileName);
237     if (!BufOrErr)
238       return createFileError(FileName, errorCodeToError(BufOrErr.getError()));
239     auto Buf = std::move(*BufOrErr);
240 
241     addSection(
242         Obj, SecName,
243         makeArrayRef(reinterpret_cast<const uint8_t *>(Buf->getBufferStart()),
244                      Buf->getBufferSize()),
245         IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES);
246   }
247 
248   if (!Config.AddGnuDebugLink.empty())
249     if (Error E = addGnuDebugLink(Obj, Config.AddGnuDebugLink))
250       return E;
251 
252   if (Config.AllowBrokenLinks || !Config.BuildIdLinkDir.empty() ||
253       Config.BuildIdLinkInput || Config.BuildIdLinkOutput ||
254       !Config.SplitDWO.empty() || !Config.SymbolsPrefix.empty() ||
255       !Config.AllocSectionsPrefix.empty() || !Config.DumpSection.empty() ||
256       !Config.KeepSection.empty() || Config.NewSymbolVisibility ||
257       !Config.SymbolsToGlobalize.empty() || !Config.SymbolsToKeep.empty() ||
258       !Config.SymbolsToLocalize.empty() || !Config.SymbolsToWeaken.empty() ||
259       !Config.SymbolsToKeepGlobal.empty() || !Config.SectionsToRename.empty() ||
260       !Config.SetSectionAlignment.empty() || Config.ExtractDWO ||
261       Config.LocalizeHidden || Config.PreserveDates || Config.StripDWO ||
262       Config.StripNonAlloc || Config.StripSections ||
263       Config.StripSwiftSymbols || Config.Weaken ||
264       Config.DecompressDebugSections ||
265       Config.DiscardMode == DiscardType::Locals ||
266       !Config.SymbolsToAdd.empty() || Config.EntryExpr) {
267     return createStringError(llvm::errc::invalid_argument,
268                              "option not supported by llvm-objcopy for COFF");
269   }
270 
271   return Error::success();
272 }
273 
executeObjcopyOnBinary(const CopyConfig & Config,COFFObjectFile & In,Buffer & Out)274 Error executeObjcopyOnBinary(const CopyConfig &Config, COFFObjectFile &In,
275                              Buffer &Out) {
276   COFFReader Reader(In);
277   Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create();
278   if (!ObjOrErr)
279     return createFileError(Config.InputFilename, ObjOrErr.takeError());
280   Object *Obj = ObjOrErr->get();
281   assert(Obj && "Unable to deserialize COFF object");
282   if (Error E = handleArgs(Config, *Obj))
283     return createFileError(Config.InputFilename, std::move(E));
284   COFFWriter Writer(*Obj, Out);
285   if (Error E = Writer.write())
286     return createFileError(Config.OutputFilename, std::move(E));
287   return Error::success();
288 }
289 
290 } // end namespace coff
291 } // end namespace objcopy
292 } // end namespace llvm
293