Skip to content

Commit fd6e19c

Browse files
ysyedaYusra Syeda
andauthored
[SystemZ][z/OS] yaml2obj for header and end records (#73859)
This PR implements part 1 of yaml2obj for the GOFF Object File Format. It adds support for the header and end records. --------- Co-authored-by: Yusra Syeda <[email protected]>
1 parent 01061ed commit fd6e19c

File tree

11 files changed

+446
-0
lines changed

11 files changed

+446
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===- GOFFYAML.h - GOFF YAMLIO implementation ------------------*- C++ -*-===//
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+
// This file declares classes for handling the YAML representation of GOFF.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_OBJECTYAML_GOFFYAML_H
14+
#define LLVM_OBJECTYAML_GOFFYAML_H
15+
16+
#include "llvm/ADT/StringRef.h"
17+
#include "llvm/BinaryFormat/GOFF.h"
18+
#include "llvm/ObjectYAML/YAML.h"
19+
#include <cstdint>
20+
#include <vector>
21+
22+
namespace llvm {
23+
24+
// The structure of the yaml files is not an exact 1:1 match to GOFF. In order
25+
// to use yaml::IO, we use these structures which are closer to the source.
26+
namespace GOFFYAML {
27+
28+
struct FileHeader {
29+
uint32_t TargetEnvironment = 0;
30+
uint32_t TargetOperatingSystem = 0;
31+
uint16_t CCSID = 0;
32+
StringRef CharacterSetName;
33+
StringRef LanguageProductIdentifier;
34+
uint32_t ArchitectureLevel = 0;
35+
std::optional<uint16_t> InternalCCSID;
36+
std::optional<uint8_t> TargetSoftwareEnvironment;
37+
};
38+
39+
struct Object {
40+
FileHeader Header;
41+
Object();
42+
};
43+
} // end namespace GOFFYAML
44+
} // end namespace llvm
45+
46+
LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::FileHeader)
47+
LLVM_YAML_DECLARE_MAPPING_TRAITS(GOFFYAML::Object)
48+
49+
#endif // LLVM_OBJECTYAML_GOFFYAML_H

llvm/include/llvm/ObjectYAML/ObjectYAML.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "llvm/ObjectYAML/COFFYAML.h"
1414
#include "llvm/ObjectYAML/DXContainerYAML.h"
1515
#include "llvm/ObjectYAML/ELFYAML.h"
16+
#include "llvm/ObjectYAML/GOFFYAML.h"
1617
#include "llvm/ObjectYAML/MachOYAML.h"
1718
#include "llvm/ObjectYAML/MinidumpYAML.h"
1819
#include "llvm/ObjectYAML/OffloadYAML.h"
@@ -30,6 +31,7 @@ struct YamlObjectFile {
3031
std::unique_ptr<ArchYAML::Archive> Arch;
3132
std::unique_ptr<ELFYAML::Object> Elf;
3233
std::unique_ptr<COFFYAML::Object> Coff;
34+
std::unique_ptr<GOFFYAML::Object> Goff;
3335
std::unique_ptr<MachOYAML::Object> MachO;
3436
std::unique_ptr<MachOYAML::UniversalBinary> FatMachO;
3537
std::unique_ptr<MinidumpYAML::Object> Minidump;

llvm/include/llvm/ObjectYAML/yaml2obj.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ namespace ELFYAML {
3232
struct Object;
3333
}
3434

35+
namespace GOFFYAML {
36+
struct Object;
37+
}
38+
3539
namespace MinidumpYAML {
3640
struct Object;
3741
}
@@ -64,6 +68,7 @@ using ErrorHandler = llvm::function_ref<void(const Twine &Msg)>;
6468

6569
bool yaml2archive(ArchYAML::Archive &Doc, raw_ostream &Out, ErrorHandler EH);
6670
bool yaml2coff(COFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH);
71+
bool yaml2goff(GOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH);
6772
bool yaml2elf(ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH,
6873
uint64_t MaxSize);
6974
bool yaml2macho(YamlObjectFile &Doc, raw_ostream &Out, ErrorHandler EH);

llvm/lib/ObjectYAML/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ add_llvm_component_library(LLVMObjectYAML
1313
DXContainerYAML.cpp
1414
ELFEmitter.cpp
1515
ELFYAML.cpp
16+
GOFFEmitter.cpp
17+
GOFFYAML.cpp
1618
MachOEmitter.cpp
1719
MachOYAML.cpp
1820
ObjectYAML.cpp

llvm/lib/ObjectYAML/GOFFEmitter.cpp

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
//===- yaml2goff - Convert YAML to a GOFF object file ---------------------===//
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+
/// \file
10+
/// The GOFF component of yaml2obj.
11+
///
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "llvm/ADT/IndexedMap.h"
15+
#include "llvm/ObjectYAML/ObjectYAML.h"
16+
#include "llvm/ObjectYAML/yaml2obj.h"
17+
#include "llvm/Support/ConvertEBCDIC.h"
18+
#include "llvm/Support/Endian.h"
19+
#include "llvm/Support/raw_ostream.h"
20+
21+
using namespace llvm;
22+
23+
namespace {
24+
25+
// Common flag values on records.
26+
enum {
27+
// Flag: This record is continued.
28+
Rec_Continued = 1,
29+
30+
// Flag: This record is a continuation.
31+
Rec_Continuation = 1 << (8 - 6 - 1),
32+
};
33+
34+
template <typename ValueType> struct BinaryBeImpl {
35+
ValueType Value;
36+
BinaryBeImpl(ValueType V) : Value(V) {}
37+
};
38+
39+
template <typename ValueType>
40+
raw_ostream &operator<<(raw_ostream &OS, const BinaryBeImpl<ValueType> &BBE) {
41+
char Buffer[sizeof(BBE.Value)];
42+
support::endian::write<ValueType, llvm::endianness::big, support::unaligned>(
43+
Buffer, BBE.Value);
44+
OS.write(Buffer, sizeof(BBE.Value));
45+
return OS;
46+
}
47+
48+
template <typename ValueType> BinaryBeImpl<ValueType> binaryBe(ValueType V) {
49+
return BinaryBeImpl<ValueType>(V);
50+
}
51+
52+
struct ZerosImpl {
53+
size_t NumBytes;
54+
};
55+
56+
raw_ostream &operator<<(raw_ostream &OS, const ZerosImpl &Z) {
57+
OS.write_zeros(Z.NumBytes);
58+
return OS;
59+
}
60+
61+
ZerosImpl zeros(const size_t NumBytes) { return ZerosImpl{NumBytes}; }
62+
63+
// The GOFFOstream is responsible to write the data into the fixed physical
64+
// records of the format. A user of this class announces the start of a new
65+
// logical record and the size of its payload. While writing the payload, the
66+
// physical records are created for the data. Possible fill bytes at the end of
67+
// a physical record are written automatically.
68+
class GOFFOstream : public raw_ostream {
69+
public:
70+
explicit GOFFOstream(raw_ostream &OS)
71+
: OS(OS), LogicalRecords(0), RemainingSize(0), NewLogicalRecord(false) {
72+
SetBufferSize(GOFF::PayloadLength);
73+
}
74+
75+
~GOFFOstream() { finalize(); }
76+
77+
void makeNewRecord(GOFF::RecordType Type, size_t Size) {
78+
fillRecord();
79+
CurrentType = Type;
80+
RemainingSize = Size;
81+
if (size_t Gap = (RemainingSize % GOFF::PayloadLength))
82+
RemainingSize += GOFF::PayloadLength - Gap;
83+
NewLogicalRecord = true;
84+
++LogicalRecords;
85+
}
86+
87+
void finalize() { fillRecord(); }
88+
89+
uint32_t logicalRecords() { return LogicalRecords; }
90+
91+
private:
92+
// The underlying raw_ostream.
93+
raw_ostream &OS;
94+
95+
// The number of logical records emitted so far.
96+
uint32_t LogicalRecords;
97+
98+
// The remaining size of this logical record, including fill bytes.
99+
size_t RemainingSize;
100+
101+
// The type of the current (logical) record.
102+
GOFF::RecordType CurrentType;
103+
104+
// Signals start of new record.
105+
bool NewLogicalRecord;
106+
107+
// Return the number of bytes left to write until next physical record.
108+
// Please note that we maintain the total number of bytes left, not the
109+
// written size.
110+
size_t bytesToNextPhysicalRecord() {
111+
size_t Bytes = RemainingSize % GOFF::PayloadLength;
112+
return Bytes ? Bytes : GOFF::PayloadLength;
113+
}
114+
115+
// Write the record prefix of a physical record, using the current record
116+
// type.
117+
static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,
118+
size_t RemainingSize,
119+
uint8_t Flags = Rec_Continuation) {
120+
uint8_t TypeAndFlags = Flags | (Type << 4);
121+
if (RemainingSize > GOFF::RecordLength)
122+
TypeAndFlags |= Rec_Continued;
123+
OS << binaryBe(static_cast<unsigned char>(GOFF::PTVPrefix))
124+
<< binaryBe(static_cast<unsigned char>(TypeAndFlags))
125+
<< binaryBe(static_cast<unsigned char>(0));
126+
}
127+
128+
// Fill the last physical record of a logical record with zero bytes.
129+
void fillRecord() {
130+
assert((GetNumBytesInBuffer() <= RemainingSize) &&
131+
"More bytes in buffer than expected");
132+
size_t Remains = RemainingSize - GetNumBytesInBuffer();
133+
if (Remains) {
134+
assert((Remains < GOFF::RecordLength) &&
135+
"Attempting to fill more than one physical record");
136+
raw_ostream::write_zeros(Remains);
137+
}
138+
flush();
139+
assert(RemainingSize == 0 && "Not fully flushed");
140+
assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty");
141+
}
142+
143+
// See raw_ostream::write_impl.
144+
void write_impl(const char *Ptr, size_t Size) override {
145+
assert((RemainingSize >= Size) && "Attempt to write too much data");
146+
assert(RemainingSize && "Logical record overflow");
147+
if (!(RemainingSize % GOFF::PayloadLength)) {
148+
writeRecordPrefix(OS, CurrentType, RemainingSize,
149+
NewLogicalRecord ? 0 : Rec_Continuation);
150+
NewLogicalRecord = false;
151+
}
152+
assert(!NewLogicalRecord &&
153+
"New logical record not on physical record boundary");
154+
155+
size_t Idx = 0;
156+
while (Size > 0) {
157+
size_t BytesToWrite = bytesToNextPhysicalRecord();
158+
if (BytesToWrite > Size)
159+
BytesToWrite = Size;
160+
OS.write(Ptr + Idx, BytesToWrite);
161+
Idx += BytesToWrite;
162+
Size -= BytesToWrite;
163+
RemainingSize -= BytesToWrite;
164+
if (Size) {
165+
writeRecordPrefix(OS, CurrentType, RemainingSize);
166+
}
167+
}
168+
}
169+
170+
// Return the current position within the stream, not counting the bytes
171+
// currently in the buffer.
172+
uint64_t current_pos() const override { return OS.tell(); }
173+
};
174+
175+
class GOFFState {
176+
void writeHeader(GOFFYAML::FileHeader &FileHdr);
177+
void writeEnd();
178+
179+
void reportError(const Twine &Msg) {
180+
ErrHandler(Msg);
181+
HasError = true;
182+
}
183+
184+
GOFFState(raw_ostream &OS, GOFFYAML::Object &Doc,
185+
yaml::ErrorHandler ErrHandler)
186+
: GW(OS), Doc(Doc), ErrHandler(ErrHandler), HasError(false) {}
187+
188+
~GOFFState() { GW.finalize(); }
189+
190+
bool writeObject();
191+
192+
public:
193+
static bool writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc,
194+
yaml::ErrorHandler ErrHandler);
195+
196+
private:
197+
GOFFOstream GW;
198+
GOFFYAML::Object &Doc;
199+
yaml::ErrorHandler ErrHandler;
200+
bool HasError;
201+
};
202+
203+
void GOFFState::writeHeader(GOFFYAML::FileHeader &FileHdr) {
204+
SmallString<16> CCSIDName;
205+
if (std::error_code EC =
206+
ConverterEBCDIC::convertToEBCDIC(FileHdr.CharacterSetName, CCSIDName))
207+
reportError("Conversion error on " + FileHdr.CharacterSetName);
208+
if (CCSIDName.size() > 16) {
209+
reportError("CharacterSetName too long");
210+
CCSIDName.resize(16);
211+
}
212+
SmallString<16> LangProd;
213+
if (std::error_code EC = ConverterEBCDIC::convertToEBCDIC(
214+
FileHdr.LanguageProductIdentifier, LangProd))
215+
reportError("Conversion error on " + FileHdr.LanguageProductIdentifier);
216+
if (LangProd.size() > 16) {
217+
reportError("LanguageProductIdentifier too long");
218+
LangProd.resize(16);
219+
}
220+
221+
GW.makeNewRecord(GOFF::RT_HDR, GOFF::PayloadLength);
222+
GW << binaryBe(FileHdr.TargetEnvironment) // TargetEnvironment
223+
<< binaryBe(FileHdr.TargetOperatingSystem) // TargetOperatingSystem
224+
<< zeros(2) // Reserved
225+
<< binaryBe(FileHdr.CCSID) // CCSID
226+
<< CCSIDName // CharacterSetName
227+
<< zeros(16 - CCSIDName.size()) // Fill bytes
228+
<< LangProd // LanguageProductIdentifier
229+
<< zeros(16 - LangProd.size()) // Fill bytes
230+
<< binaryBe(FileHdr.ArchitectureLevel); // ArchitectureLevel
231+
// The module propties are optional. Figure out if we need to write them.
232+
uint16_t ModPropLen = 0;
233+
if (FileHdr.TargetSoftwareEnvironment)
234+
ModPropLen = 3;
235+
else if (FileHdr.InternalCCSID)
236+
ModPropLen = 2;
237+
if (ModPropLen) {
238+
GW << binaryBe(ModPropLen) << zeros(6);
239+
if (ModPropLen >= 2)
240+
GW << binaryBe(FileHdr.InternalCCSID ? *FileHdr.InternalCCSID : 0);
241+
if (ModPropLen >= 3)
242+
GW << binaryBe(FileHdr.TargetSoftwareEnvironment
243+
? *FileHdr.TargetSoftwareEnvironment
244+
: 0);
245+
}
246+
}
247+
248+
void GOFFState::writeEnd() {
249+
GW.makeNewRecord(GOFF::RT_END, GOFF::PayloadLength);
250+
GW << binaryBe(uint8_t(0)) // No entry point
251+
<< binaryBe(uint8_t(0)) // No AMODE
252+
<< zeros(3) // Reserved
253+
<< binaryBe(GW.logicalRecords());
254+
// No entry point yet. Automatically fill remaining space with zero bytes.
255+
GW.finalize();
256+
}
257+
258+
bool GOFFState::writeObject() {
259+
writeHeader(Doc.Header);
260+
if (HasError)
261+
return false;
262+
writeEnd();
263+
return true;
264+
}
265+
266+
bool GOFFState::writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc,
267+
yaml::ErrorHandler ErrHandler) {
268+
GOFFState State(OS, Doc, ErrHandler);
269+
return State.writeObject();
270+
}
271+
} // namespace
272+
273+
namespace llvm {
274+
namespace yaml {
275+
276+
bool yaml2goff(llvm::GOFFYAML::Object &Doc, raw_ostream &Out,
277+
ErrorHandler ErrHandler) {
278+
return GOFFState::writeGOFF(Out, Doc, ErrHandler);
279+
}
280+
281+
} // namespace yaml
282+
} // namespace llvm

0 commit comments

Comments
 (0)