1 //===- COFFObjcopy.cpp ----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "COFFObjcopy.h"
11 #include "CopyConfig.h"
15 #include "llvm-objcopy.h"
17 #include "llvm/Object/Binary.h"
18 #include "llvm/Object/COFF.h"
19 #include "llvm/Support/Errc.h"
20 #include "llvm/Support/JamCRC.h"
21 #include "llvm/Support/Path.h"
28 using namespace object
;
31 static bool isDebugSection(const Section
&Sec
) {
32 return Sec
.Name
.startswith(".debug");
35 static uint64_t getNextRVA(const Object
&Obj
) {
36 if (Obj
.getSections().empty())
38 const Section
&Last
= Obj
.getSections().back();
39 return alignTo(Last
.Header
.VirtualAddress
+ Last
.Header
.VirtualSize
,
40 Obj
.IsPE
? Obj
.PeHeader
.SectionAlignment
: 1);
43 static uint32_t getCRC32(StringRef Data
) {
45 CRC
.update(ArrayRef
<char>(Data
.data(), Data
.size()));
46 // The CRC32 value needs to be complemented because the JamCRC dosn't
47 // finalize the CRC32 value. It also dosn't negate the initial CRC32 value
48 // but it starts by default at 0xFFFFFFFF which is the complement of zero.
52 static std::vector
<uint8_t> createGnuDebugLinkSectionContents(StringRef File
) {
53 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> LinkTargetOrErr
=
54 MemoryBuffer::getFile(File
);
56 error("'" + File
+ "': " + LinkTargetOrErr
.getError().message());
57 auto LinkTarget
= std::move(*LinkTargetOrErr
);
58 uint32_t CRC32
= getCRC32(LinkTarget
->getBuffer());
60 StringRef FileName
= sys::path::filename(File
);
61 size_t CRCPos
= alignTo(FileName
.size() + 1, 4);
62 std::vector
<uint8_t> Data(CRCPos
+ 4);
63 memcpy(Data
.data(), FileName
.data(), FileName
.size());
64 support::endian::write32le(Data
.data() + CRCPos
, CRC32
);
68 // Adds named section with given contents to the object.
69 static void addSection(Object
&Obj
, StringRef Name
, ArrayRef
<uint8_t> Contents
,
70 uint32_t Characteristics
) {
71 bool NeedVA
= Characteristics
& (IMAGE_SCN_MEM_EXECUTE
| IMAGE_SCN_MEM_READ
|
75 Sec
.setOwnedContents(Contents
);
77 Sec
.Header
.VirtualSize
= NeedVA
? Sec
.getContents().size() : 0u;
78 Sec
.Header
.VirtualAddress
= NeedVA
? getNextRVA(Obj
) : 0u;
79 Sec
.Header
.SizeOfRawData
=
80 NeedVA
? alignTo(Sec
.Header
.VirtualSize
,
81 Obj
.IsPE
? Obj
.PeHeader
.FileAlignment
: 1)
82 : Sec
.getContents().size();
83 // Sec.Header.PointerToRawData is filled in by the writer.
84 Sec
.Header
.PointerToRelocations
= 0;
85 Sec
.Header
.PointerToLinenumbers
= 0;
86 // Sec.Header.NumberOfRelocations is filled in by the writer.
87 Sec
.Header
.NumberOfLinenumbers
= 0;
88 Sec
.Header
.Characteristics
= Characteristics
;
93 static void addGnuDebugLink(Object
&Obj
, StringRef DebugLinkFile
) {
94 std::vector
<uint8_t> Contents
=
95 createGnuDebugLinkSectionContents(DebugLinkFile
);
96 addSection(Obj
, ".gnu_debuglink", Contents
,
97 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
|
98 IMAGE_SCN_MEM_DISCARDABLE
);
101 static Error
handleArgs(const CopyConfig
&Config
, Object
&Obj
) {
102 // Perform the actual section removals.
103 Obj
.removeSections([&Config
](const Section
&Sec
) {
104 // Contrary to --only-keep-debug, --only-section fully removes sections that
106 if (!Config
.OnlySection
.empty() && !Config
.OnlySection
.matches(Sec
.Name
))
109 if (Config
.StripDebug
|| Config
.StripAll
|| Config
.StripAllGNU
||
110 Config
.DiscardMode
== DiscardType::All
|| Config
.StripUnneeded
) {
111 if (isDebugSection(Sec
) &&
112 (Sec
.Header
.Characteristics
& IMAGE_SCN_MEM_DISCARDABLE
) != 0)
116 if (Config
.ToRemove
.matches(Sec
.Name
))
122 if (Config
.OnlyKeepDebug
) {
123 // For --only-keep-debug, we keep all other sections, but remove their
124 // content. The VirtualSize field in the section header is kept intact.
125 Obj
.truncateSections([](const Section
&Sec
) {
126 return !isDebugSection(Sec
) && Sec
.Name
!= ".buildid" &&
127 ((Sec
.Header
.Characteristics
&
128 (IMAGE_SCN_CNT_CODE
| IMAGE_SCN_CNT_INITIALIZED_DATA
)) != 0);
132 // StripAll removes all symbols and thus also removes all relocations.
133 if (Config
.StripAll
|| Config
.StripAllGNU
)
134 for (Section
&Sec
: Obj
.getMutableSections())
137 // If we need to do per-symbol removals, initialize the Referenced field.
138 if (Config
.StripUnneeded
|| Config
.DiscardMode
== DiscardType::All
||
139 !Config
.SymbolsToRemove
.empty())
140 if (Error E
= Obj
.markSymbols())
143 // Actually do removals of symbols.
144 Obj
.removeSymbols([&](const Symbol
&Sym
) {
145 // For StripAll, all relocations have been stripped and we remove all
147 if (Config
.StripAll
|| Config
.StripAllGNU
)
150 if (Config
.SymbolsToRemove
.matches(Sym
.Name
)) {
151 // Explicitly removing a referenced symbol is an error.
153 reportError(Config
.OutputFilename
,
154 createStringError(llvm::errc::invalid_argument
,
155 "not stripping symbol '%s' because it is "
156 "named in a relocation",
157 Sym
.Name
.str().c_str()));
161 if (!Sym
.Referenced
) {
162 // With --strip-unneeded, GNU objcopy removes all unreferenced local
163 // symbols, and any unreferenced undefined external.
164 // With --strip-unneeded-symbol we strip only specific unreferenced
165 // local symbol instead of removing all of such.
166 if (Sym
.Sym
.StorageClass
== IMAGE_SYM_CLASS_STATIC
||
167 Sym
.Sym
.SectionNumber
== 0)
168 if (Config
.StripUnneeded
||
169 Config
.UnneededSymbolsToRemove
.matches(Sym
.Name
))
172 // GNU objcopy keeps referenced local symbols and external symbols
173 // if --discard-all is set, similar to what --strip-unneeded does,
174 // but undefined local symbols are kept when --discard-all is set.
175 if (Config
.DiscardMode
== DiscardType::All
&&
176 Sym
.Sym
.StorageClass
== IMAGE_SYM_CLASS_STATIC
&&
177 Sym
.Sym
.SectionNumber
!= 0)
184 for (const auto &Flag
: Config
.AddSection
) {
185 StringRef SecName
, FileName
;
186 std::tie(SecName
, FileName
) = Flag
.split("=");
188 auto BufOrErr
= MemoryBuffer::getFile(FileName
);
190 return createFileError(FileName
, errorCodeToError(BufOrErr
.getError()));
191 auto Buf
= std::move(*BufOrErr
);
195 makeArrayRef(reinterpret_cast<const uint8_t *>(Buf
->getBufferStart()),
196 Buf
->getBufferSize()),
197 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_ALIGN_1BYTES
);
200 if (!Config
.AddGnuDebugLink
.empty())
201 addGnuDebugLink(Obj
, Config
.AddGnuDebugLink
);
203 if (Config
.AllowBrokenLinks
|| !Config
.BuildIdLinkDir
.empty() ||
204 Config
.BuildIdLinkInput
|| Config
.BuildIdLinkOutput
||
205 !Config
.SplitDWO
.empty() || !Config
.SymbolsPrefix
.empty() ||
206 !Config
.AllocSectionsPrefix
.empty() || !Config
.DumpSection
.empty() ||
207 !Config
.KeepSection
.empty() || Config
.NewSymbolVisibility
||
208 !Config
.SymbolsToGlobalize
.empty() || !Config
.SymbolsToKeep
.empty() ||
209 !Config
.SymbolsToLocalize
.empty() || !Config
.SymbolsToWeaken
.empty() ||
210 !Config
.SymbolsToKeepGlobal
.empty() || !Config
.SectionsToRename
.empty() ||
211 !Config
.SetSectionFlags
.empty() || !Config
.SymbolsToRename
.empty() ||
212 Config
.ExtractDWO
|| Config
.KeepFileSymbols
|| Config
.LocalizeHidden
||
213 Config
.PreserveDates
|| Config
.StripDWO
|| Config
.StripNonAlloc
||
214 Config
.StripSections
|| Config
.Weaken
|| Config
.DecompressDebugSections
||
215 Config
.DiscardMode
== DiscardType::Locals
||
216 !Config
.SymbolsToAdd
.empty() || Config
.EntryExpr
) {
217 return createStringError(llvm::errc::invalid_argument
,
218 "option not supported by llvm-objcopy for COFF");
221 return Error::success();
224 Error
executeObjcopyOnBinary(const CopyConfig
&Config
, COFFObjectFile
&In
,
226 COFFReader
Reader(In
);
227 Expected
<std::unique_ptr
<Object
>> ObjOrErr
= Reader
.create();
229 return createFileError(Config
.InputFilename
, ObjOrErr
.takeError());
230 Object
*Obj
= ObjOrErr
->get();
231 assert(Obj
&& "Unable to deserialize COFF object");
232 if (Error E
= handleArgs(Config
, *Obj
))
233 return createFileError(Config
.InputFilename
, std::move(E
));
234 COFFWriter
Writer(*Obj
, Out
);
235 if (Error E
= Writer
.write())
236 return createFileError(Config
.OutputFilename
, std::move(E
));
237 return Error::success();
240 } // end namespace coff
241 } // end namespace objcopy
242 } // end namespace llvm