1 //===- yaml2xcoff - Convert YAML to a xcoff object file -------------------===//
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 //===----------------------------------------------------------------------===//
10 /// The xcoff component of yaml2obj.
12 //===----------------------------------------------------------------------===//
14 #include "llvm/ADT/DenseMap.h"
15 #include "llvm/BinaryFormat/XCOFF.h"
16 #include "llvm/MC/StringTableBuilder.h"
17 #include "llvm/Object/XCOFFObjectFile.h"
18 #include "llvm/ObjectYAML/ObjectYAML.h"
19 #include "llvm/ObjectYAML/yaml2obj.h"
20 #include "llvm/Support/EndianStream.h"
21 #include "llvm/Support/raw_ostream.h"
22 #include "llvm/Support/LEB128.h"
28 constexpr unsigned DefaultSectionAlign
= 4;
29 constexpr int16_t MaxSectionIndex
= INT16_MAX
;
30 constexpr uint32_t MaxRawDataSize
= UINT32_MAX
;
34 XCOFFWriter(XCOFFYAML::Object
&Obj
, raw_ostream
&OS
, yaml::ErrorHandler EH
)
35 : Obj(Obj
), W(OS
, support::big
), ErrHandler(EH
),
36 Strings(StringTableBuilder::XCOFF
) {
37 Is64Bit
= Obj
.Header
.Magic
== (llvm::yaml::Hex16
)XCOFF::XCOFF64
;
42 bool nameShouldBeInStringTable(StringRef SymbolName
);
43 bool initFileHeader(uint64_t CurrentOffset
);
44 bool initSectionHeader(uint64_t &CurrentOffset
);
45 bool initRelocations(uint64_t &CurrentOffset
);
46 bool assignAddressesAndIndices();
47 void writeFileHeader();
48 void writeSectionHeader();
49 bool writeSectionData();
50 bool writeRelocations();
53 XCOFFYAML::Object
&Obj
;
55 support::endian::Writer W
;
56 yaml::ErrorHandler ErrHandler
;
57 StringTableBuilder Strings
;
59 // Map the section name to its corrresponding section index.
60 DenseMap
<StringRef
, int16_t> SectionIndexMap
= {
61 {StringRef("N_DEBUG"), XCOFF::N_DEBUG
},
62 {StringRef("N_ABS"), XCOFF::N_ABS
},
63 {StringRef("N_UNDEF"), XCOFF::N_UNDEF
}};
64 XCOFFYAML::FileHeader InitFileHdr
= Obj
.Header
;
65 std::vector
<XCOFFYAML::Section
> InitSections
= Obj
.Sections
;
68 static void writeName(StringRef StrName
, support::endian::Writer W
) {
69 char Name
[XCOFF::NameSize
];
70 memset(Name
, 0, XCOFF::NameSize
);
72 memcpy(Name
, StrName
.size() ? StrName
.data() : SrcName
, StrName
.size());
73 ArrayRef
<char> NameRef(Name
, XCOFF::NameSize
);
77 bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName
) {
78 // For XCOFF64: The symbol name is always in the string table.
79 return (SymbolName
.size() > XCOFF::NameSize
) || Is64Bit
;
82 bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset
) {
83 for (uint16_t I
= 0, E
= InitSections
.size(); I
< E
; ++I
) {
84 if (!InitSections
[I
].Relocations
.empty()) {
85 InitSections
[I
].NumberOfRelocations
= InitSections
[I
].Relocations
.size();
86 InitSections
[I
].FileOffsetToRelocations
= CurrentOffset
;
87 uint64_t RelSize
= Is64Bit
? XCOFF::RelocationSerializationSize64
88 : XCOFF::RelocationSerializationSize32
;
89 CurrentOffset
+= InitSections
[I
].NumberOfRelocations
* RelSize
;
90 if (CurrentOffset
> MaxRawDataSize
) {
91 ErrHandler("maximum object size of" + Twine(MaxRawDataSize
) +
92 "exceeded when writing relocation data");
100 bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset
) {
101 uint64_t CurrentSecAddr
= 0;
102 for (uint16_t I
= 0, E
= InitSections
.size(); I
< E
; ++I
) {
103 if (CurrentOffset
> MaxRawDataSize
) {
104 ErrHandler("maximum object size of" + Twine(MaxRawDataSize
) +
105 "exceeded when writing section data");
109 // Assign indices for sections.
110 if (InitSections
[I
].SectionName
.size() &&
111 !SectionIndexMap
[InitSections
[I
].SectionName
]) {
112 // The section index starts from 1.
113 SectionIndexMap
[InitSections
[I
].SectionName
] = I
+ 1;
114 if ((I
+ 1) > MaxSectionIndex
) {
115 ErrHandler("exceeded the maximum permitted section index of " +
116 Twine(MaxSectionIndex
));
121 // Calculate the physical/virtual address. This field should contain 0 for
122 // all sections except the text, data and bss sections.
123 if (InitSections
[I
].Flags
!= XCOFF::STYP_TEXT
&&
124 InitSections
[I
].Flags
!= XCOFF::STYP_DATA
&&
125 InitSections
[I
].Flags
!= XCOFF::STYP_BSS
)
126 InitSections
[I
].Address
= 0;
128 InitSections
[I
].Address
= CurrentSecAddr
;
130 // Calculate the FileOffsetToData and data size for sections.
131 if (InitSections
[I
].SectionData
.binary_size()) {
132 InitSections
[I
].FileOffsetToData
= CurrentOffset
;
133 CurrentOffset
+= InitSections
[I
].SectionData
.binary_size();
134 // Ensure the offset is aligned to DefaultSectionAlign.
135 CurrentOffset
= alignTo(CurrentOffset
, DefaultSectionAlign
);
136 InitSections
[I
].Size
= CurrentOffset
- InitSections
[I
].FileOffsetToData
;
137 CurrentSecAddr
+= InitSections
[I
].Size
;
140 return initRelocations(CurrentOffset
);
143 bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset
) {
144 // The default format of the object file is XCOFF32.
145 InitFileHdr
.Magic
= XCOFF::XCOFF32
;
146 InitFileHdr
.NumberOfSections
= Obj
.Sections
.size();
147 InitFileHdr
.NumberOfSymTableEntries
= Obj
.Symbols
.size();
149 for (const XCOFFYAML::Symbol
&YamlSym
: Obj
.Symbols
) {
150 // Add the number of auxiliary symbols to the total number.
151 InitFileHdr
.NumberOfSymTableEntries
+= YamlSym
.NumberOfAuxEntries
;
152 if (nameShouldBeInStringTable(YamlSym
.SymbolName
))
153 Strings
.add(YamlSym
.SymbolName
);
155 // Finalize the string table.
158 // Calculate SymbolTableOffset for the file header.
159 if (InitFileHdr
.NumberOfSymTableEntries
) {
160 InitFileHdr
.SymbolTableOffset
= CurrentOffset
;
162 InitFileHdr
.NumberOfSymTableEntries
* XCOFF::SymbolTableEntrySize
;
163 if (CurrentOffset
> MaxRawDataSize
) {
164 ErrHandler("maximum object size of" + Twine(MaxRawDataSize
) +
165 "exceeded when writing symbols");
169 // TODO: Calculate FileOffsetToLineNumbers when line number supported.
173 bool XCOFFWriter::assignAddressesAndIndices() {
175 uint64_t FileHdrSize
=
176 Is64Bit
? XCOFF::FileHeaderSize64
: XCOFF::FileHeaderSize32
;
177 uint64_t SecHdrSize
=
178 Is64Bit
? XCOFF::SectionHeaderSize64
: XCOFF::SectionHeaderSize32
;
179 uint64_t CurrentOffset
= FileHdrSize
/* TODO: + auxiliaryHeaderSize() */ +
180 InitSections
.size() * SecHdrSize
;
182 // Calculate section header info.
183 if (!initSectionHeader(CurrentOffset
))
185 // Calculate file header info.
186 return initFileHeader(CurrentOffset
);
189 void XCOFFWriter::writeFileHeader() {
190 W
.write
<uint16_t>(Obj
.Header
.Magic
? Obj
.Header
.Magic
: InitFileHdr
.Magic
);
191 W
.write
<uint16_t>(Obj
.Header
.NumberOfSections
? Obj
.Header
.NumberOfSections
192 : InitFileHdr
.NumberOfSections
);
193 W
.write
<int32_t>(Obj
.Header
.TimeStamp
);
195 W
.write
<uint64_t>(Obj
.Header
.SymbolTableOffset
196 ? Obj
.Header
.SymbolTableOffset
197 : InitFileHdr
.SymbolTableOffset
);
198 W
.write
<uint16_t>(Obj
.Header
.AuxHeaderSize
);
199 W
.write
<uint16_t>(Obj
.Header
.Flags
);
200 W
.write
<int32_t>(Obj
.Header
.NumberOfSymTableEntries
201 ? Obj
.Header
.NumberOfSymTableEntries
202 : InitFileHdr
.NumberOfSymTableEntries
);
204 W
.write
<uint32_t>(Obj
.Header
.SymbolTableOffset
205 ? Obj
.Header
.SymbolTableOffset
206 : InitFileHdr
.SymbolTableOffset
);
207 W
.write
<int32_t>(Obj
.Header
.NumberOfSymTableEntries
208 ? Obj
.Header
.NumberOfSymTableEntries
209 : InitFileHdr
.NumberOfSymTableEntries
);
210 W
.write
<uint16_t>(Obj
.Header
.AuxHeaderSize
);
211 W
.write
<uint16_t>(Obj
.Header
.Flags
);
215 void XCOFFWriter::writeSectionHeader() {
216 for (uint16_t I
= 0, E
= Obj
.Sections
.size(); I
< E
; ++I
) {
217 XCOFFYAML::Section YamlSec
= Obj
.Sections
[I
];
218 XCOFFYAML::Section DerivedSec
= InitSections
[I
];
219 writeName(YamlSec
.SectionName
, W
);
220 // Virtual address is the same as physical address.
221 uint64_t SectionAddress
=
222 YamlSec
.Address
? YamlSec
.Address
: DerivedSec
.Address
;
224 W
.write
<uint64_t>(SectionAddress
); // Physical address
225 W
.write
<uint64_t>(SectionAddress
); // Virtual address
226 W
.write
<uint64_t>(YamlSec
.Size
? YamlSec
.Size
: DerivedSec
.Size
);
227 W
.write
<uint64_t>(YamlSec
.FileOffsetToData
? YamlSec
.FileOffsetToData
228 : DerivedSec
.FileOffsetToData
);
229 W
.write
<uint64_t>(YamlSec
.FileOffsetToRelocations
230 ? YamlSec
.FileOffsetToRelocations
231 : DerivedSec
.FileOffsetToRelocations
);
232 W
.write
<uint64_t>(YamlSec
.FileOffsetToLineNumbers
);
233 W
.write
<uint32_t>(YamlSec
.NumberOfRelocations
234 ? YamlSec
.NumberOfRelocations
235 : DerivedSec
.NumberOfRelocations
);
236 W
.write
<uint32_t>(YamlSec
.NumberOfLineNumbers
);
237 W
.write
<int32_t>(YamlSec
.Flags
);
240 W
.write
<uint32_t>(SectionAddress
); // Physical address
241 W
.write
<uint32_t>(SectionAddress
); // Virtual address
242 W
.write
<uint32_t>(YamlSec
.Size
? YamlSec
.Size
: DerivedSec
.Size
);
243 W
.write
<uint32_t>(YamlSec
.FileOffsetToData
? YamlSec
.FileOffsetToData
244 : DerivedSec
.FileOffsetToData
);
245 W
.write
<uint32_t>(YamlSec
.FileOffsetToRelocations
246 ? YamlSec
.FileOffsetToRelocations
247 : DerivedSec
.FileOffsetToRelocations
);
248 W
.write
<uint32_t>(YamlSec
.FileOffsetToLineNumbers
);
249 W
.write
<uint16_t>(YamlSec
.NumberOfRelocations
250 ? YamlSec
.NumberOfRelocations
251 : DerivedSec
.NumberOfRelocations
);
252 W
.write
<uint16_t>(YamlSec
.NumberOfLineNumbers
);
253 W
.write
<int32_t>(YamlSec
.Flags
);
258 bool XCOFFWriter::writeSectionData() {
259 for (uint16_t I
= 0, E
= Obj
.Sections
.size(); I
< E
; ++I
) {
260 XCOFFYAML::Section YamlSec
= Obj
.Sections
[I
];
261 if (YamlSec
.SectionData
.binary_size()) {
262 // Fill the padding size with zeros.
263 int64_t PaddingSize
=
264 InitSections
[I
].FileOffsetToData
- (W
.OS
.tell() - StartOffset
);
265 if (PaddingSize
< 0) {
266 ErrHandler("redundant data was written before section data");
269 W
.OS
.write_zeros(PaddingSize
);
270 YamlSec
.SectionData
.writeAsBinary(W
.OS
);
276 bool XCOFFWriter::writeRelocations() {
277 for (uint16_t I
= 0, E
= Obj
.Sections
.size(); I
< E
; ++I
) {
278 XCOFFYAML::Section YamlSec
= Obj
.Sections
[I
];
279 if (!YamlSec
.Relocations
.empty()) {
280 int64_t PaddingSize
=
281 InitSections
[I
].FileOffsetToRelocations
- (W
.OS
.tell() - StartOffset
);
282 if (PaddingSize
< 0) {
283 ErrHandler("redundant data was written before relocations");
286 W
.OS
.write_zeros(PaddingSize
);
287 for (const XCOFFYAML::Relocation
&YamlRel
: YamlSec
.Relocations
) {
289 W
.write
<uint64_t>(YamlRel
.VirtualAddress
);
291 W
.write
<uint32_t>(YamlRel
.VirtualAddress
);
292 W
.write
<uint32_t>(YamlRel
.SymbolIndex
);
293 W
.write
<uint8_t>(YamlRel
.Info
);
294 W
.write
<uint8_t>(YamlRel
.Type
);
301 bool XCOFFWriter::writeSymbols() {
302 int64_t PaddingSize
=
303 (uint64_t)InitFileHdr
.SymbolTableOffset
- (W
.OS
.tell() - StartOffset
);
304 if (PaddingSize
< 0) {
305 ErrHandler("redundant data was written before symbols");
308 W
.OS
.write_zeros(PaddingSize
);
309 for (const XCOFFYAML::Symbol
&YamlSym
: Obj
.Symbols
) {
311 W
.write
<uint64_t>(YamlSym
.Value
);
312 W
.write
<uint32_t>(Strings
.getOffset(YamlSym
.SymbolName
));
314 if (nameShouldBeInStringTable(YamlSym
.SymbolName
)) {
315 // For XCOFF32: A value of 0 indicates that the symbol name is in the
318 W
.write
<uint32_t>(Strings
.getOffset(YamlSym
.SymbolName
));
320 writeName(YamlSym
.SymbolName
, W
);
322 W
.write
<uint32_t>(YamlSym
.Value
);
325 YamlSym
.SectionName
.size() ? SectionIndexMap
[YamlSym
.SectionName
] : 0);
326 W
.write
<uint16_t>(YamlSym
.Type
);
327 W
.write
<uint8_t>(YamlSym
.StorageClass
);
328 W
.write
<uint8_t>(YamlSym
.NumberOfAuxEntries
);
330 // Now output the auxiliary entry.
331 for (uint8_t I
= 0, E
= YamlSym
.NumberOfAuxEntries
; I
< E
; ++I
) {
332 // TODO: Auxiliary entry is not supported yet.
333 // The auxiliary entries for a symbol follow its symbol table entry. The
334 // length of each auxiliary entry is the same as a symbol table entry (18
335 // bytes). The format and quantity of auxiliary entries depend on the
336 // storage class (n_sclass) and type (n_type) of the symbol table entry.
337 W
.OS
.write_zeros(XCOFF::SymbolTableEntrySize
);
343 bool XCOFFWriter::writeXCOFF() {
344 if (!assignAddressesAndIndices())
346 StartOffset
= W
.OS
.tell();
348 if (!Obj
.Sections
.empty()) {
349 writeSectionHeader();
350 if (!writeSectionData())
352 if (!writeRelocations())
355 if (!Obj
.Symbols
.empty() && !writeSymbols())
357 // Write the string table.
358 if (Strings
.getSize() > 4)
363 } // end anonymous namespace
368 bool yaml2xcoff(XCOFFYAML::Object
&Doc
, raw_ostream
&Out
, ErrorHandler EH
) {
369 XCOFFWriter
Writer(Doc
, Out
, EH
);
370 return Writer
.writeXCOFF();