Skip to content

Commit 2bdcfc7

Browse files
authored
[DirectX] Add extract-section to llvm-objcopy and implement it for DXContainer (#154804)
This pr adds the `extract-section` option to `llvm-objcopy` as a common option. It differs from `dump-section` as it will produce a standalone object with just one section, as opposed to just the section contents. For more context as to other options considered, see #153265 (comment). This difference in behaviour is used for DXC compatibility with `extract-rootsignature` and `/Frs`. This pr then implements this functionality for `DXContainer` objects. This is the second step of #150277 to implement as a compiler action that invokes `llvm-objcopy` for functionality. This also completes the implementation of `extract-rootsignature` as described in #149560.
1 parent 8f16af3 commit 2bdcfc7

File tree

10 files changed

+276
-8
lines changed

10 files changed

+276
-8
lines changed

llvm/docs/CommandGuide/llvm-objcopy.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ multiple file formats.
7979
Enable deterministic mode when copying archives, i.e. use 0 for archive member
8080
header UIDs, GIDs and timestamp fields. On by default.
8181

82+
.. option:: --extract-section <section>=<file>
83+
84+
Extract the specified section ``<section>`` into the file ``<file>`` as a
85+
seperate object. Can be specified multiple times to extract multiple sections.
86+
``<file>`` is unrelated to the input and output files provided to
87+
:program:`llvm-objcopy` and as such the normal copying and editing
88+
operations will still be performed. No operations are performed on the sections
89+
prior to dumping them.
90+
8291
.. option:: --globalize-symbol <symbol>
8392

8493
Mark any defined symbols named ``<symbol>`` as global symbols in the output.

llvm/include/llvm/ObjCopy/CommonConfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ struct CommonConfig {
233233
SmallVector<StringRef, 0> DumpSection;
234234
SmallVector<NewSectionInfo, 0> UpdateSection;
235235
SmallVector<SectionPatternAddressUpdate, 0> ChangeSectionAddress;
236+
SmallVector<StringRef, 0> ExtractSection;
236237

237238
// Section matchers
238239
NameMatcher KeepSection;

llvm/include/llvm/ObjCopy/ConfigManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct LLVM_ABI ConfigManager : public MultiFormatConfig {
2727

2828
const CommonConfig &getCommonConfig() const override { return Common; }
2929

30-
Expected<const ELFConfig &> getELFConfig() const override { return ELF; }
30+
Expected<const ELFConfig &> getELFConfig() const override;
3131

3232
Expected<const COFFConfig &> getCOFFConfig() const override;
3333

llvm/lib/ObjCopy/ConfigManager.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
using namespace llvm;
1414
using namespace llvm::objcopy;
1515

16+
Expected<const ELFConfig &> ConfigManager::getELFConfig() const {
17+
if (!Common.ExtractSection.empty())
18+
return createStringError(llvm::errc::invalid_argument,
19+
"option is not supported for ELF");
20+
return ELF;
21+
}
22+
1623
Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const {
1724
if (!Common.SplitDWO.empty() || !Common.SymbolsPrefix.empty() ||
1825
!Common.SymbolsPrefixRemove.empty() || !Common.SymbolsToSkip.empty() ||
@@ -27,7 +34,7 @@ Expected<const COFFConfig &> ConfigManager::getCOFFConfig() const {
2734
Common.DiscardMode == DiscardType::Locals ||
2835
!Common.SymbolsToAdd.empty() || Common.GapFill != 0 ||
2936
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0 ||
30-
!Common.ChangeSectionAddress.empty())
37+
!Common.ChangeSectionAddress.empty() || !Common.ExtractSection.empty())
3138
return createStringError(llvm::errc::invalid_argument,
3239
"option is not supported for COFF");
3340

@@ -48,7 +55,7 @@ Expected<const MachOConfig &> ConfigManager::getMachOConfig() const {
4855
Common.DiscardMode == DiscardType::Locals ||
4956
!Common.SymbolsToAdd.empty() || Common.GapFill != 0 ||
5057
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0 ||
51-
!Common.ChangeSectionAddress.empty())
58+
!Common.ChangeSectionAddress.empty() || !Common.ExtractSection.empty())
5259
return createStringError(llvm::errc::invalid_argument,
5360
"option is not supported for MachO");
5461

@@ -69,7 +76,7 @@ Expected<const WasmConfig &> ConfigManager::getWasmConfig() const {
6976
!Common.SetSectionFlags.empty() || !Common.SetSectionType.empty() ||
7077
!Common.SymbolsToRename.empty() || Common.GapFill != 0 ||
7178
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0 ||
72-
!Common.ChangeSectionAddress.empty())
79+
!Common.ChangeSectionAddress.empty() || !Common.ExtractSection.empty())
7380
return createStringError(llvm::errc::invalid_argument,
7481
"only flags for section dumping, removal, and "
7582
"addition are supported");
@@ -99,7 +106,7 @@ Expected<const XCOFFConfig &> ConfigManager::getXCOFFConfig() const {
99106
Common.Weaken || Common.StripUnneeded || Common.DecompressDebugSections ||
100107
Common.GapFill != 0 || Common.PadTo != 0 ||
101108
Common.ChangeSectionLMAValAll != 0 ||
102-
!Common.ChangeSectionAddress.empty()) {
109+
!Common.ChangeSectionAddress.empty() || !Common.ExtractSection.empty()) {
103110
return createStringError(
104111
llvm::errc::invalid_argument,
105112
"no flags are supported yet, only basic copying is allowed");
@@ -124,9 +131,8 @@ ConfigManager::getDXContainerConfig() const {
124131
Common.DecompressDebugSections || Common.GapFill != 0 ||
125132
Common.PadTo != 0 || Common.ChangeSectionLMAValAll != 0 ||
126133
!Common.ChangeSectionAddress.empty()) {
127-
return createStringError(
128-
llvm::errc::invalid_argument,
129-
"no flags are supported yet, only basic copying is allowed");
134+
return createStringError(llvm::errc::invalid_argument,
135+
"option is not supported for DXContainer");
130136
}
131137
return DXContainer;
132138
}

llvm/lib/ObjCopy/DXContainer/DXContainerObjcopy.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,48 @@
1111
#include "DXContainerWriter.h"
1212
#include "llvm/ObjCopy/CommonConfig.h"
1313
#include "llvm/ObjCopy/DXContainer/DXContainerConfig.h"
14+
#include "llvm/Support/raw_ostream.h"
1415

1516
namespace llvm {
1617
namespace objcopy {
1718
namespace dxbc {
1819

1920
using namespace object;
2021

22+
static Error extractPartAsObject(StringRef PartName, StringRef OutFilename,
23+
StringRef InputFilename, const Object &Obj) {
24+
for (const Part &P : Obj.Parts)
25+
if (P.Name == PartName) {
26+
Object PartObj;
27+
PartObj.Header = Obj.Header;
28+
PartObj.Parts.push_back({P.Name, P.Data});
29+
PartObj.recomputeHeader();
30+
31+
auto Write = [&OutFilename, &PartObj](raw_ostream &Out) -> Error {
32+
DXContainerWriter Writer(PartObj, Out);
33+
if (Error E = Writer.write())
34+
return createFileError(OutFilename, std::move(E));
35+
return Error::success();
36+
};
37+
38+
return writeToOutput(OutFilename, Write);
39+
}
40+
41+
return createFileError(InputFilename, object_error::parse_failed,
42+
"part '%s' not found", PartName.str().c_str());
43+
}
44+
2145
static Error handleArgs(const CommonConfig &Config, Object &Obj) {
46+
// Extract all sections before any modifications.
47+
for (StringRef Flag : Config.ExtractSection) {
48+
StringRef SectionName;
49+
StringRef FileName;
50+
std::tie(SectionName, FileName) = Flag.split('=');
51+
if (Error E = extractPartAsObject(SectionName, FileName,
52+
Config.InputFilename, Obj))
53+
return E;
54+
}
55+
2256
std::function<bool(const Part &)> RemovePred = [](const Part &) {
2357
return false;
2458
};
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
## Tests that a separate DXContainer is created for the RTS0 (root signature)
2+
## part, when--extract-section is specified.
3+
4+
# RUN: yaml2obj %s -o %t
5+
# RUN: llvm-objcopy %t --extract-section=RTS0=%t.rts0.out
6+
# RUN: obj2yaml %t.rts0.out | FileCheck %s --implicit-check-not=Name:
7+
8+
## The DXContainer described below was generated with:
9+
10+
## `clang-dxc -T cs_6_7 test.hlsl /Fo temp.dxo`
11+
## `obj2yaml temp.dxo`
12+
13+
## and has the DXIL section trimmed for readability.
14+
15+
## ``` test.hlsl
16+
## [RootSignature("")]
17+
## [numthreads(1,1,1)]
18+
## void main() {}
19+
## ```
20+
21+
# CHECK: Header:
22+
# CHECK-NEXT: Hash:
23+
# CHECK: Version:
24+
# CHECK-NEXT: Major: 1
25+
# CHECK-NEXT: Minor: 0
26+
# CHECK-NEXT: FileSize: 68
27+
# CHECK-NEXT: PartCount: 1
28+
# CHECK-NEXT: PartOffsets: [ 36 ]
29+
# CHECK-NEXT: Parts:
30+
# CHECK-NEXT: Name: RTS0
31+
# CHECK-NEXT Size: 24
32+
# CHECK-NEXT RootSignature:
33+
# CHECK-NEXT Version: 2
34+
# CHECK-NEXT NumRootParameters: 0
35+
# CHECK-NEXT RootParametersOffset: 24
36+
# CHECK-NEXT NumStaticSamplers: 0
37+
# CHECK-NEXT StaticSamplersOffset: 24
38+
# CHECK-NEXT Parameters: []
39+
40+
--- !dxcontainer
41+
Header:
42+
Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
43+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
44+
Version:
45+
Major: 1
46+
Minor: 0
47+
FileSize: 1984
48+
PartCount: 7
49+
PartOffsets: [ 60, 1792, 1808, 1836, 1852, 1868, 1900 ]
50+
Parts:
51+
- Name: DXIL
52+
Size: 1724
53+
Program:
54+
MajorVersion: 6
55+
MinorVersion: 7
56+
ShaderKind: 5
57+
Size: 28
58+
DXILMajorVersion: 1
59+
DXILMinorVersion: 7
60+
DXILSize: 4
61+
DXIL: [ 0x42, 0x43, 0xC0, 0xDE, ]
62+
- Name: SFI0
63+
Size: 8
64+
- Name: HASH
65+
Size: 20
66+
Hash:
67+
IncludesSource: false
68+
Digest: [ 0x9F, 0xD1, 0xD9, 0xE2, 0x49, 0xFB, 0x3A, 0x6C,
69+
0x8C, 0x14, 0x8A, 0x96, 0x1C, 0x7D, 0x85, 0xA9 ]
70+
- Name: ISG1
71+
Size: 8
72+
Signature:
73+
Parameters: []
74+
- Name: OSG1
75+
Size: 8
76+
Signature:
77+
Parameters: []
78+
- Name: RTS0
79+
Size: 24
80+
RootSignature:
81+
Version: 2
82+
NumRootParameters: 0
83+
RootParametersOffset: 24
84+
NumStaticSamplers: 0
85+
StaticSamplersOffset: 24
86+
Parameters: []
87+
- Name: PSV0
88+
Size: 76
89+
PSVInfo:
90+
Version: 3
91+
ShaderStage: 5
92+
MinimumWaveLaneCount: 0
93+
MaximumWaveLaneCount: 4294967295
94+
UsesViewID: 0
95+
SigInputVectors: 0
96+
SigOutputVectors: [ 0, 0, 0, 0 ]
97+
NumThreadsX: 1
98+
NumThreadsY: 1
99+
NumThreadsZ: 1
100+
EntryName: main
101+
ResourceStride: 24
102+
Resources: []
103+
SigInputElements: []
104+
SigOutputElements: []
105+
SigPatchOrPrimElements: []
106+
InputOutputMap:
107+
- [ ]
108+
- [ ]
109+
- [ ]
110+
- [ ]
111+
...
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## Check that llvm-objcopy reports a suitable error when it can't find the
2+
## section to extract.
3+
4+
## We can't extract a part that doesn't exist.
5+
# RUN: yaml2obj %s --docnum=1 -o %t1
6+
# RUN: not llvm-objcopy %t1 --extract-section=UNKNOWN=%t.unknown.out 2>&1 | FileCheck %s -DFILE=%t1 --check-prefix=ERROR1
7+
8+
# ERROR1: error: '[[FILE]]': part 'UNKNOWN' not found
9+
10+
--- !dxcontainer
11+
Header:
12+
Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
13+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
14+
Version:
15+
Major: 1
16+
Minor: 0
17+
PartCount: 1
18+
Parts:
19+
- Name: FKE0
20+
Size: 8
21+
...
22+
23+
## We can't extract a part that is specified incorrectly.
24+
# RUN: yaml2obj %s --docnum=2 -o %t2
25+
# RUN: not llvm-objcopy %t2 --extract-section=FKE0,%t.fke0.out 2>&1 | FileCheck %s -DFILE=%t2 --check-prefix=ERROR2
26+
27+
# ERROR2: error: bad format for --extract-section, expected section=file
28+
29+
--- !dxcontainer
30+
Header:
31+
Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
32+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
33+
Version:
34+
Major: 1
35+
Minor: 0
36+
PartCount: 1
37+
Parts:
38+
- Name: FKE0
39+
Size: 8
40+
...
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
## Tests that a separate DXContainer is created with only the specified section
2+
## for each --extract-section specified.
3+
4+
# RUN: yaml2obj %s -o %t
5+
# RUN: llvm-objcopy %t --extract-section=FKE1=%t.fke1.out --extract-section=FKE4=%t.fke4.out
6+
# RUN: obj2yaml %t.fke1.out | FileCheck %s --check-prefixes=CHECK,FKE1 --implicit-check-not=Name:
7+
# RUN: obj2yaml %t.fke4.out | FileCheck %s --check-prefixes=CHECK,FKE4 --implicit-check-not=Name:
8+
9+
# FKE1: FileSize: 52
10+
# FKE4: FileSize: 1732
11+
# CHECK-NEXT: PartCount: 1
12+
# CHECK-NEXT: PartOffsets: [ 36 ]
13+
# CHECK-NEXT: Parts:
14+
# FKE1-NEXT: Name: FKE1
15+
# FKE1-NEXT: Size: 8
16+
# FKE4-NEXT: Name: FKE4
17+
# FKE4-NEXT: Size: 1688
18+
19+
--- !dxcontainer
20+
Header:
21+
Hash: [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
22+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
23+
Version:
24+
Major: 1
25+
Minor: 0
26+
FileSize: 1996
27+
PartCount: 7
28+
PartOffsets: [ 60, 76, 92, 108, 236, 1932, 1960 ]
29+
Parts:
30+
- Name: FKE0
31+
Size: 8
32+
- Name: FKE1
33+
Size: 8
34+
- Name: FKE2
35+
Size: 8
36+
- Name: FKE3
37+
Size: 120
38+
- Name: FKE4
39+
Size: 1688
40+
- Name: FKE5
41+
Size: 20
42+
- Name: DXIL
43+
Size: 28
44+
Program:
45+
MajorVersion: 6
46+
MinorVersion: 5
47+
ShaderKind: 5
48+
Size: 8
49+
DXILMajorVersion: 1
50+
DXILMinorVersion: 5
51+
DXILSize: 4
52+
DXIL: [ 0x42, 0x43, 0xC0, 0xDE, ]
53+
...

llvm/tools/llvm-objcopy/CommonOpts.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ def enable_deterministic_archives
2323
: Flag<["--"], "enable-deterministic-archives">,
2424
HelpText<"Enable deterministic mode when operating on archives (use "
2525
"zero for UIDs, GIDs, and timestamps).">;
26+
27+
defm extract_section
28+
: Eq<"extract-section",
29+
"Extract section named <section> into standalone object in file <file>">,
30+
MetaVarName<"section=file">;
31+
2632
def D : Flag<["-"], "D">,
2733
Alias<enable_deterministic_archives>,
2834
HelpText<"Alias for --enable-deterministic-archives">;

llvm/tools/llvm-objcopy/ObjcopyOptions.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,14 @@ objcopy::parseObjcopyOptions(ArrayRef<const char *> ArgsArr,
10821082
"bad format for --dump-section, expected section=file");
10831083
Config.DumpSection.push_back(Value);
10841084
}
1085+
for (auto *Arg : InputArgs.filtered(OBJCOPY_extract_section)) {
1086+
StringRef Value(Arg->getValue());
1087+
if (Value.split('=').second.empty())
1088+
return createStringError(
1089+
errc::invalid_argument,
1090+
"bad format for --extract-section, expected section=file");
1091+
Config.ExtractSection.push_back(Value);
1092+
}
10851093
Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all);
10861094
Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu);
10871095
Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug);

0 commit comments

Comments
 (0)