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 "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"
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"
28 using namespace object
;
31 static bool isDebugSection(const Section
&Sec
) {
32 return Sec
.Name
.starts_with(".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 Expected
<std::vector
<uint8_t>>
44 createGnuDebugLinkSectionContents(StringRef File
) {
45 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> LinkTargetOrErr
=
46 MemoryBuffer::getFile(File
);
48 return createFileError(File
, LinkTargetOrErr
.getError());
49 auto LinkTarget
= std::move(*LinkTargetOrErr
);
50 uint32_t CRC32
= llvm::crc32(arrayRefFromStringRef(LinkTarget
->getBuffer()));
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
);
60 // Adds named section with given contents to the object.
61 static 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
|
67 Sec
.setOwnedContents(Contents
);
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
;
85 static Error
addGnuDebugLink(Object
&Obj
, StringRef DebugLinkFile
) {
86 Expected
<std::vector
<uint8_t>> Contents
=
87 createGnuDebugLinkSectionContents(DebugLinkFile
);
89 return Contents
.takeError();
91 addSection(Obj
, ".gnu_debuglink", *Contents
,
92 IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_MEM_READ
|
93 IMAGE_SCN_MEM_DISCARDABLE
);
95 return Error::success();
98 static 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
;
109 // Setup new section characteristics based on the flags provided in command
111 uint32_t NewCharacteristics
= (OldChar
& PreserveMask
) | IMAGE_SCN_MEM_READ
;
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
;
131 return NewCharacteristics
;
134 static Error
dumpSection(Object
&O
, StringRef SectionName
, StringRef FileName
) {
135 for (const coff::Section
&Section
: O
.getSections()) {
136 if (Section
.Name
!= SectionName
)
139 ArrayRef
<uint8_t> Contents
= Section
.getContents();
141 std::unique_ptr
<FileOutputBuffer
> Buffer
;
142 if (auto B
= FileOutputBuffer::create(FileName
, Contents
.size()))
143 Buffer
= std::move(*B
);
145 return B
.takeError();
147 llvm::copy(Contents
, Buffer
->getBufferStart());
148 if (Error E
= Buffer
->commit())
151 return Error::success();
153 return createStringError(object_error::parse_failed
, "section '%s' not found",
154 SectionName
.str().c_str());
157 static 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
))
165 // Perform the actual section removals.
166 Obj
.removeSections([&Config
](const Section
&Sec
) {
167 // Contrary to --only-keep-debug, --only-section fully removes sections that
169 if (!Config
.OnlySection
.empty() && !Config
.OnlySection
.matches(Sec
.Name
))
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)
179 if (Config
.ToRemove
.matches(Sec
.Name
))
185 if (Config
.OnlyKeepDebug
) {
186 // For --only-keep-debug, we keep all other sections, but remove their
187 // content. The VirtualSize field in the section header is kept intact.
188 Obj
.truncateSections([](const Section
&Sec
) {
189 return !isDebugSection(Sec
) && Sec
.Name
!= ".buildid" &&
190 ((Sec
.Header
.Characteristics
&
191 (IMAGE_SCN_CNT_CODE
| IMAGE_SCN_CNT_INITIALIZED_DATA
)) != 0);
195 // StripAll removes all symbols and thus also removes all relocations.
196 if (Config
.StripAll
|| Config
.StripAllGNU
)
197 for (Section
&Sec
: Obj
.getMutableSections())
200 // If we need to do per-symbol removals, initialize the Referenced field.
201 if (Config
.StripUnneeded
|| Config
.DiscardMode
== DiscardType::All
||
202 !Config
.SymbolsToRemove
.empty())
203 if (Error E
= Obj
.markSymbols())
206 for (Symbol
&Sym
: Obj
.getMutableSymbols()) {
207 auto I
= Config
.SymbolsToRename
.find(Sym
.Name
);
208 if (I
!= Config
.SymbolsToRename
.end())
209 Sym
.Name
= I
->getValue();
212 auto ToRemove
= [&](const Symbol
&Sym
) -> Expected
<bool> {
213 // For StripAll, all relocations have been stripped and we remove all
215 if (Config
.StripAll
|| Config
.StripAllGNU
)
218 if (Config
.SymbolsToRemove
.matches(Sym
.Name
)) {
219 // Explicitly removing a referenced symbol is an error.
221 return createStringError(
222 llvm::errc::invalid_argument
,
223 "'" + Config
.OutputFilename
+ "': not stripping symbol '" +
224 Sym
.Name
.str() + "' because it is named in a relocation");
228 if (!Sym
.Referenced
) {
229 // With --strip-unneeded, GNU objcopy removes all unreferenced local
230 // symbols, and any unreferenced undefined external.
231 // With --strip-unneeded-symbol we strip only specific unreferenced
232 // local symbol instead of removing all of such.
233 if (Sym
.Sym
.StorageClass
== IMAGE_SYM_CLASS_STATIC
||
234 Sym
.Sym
.SectionNumber
== 0)
235 if (Config
.StripUnneeded
||
236 Config
.UnneededSymbolsToRemove
.matches(Sym
.Name
))
239 // GNU objcopy keeps referenced local symbols and external symbols
240 // if --discard-all is set, similar to what --strip-unneeded does,
241 // but undefined local symbols are kept when --discard-all is set.
242 if (Config
.DiscardMode
== DiscardType::All
&&
243 Sym
.Sym
.StorageClass
== IMAGE_SYM_CLASS_STATIC
&&
244 Sym
.Sym
.SectionNumber
!= 0)
251 // Actually do removals of symbols.
252 if (Error Err
= Obj
.removeSymbols(ToRemove
))
255 if (!Config
.SetSectionFlags
.empty())
256 for (Section
&Sec
: Obj
.getMutableSections()) {
257 const auto It
= Config
.SetSectionFlags
.find(Sec
.Name
);
258 if (It
!= Config
.SetSectionFlags
.end())
259 Sec
.Header
.Characteristics
= flagsToCharacteristics(
260 It
->second
.NewFlags
, Sec
.Header
.Characteristics
);
263 for (const NewSectionInfo
&NewSection
: Config
.AddSection
) {
264 uint32_t Characteristics
;
265 const auto It
= Config
.SetSectionFlags
.find(NewSection
.SectionName
);
266 if (It
!= Config
.SetSectionFlags
.end())
267 Characteristics
= flagsToCharacteristics(It
->second
.NewFlags
, 0);
269 Characteristics
= IMAGE_SCN_CNT_INITIALIZED_DATA
| IMAGE_SCN_ALIGN_1BYTES
;
271 addSection(Obj
, NewSection
.SectionName
,
272 ArrayRef(reinterpret_cast<const uint8_t *>(
273 NewSection
.SectionData
->getBufferStart()),
274 NewSection
.SectionData
->getBufferSize()),
278 for (const NewSectionInfo
&NewSection
: Config
.UpdateSection
) {
279 auto It
= llvm::find_if(Obj
.getMutableSections(), [&](auto &Sec
) {
280 return Sec
.Name
== NewSection
.SectionName
;
282 if (It
== Obj
.getMutableSections().end())
283 return createStringError(errc::invalid_argument
,
284 "could not find section with name '%s'",
285 NewSection
.SectionName
.str().c_str());
286 size_t ContentSize
= It
->getContents().size();
288 return createStringError(
289 errc::invalid_argument
,
290 "section '%s' cannot be updated because it does not have contents",
291 NewSection
.SectionName
.str().c_str());
292 if (ContentSize
< NewSection
.SectionData
->getBufferSize())
293 return createStringError(
294 errc::invalid_argument
,
295 "new section cannot be larger than previous section");
296 It
->setOwnedContents({NewSection
.SectionData
->getBufferStart(),
297 NewSection
.SectionData
->getBufferEnd()});
300 if (!Config
.AddGnuDebugLink
.empty())
301 if (Error E
= addGnuDebugLink(Obj
, Config
.AddGnuDebugLink
))
304 if (COFFConfig
.Subsystem
|| COFFConfig
.MajorSubsystemVersion
||
305 COFFConfig
.MinorSubsystemVersion
) {
307 return createStringError(
308 errc::invalid_argument
,
309 "'" + Config
.OutputFilename
+
310 "': unable to set subsystem on a relocatable object file");
311 if (COFFConfig
.Subsystem
)
312 Obj
.PeHeader
.Subsystem
= *COFFConfig
.Subsystem
;
313 if (COFFConfig
.MajorSubsystemVersion
)
314 Obj
.PeHeader
.MajorSubsystemVersion
= *COFFConfig
.MajorSubsystemVersion
;
315 if (COFFConfig
.MinorSubsystemVersion
)
316 Obj
.PeHeader
.MinorSubsystemVersion
= *COFFConfig
.MinorSubsystemVersion
;
319 return Error::success();
322 Error
executeObjcopyOnBinary(const CommonConfig
&Config
,
323 const COFFConfig
&COFFConfig
, COFFObjectFile
&In
,
325 COFFReader
Reader(In
);
326 Expected
<std::unique_ptr
<Object
>> ObjOrErr
= Reader
.create();
328 return createFileError(Config
.InputFilename
, ObjOrErr
.takeError());
329 Object
*Obj
= ObjOrErr
->get();
330 assert(Obj
&& "Unable to deserialize COFF object");
331 if (Error E
= handleArgs(Config
, COFFConfig
, *Obj
))
332 return createFileError(Config
.InputFilename
, std::move(E
));
333 COFFWriter
Writer(*Obj
, Out
);
334 if (Error E
= Writer
.write())
335 return createFileError(Config
.OutputFilename
, std::move(E
));
336 return Error::success();
339 } // end namespace coff
340 } // end namespace objcopy
341 } // end namespace llvm