1 //===- lib/MC/GOFFObjectWriter.cpp - GOFF File Writer ---------------------===//
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 // This file implements GOFF object file writer information.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/BinaryFormat/GOFF.h"
14 #include "llvm/MC/MCAssembler.h"
15 #include "llvm/MC/MCGOFFObjectWriter.h"
16 #include "llvm/MC/MCValue.h"
17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/Endian.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/raw_ostream.h"
24 #define DEBUG_TYPE "goff-writer"
28 // The standard System/390 convention is to name the high-order (leftmost) bit
29 // in a byte as bit zero. The Flags type helps to set bits in a byte according
30 // to this numeration order.
34 constexpr static uint8_t bits(uint8_t BitIndex
, uint8_t Length
, uint8_t Value
,
36 assert(BitIndex
< 8 && "Bit index out of bounds!");
37 assert(Length
+ BitIndex
<= 8 && "Bit length too long!");
39 uint8_t Mask
= ((1 << Length
) - 1) << (8 - BitIndex
- Length
);
40 Value
= Value
<< (8 - BitIndex
- Length
);
41 assert((Value
& Mask
) == Value
&& "Bits set outside of range!");
43 return (OldValue
& ~Mask
) | Value
;
47 constexpr Flags() : Val(0) {}
48 constexpr Flags(uint8_t BitIndex
, uint8_t Length
, uint8_t Value
)
49 : Val(bits(BitIndex
, Length
, Value
, 0)) {}
51 void set(uint8_t BitIndex
, uint8_t Length
, uint8_t Value
) {
52 Val
= bits(BitIndex
, Length
, Value
, Val
);
55 constexpr operator uint8_t() const { return Val
; }
58 // Common flag values on records.
60 // Flag: This record is continued.
61 constexpr uint8_t RecContinued
= Flags(7, 1, 1);
63 // Flag: This record is a continuation.
64 constexpr uint8_t RecContinuation
= Flags(6, 1, 1);
66 // The GOFFOstream is responsible to write the data into the fixed physical
67 // records of the format. A user of this class announces the start of a new
68 // logical record and the size of its content. While writing the content, the
69 // physical records are created for the data. Possible fill bytes at the end of
70 // a physical record are written automatically. In principle, the GOFFOstream
71 // is agnostic of the endianness of the content. However, it also supports
72 // writing data in big endian byte order.
73 class GOFFOstream
: public raw_ostream
{
74 /// The underlying raw_pwrite_stream.
75 raw_pwrite_stream
&OS
;
77 /// The remaining size of this logical record, including fill bytes.
81 /// The number of bytes needed to fill up the last physical record.
85 /// The number of logical records emitted to far.
86 uint32_t LogicalRecords
;
88 /// The type of the current (logical) record.
89 GOFF::RecordType CurrentType
;
91 /// Signals start of new record.
92 bool NewLogicalRecord
;
94 /// Static allocated buffer for the stream, used by the raw_ostream class. The
95 /// buffer is sized to hold the content of a physical record.
96 char Buffer
[GOFF::RecordContentLength
];
98 // Return the number of bytes left to write until next physical record.
99 // Please note that we maintain the total numbers of byte left, not the
101 size_t bytesToNextPhysicalRecord() {
102 size_t Bytes
= RemainingSize
% GOFF::RecordContentLength
;
103 return Bytes
? Bytes
: GOFF::RecordContentLength
;
106 /// Write the record prefix of a physical record, using the given record type.
107 static void writeRecordPrefix(raw_ostream
&OS
, GOFF::RecordType Type
,
108 size_t RemainingSize
,
109 uint8_t Flags
= RecContinuation
);
111 /// Fill the last physical record of a logical record with zero bytes.
114 /// See raw_ostream::write_impl.
115 void write_impl(const char *Ptr
, size_t Size
) override
;
117 /// Return the current position within the stream, not counting the bytes
118 /// currently in the buffer.
119 uint64_t current_pos() const override
{ return OS
.tell(); }
122 explicit GOFFOstream(raw_pwrite_stream
&OS
)
123 : OS(OS
), RemainingSize(0), LogicalRecords(0), NewLogicalRecord(false) {
124 SetBuffer(Buffer
, sizeof(Buffer
));
127 ~GOFFOstream() { finalize(); }
129 raw_pwrite_stream
&getOS() { return OS
; }
131 void newRecord(GOFF::RecordType Type
, size_t Size
);
133 void finalize() { fillRecord(); }
135 uint32_t logicalRecords() { return LogicalRecords
; }
137 // Support for endian-specific data.
138 template <typename value_type
> void writebe(value_type Value
) {
140 support::endian::byte_swap
<value_type
>(Value
, llvm::endianness::big
);
141 write(reinterpret_cast<const char *>(&Value
), sizeof(value_type
));
145 void GOFFOstream::writeRecordPrefix(raw_ostream
&OS
, GOFF::RecordType Type
,
146 size_t RemainingSize
, uint8_t Flags
) {
147 uint8_t TypeAndFlags
= Flags
| (Type
<< 4);
148 if (RemainingSize
> GOFF::RecordLength
)
149 TypeAndFlags
|= RecContinued
;
150 OS
<< static_cast<unsigned char>(GOFF::PTVPrefix
) // Record Type
151 << static_cast<unsigned char>(TypeAndFlags
) // Continuation
152 << static_cast<unsigned char>(0); // Version
155 void GOFFOstream::newRecord(GOFF::RecordType Type
, size_t Size
) {
158 RemainingSize
= Size
;
162 Gap
= (RemainingSize
% GOFF::RecordContentLength
);
164 Gap
= GOFF::RecordContentLength
- Gap
;
165 RemainingSize
+= Gap
;
167 NewLogicalRecord
= true;
171 void GOFFOstream::fillRecord() {
172 assert((GetNumBytesInBuffer() <= RemainingSize
) &&
173 "More bytes in buffer than expected");
174 size_t Remains
= RemainingSize
- GetNumBytesInBuffer();
176 assert(Remains
== Gap
&& "Wrong size of fill gap");
177 assert((Remains
< GOFF::RecordLength
) &&
178 "Attempt to fill more than one physical record");
179 raw_ostream::write_zeros(Remains
);
182 assert(RemainingSize
== 0 && "Not fully flushed");
183 assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty");
186 // This function is called from the raw_ostream implementation if:
187 // - The internal buffer is full. Size is excactly the size of the buffer.
188 // - Data larger than the internal buffer is written. Size is a multiple of the
190 // - flush() has been called. Size is at most the buffer size.
191 // The GOFFOstream implementation ensures that flush() is called before a new
192 // logical record begins. Therefore it is sufficient to check for a new block
194 void GOFFOstream::write_impl(const char *Ptr
, size_t Size
) {
195 assert((RemainingSize
>= Size
) && "Attempt to write too much data");
196 assert(RemainingSize
&& "Logical record overflow");
197 if (!(RemainingSize
% GOFF::RecordContentLength
)) {
198 writeRecordPrefix(OS
, CurrentType
, RemainingSize
,
199 NewLogicalRecord
? 0 : RecContinuation
);
200 NewLogicalRecord
= false;
202 assert(!NewLogicalRecord
&&
203 "New logical record not on physical record boundary");
207 size_t BytesToWrite
= bytesToNextPhysicalRecord();
208 if (BytesToWrite
> Size
)
210 OS
.write(Ptr
+ Idx
, BytesToWrite
);
212 Size
-= BytesToWrite
;
213 RemainingSize
-= BytesToWrite
;
215 writeRecordPrefix(OS
, CurrentType
, RemainingSize
);
219 class GOFFObjectWriter
: public MCObjectWriter
{
220 // The target specific GOFF writer instance.
221 std::unique_ptr
<MCGOFFObjectTargetWriter
> TargetObjectWriter
;
223 // The stream used to write the GOFF records.
227 GOFFObjectWriter(std::unique_ptr
<MCGOFFObjectTargetWriter
> MOTW
,
228 raw_pwrite_stream
&OS
)
229 : TargetObjectWriter(std::move(MOTW
)), OS(OS
) {}
231 ~GOFFObjectWriter() override
{}
233 // Write GOFF records.
237 // Implementation of the MCObjectWriter interface.
238 void recordRelocation(MCAssembler
&Asm
, const MCFragment
*Fragment
,
239 const MCFixup
&Fixup
, MCValue Target
,
240 uint64_t &FixedValue
) override
{}
241 uint64_t writeObject(MCAssembler
&Asm
) override
;
243 } // end anonymous namespace
245 void GOFFObjectWriter::writeHeader() {
246 OS
.newRecord(GOFF::RT_HDR
, /*Size=*/57);
247 OS
.write_zeros(1); // Reserved
248 OS
.writebe
<uint32_t>(0); // Target Hardware Environment
249 OS
.writebe
<uint32_t>(0); // Target Operating System Environment
250 OS
.write_zeros(2); // Reserved
251 OS
.writebe
<uint16_t>(0); // CCSID
252 OS
.write_zeros(16); // Character Set name
253 OS
.write_zeros(16); // Language Product Identifier
254 OS
.writebe
<uint32_t>(1); // Architecture Level
255 OS
.writebe
<uint16_t>(0); // Module Properties Length
256 OS
.write_zeros(6); // Reserved
259 void GOFFObjectWriter::writeEnd() {
260 uint8_t F
= GOFF::END_EPR_None
;
264 // TODO Set Flags/AMODE/ESDID for entry point.
266 OS
.newRecord(GOFF::RT_END
, /*Size=*/13);
267 OS
.writebe
<uint8_t>(Flags(6, 2, F
)); // Indicator flags
268 OS
.writebe
<uint8_t>(AMODE
); // AMODE
269 OS
.write_zeros(3); // Reserved
270 // The record count is the number of logical records. In principle, this value
271 // is available as OS.logicalRecords(). However, some tools rely on this field
273 OS
.writebe
<uint32_t>(0); // Record Count
274 OS
.writebe
<uint32_t>(ESDID
); // ESDID (of entry point)
278 uint64_t GOFFObjectWriter::writeObject(MCAssembler
&Asm
) {
279 uint64_t StartOffset
= OS
.tell();
284 LLVM_DEBUG(dbgs() << "Wrote " << OS
.logicalRecords() << " logical records.");
286 return OS
.tell() - StartOffset
;
289 std::unique_ptr
<MCObjectWriter
>
290 llvm::createGOFFObjectWriter(std::unique_ptr
<MCGOFFObjectTargetWriter
> MOTW
,
291 raw_pwrite_stream
&OS
) {
292 return std::make_unique
<GOFFObjectWriter
>(std::move(MOTW
), OS
);