blob: 735eb77ae1b6a8a6822be4e0ed6fb183237366ac [file] [log] [blame]
Sam Cleggf61910d2018-01-12 22:18:22 +00001//===- InputChunks.cpp ----------------------------------------------------===//
Sam Cleggc94d3932017-11-17 18:14:09 +00002//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Sam Cleggc94d3932017-11-17 18:14:09 +00006//
7//===----------------------------------------------------------------------===//
8
Sam Clegg5fa274b2018-01-10 01:13:34 +00009#include "InputChunks.h"
Sam Cleggd96d9352018-01-10 19:22:42 +000010#include "Config.h"
Sam Cleggc94d3932017-11-17 18:14:09 +000011#include "OutputSegment.h"
Rui Ueyamabf450d92018-02-20 04:26:26 +000012#include "WriterUtils.h"
Sam Cleggd96d9352018-01-10 19:22:42 +000013#include "lld/Common/ErrorHandler.h"
Sam Cleggc94d3932017-11-17 18:14:09 +000014#include "lld/Common/LLVM.h"
Sam Cleggd96d9352018-01-10 19:22:42 +000015#include "llvm/Support/LEB128.h"
Sam Cleggc94d3932017-11-17 18:14:09 +000016
17#define DEBUG_TYPE "lld"
18
19using namespace llvm;
Sam Cleggd96d9352018-01-10 19:22:42 +000020using namespace llvm::wasm;
Rui Ueyamae351c3a2018-02-16 20:38:15 +000021using namespace llvm::support::endian;
Sam Cleggd96d9352018-01-10 19:22:42 +000022using namespace lld;
Sam Cleggc94d3932017-11-17 18:14:09 +000023using namespace lld::wasm;
24
Sam Clegge3a845e2019-03-29 22:56:39 +000025StringRef lld::relocTypeToString(uint8_t RelocType) {
Sam Cleggc1be8232018-03-11 01:35:02 +000026 switch (RelocType) {
Heejin Ahn4821ebf2018-08-29 21:03:16 +000027#define WASM_RELOC(NAME, REL) \
28 case REL: \
29 return #NAME;
Sam Cleggc1be8232018-03-11 01:35:02 +000030#include "llvm/BinaryFormat/WasmRelocs.def"
31#undef WASM_RELOC
32 }
33 llvm_unreachable("unknown reloc type");
34}
35
Rui Ueyama81bee042018-02-19 22:29:48 +000036std::string lld::toString(const InputChunk *C) {
37 return (toString(C->File) + ":(" + C->getName() + ")").str();
38}
39
Nicholas Wilsonc4d9aa12018-03-14 15:45:11 +000040StringRef InputChunk::getComdatName() const {
41 uint32_t Index = getComdat();
42 if (Index == UINT32_MAX)
43 return StringRef();
44 return File->getWasmObj()->linkingData().Comdats[Index];
45}
46
Sam Cleggc1953142018-05-05 00:18:43 +000047void InputChunk::verifyRelocTargets() const {
48 for (const WasmRelocation &Rel : Relocations) {
49 uint32_t ExistingValue;
50 unsigned BytesRead = 0;
51 uint32_t Offset = Rel.Offset - getInputSectionOffset();
52 const uint8_t *Loc = data().data() + Offset;
53 switch (Rel.Type) {
Sam Clegg79e33172019-02-04 17:49:33 +000054 case R_WASM_TYPE_INDEX_LEB:
55 case R_WASM_FUNCTION_INDEX_LEB:
56 case R_WASM_GLOBAL_INDEX_LEB:
57 case R_WASM_EVENT_INDEX_LEB:
58 case R_WASM_MEMORY_ADDR_LEB:
Sam Cleggc1953142018-05-05 00:18:43 +000059 ExistingValue = decodeULEB128(Loc, &BytesRead);
60 break;
Sam Clegg79e33172019-02-04 17:49:33 +000061 case R_WASM_TABLE_INDEX_SLEB:
Sam Clegg2a7cac92019-04-04 17:43:50 +000062 case R_WASM_TABLE_INDEX_REL_SLEB:
Sam Clegg79e33172019-02-04 17:49:33 +000063 case R_WASM_MEMORY_ADDR_SLEB:
Sam Clegg2a7cac92019-04-04 17:43:50 +000064 case R_WASM_MEMORY_ADDR_REL_SLEB:
Sam Cleggc1953142018-05-05 00:18:43 +000065 ExistingValue = static_cast<uint32_t>(decodeSLEB128(Loc, &BytesRead));
66 break;
Sam Clegg79e33172019-02-04 17:49:33 +000067 case R_WASM_TABLE_INDEX_I32:
68 case R_WASM_MEMORY_ADDR_I32:
69 case R_WASM_FUNCTION_OFFSET_I32:
70 case R_WASM_SECTION_OFFSET_I32:
Sam Cleggc1953142018-05-05 00:18:43 +000071 ExistingValue = static_cast<uint32_t>(read32le(Loc));
72 break;
73 default:
74 llvm_unreachable("unknown relocation type");
75 }
76
77 if (BytesRead && BytesRead != 5)
78 warn("expected LEB at relocation site be 5-byte padded");
Sam Clegg492f7522019-03-26 19:46:15 +000079
80 if (Rel.Type != R_WASM_GLOBAL_INDEX_LEB) {
81 uint32_t ExpectedValue = File->calcExpectedValue(Rel);
82 if (ExpectedValue != ExistingValue)
Sam Clegge3a845e2019-03-29 22:56:39 +000083 warn("unexpected existing value for " + relocTypeToString(Rel.Type) +
Sam Clegg492f7522019-03-26 19:46:15 +000084 ": existing=" + Twine(ExistingValue) +
85 " expected=" + Twine(ExpectedValue));
86 }
Sam Cleggc1953142018-05-05 00:18:43 +000087 }
88}
89
Rui Ueyamabf450d92018-02-20 04:26:26 +000090// Copy this input chunk to an mmap'ed output file and apply relocations.
91void InputChunk::writeTo(uint8_t *Buf) const {
92 // Copy contents
93 memcpy(Buf + OutputOffset, data().data(), data().size());
Rui Ueyamac06d94a2018-02-19 22:39:52 +000094
Rui Ueyamabf450d92018-02-20 04:26:26 +000095 // Apply relocations
96 if (Relocations.empty())
97 return;
Rui Ueyamac06d94a2018-02-19 22:39:52 +000098
Sam Cleggc1953142018-05-05 00:18:43 +000099#ifndef NDEBUG
100 verifyRelocTargets();
101#endif
102
Nicola Zaghene7245b42018-05-15 13:36:20 +0000103 LLVM_DEBUG(dbgs() << "applying relocations: " << getName()
104 << " count=" << Relocations.size() << "\n");
Rui Ueyamabf450d92018-02-20 04:26:26 +0000105 int32_t Off = OutputOffset - getInputSectionOffset();
106
107 for (const WasmRelocation &Rel : Relocations) {
108 uint8_t *Loc = Buf + Rel.Offset + Off;
Sam Cleggc1be8232018-03-11 01:35:02 +0000109 uint32_t Value = File->calcNewValue(Rel);
Sam Clegge3a845e2019-03-29 22:56:39 +0000110 LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(Rel.Type)
Nicola Zaghene7245b42018-05-15 13:36:20 +0000111 << " addend=" << Rel.Addend << " index=" << Rel.Index
112 << " value=" << Value << " offset=" << Rel.Offset
113 << "\n");
Rui Ueyamabf450d92018-02-20 04:26:26 +0000114
115 switch (Rel.Type) {
Sam Clegg79e33172019-02-04 17:49:33 +0000116 case R_WASM_TYPE_INDEX_LEB:
117 case R_WASM_FUNCTION_INDEX_LEB:
118 case R_WASM_GLOBAL_INDEX_LEB:
119 case R_WASM_EVENT_INDEX_LEB:
120 case R_WASM_MEMORY_ADDR_LEB:
Rui Ueyamabf450d92018-02-20 04:26:26 +0000121 encodeULEB128(Value, Loc, 5);
122 break;
Sam Clegg79e33172019-02-04 17:49:33 +0000123 case R_WASM_TABLE_INDEX_SLEB:
Sam Clegg2a7cac92019-04-04 17:43:50 +0000124 case R_WASM_TABLE_INDEX_REL_SLEB:
Sam Clegg79e33172019-02-04 17:49:33 +0000125 case R_WASM_MEMORY_ADDR_SLEB:
Sam Clegg2a7cac92019-04-04 17:43:50 +0000126 case R_WASM_MEMORY_ADDR_REL_SLEB:
Rui Ueyamabf450d92018-02-20 04:26:26 +0000127 encodeSLEB128(static_cast<int32_t>(Value), Loc, 5);
128 break;
Sam Clegg79e33172019-02-04 17:49:33 +0000129 case R_WASM_TABLE_INDEX_I32:
130 case R_WASM_MEMORY_ADDR_I32:
131 case R_WASM_FUNCTION_OFFSET_I32:
132 case R_WASM_SECTION_OFFSET_I32:
Rui Ueyamabf450d92018-02-20 04:26:26 +0000133 write32le(Loc, Value);
134 break;
135 default:
136 llvm_unreachable("unknown relocation type");
Sam Cleggd96d9352018-01-10 19:22:42 +0000137 }
Sam Cleggd96d9352018-01-10 19:22:42 +0000138 }
139}
140
Rui Ueyamabf450d92018-02-20 04:26:26 +0000141// Copy relocation entries to a given output stream.
142// This function is used only when a user passes "-r". For a regular link,
143// we consume relocations instead of copying them to an output file.
144void InputChunk::writeRelocations(raw_ostream &OS) const {
Sam Clegg50686852018-01-12 18:35:13 +0000145 if (Relocations.empty())
146 return;
Rui Ueyamabf450d92018-02-20 04:26:26 +0000147
148 int32_t Off = OutputOffset - getInputSectionOffset();
Nicola Zaghene7245b42018-05-15 13:36:20 +0000149 LLVM_DEBUG(dbgs() << "writeRelocations: " << File->getName()
150 << " offset=" << Twine(Off) << "\n");
Sam Cleggd96d9352018-01-10 19:22:42 +0000151
Rui Ueyamabf450d92018-02-20 04:26:26 +0000152 for (const WasmRelocation &Rel : Relocations) {
153 writeUleb128(OS, Rel.Type, "reloc type");
154 writeUleb128(OS, Rel.Offset + Off, "reloc offset");
155 writeUleb128(OS, File->calcNewIndex(Rel), "reloc index");
Sam Cleggd96d9352018-01-10 19:22:42 +0000156
Rui Ueyamabf450d92018-02-20 04:26:26 +0000157 switch (Rel.Type) {
Sam Clegg79e33172019-02-04 17:49:33 +0000158 case R_WASM_MEMORY_ADDR_LEB:
159 case R_WASM_MEMORY_ADDR_SLEB:
160 case R_WASM_MEMORY_ADDR_I32:
161 case R_WASM_FUNCTION_OFFSET_I32:
162 case R_WASM_SECTION_OFFSET_I32:
Sam Cleggd177ab22018-05-04 23:14:42 +0000163 writeSleb128(OS, File->calcNewAddend(Rel), "reloc addend");
Rui Ueyamabf450d92018-02-20 04:26:26 +0000164 break;
165 }
Sam Cleggd96d9352018-01-10 19:22:42 +0000166 }
167}
Sam Clegg50686852018-01-12 18:35:13 +0000168
Sam Clegge3f3ccf2018-03-12 19:56:23 +0000169void InputFunction::setFunctionIndex(uint32_t Index) {
Nicola Zaghene7245b42018-05-15 13:36:20 +0000170 LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << getName()
171 << " -> " << Index << "\n");
Sam Clegge3f3ccf2018-03-12 19:56:23 +0000172 assert(!hasFunctionIndex());
173 FunctionIndex = Index;
Eric Christopher9ea500b2018-01-13 00:44:45 +0000174}
Sam Clegg67abf532018-01-24 21:45:25 +0000175
176void InputFunction::setTableIndex(uint32_t Index) {
Nicola Zaghene7245b42018-05-15 13:36:20 +0000177 LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << getName() << " -> "
178 << Index << "\n");
Sam Clegg67abf532018-01-24 21:45:25 +0000179 assert(!hasTableIndex());
180 TableIndex = Index;
181}
Sam Cleggfb983cd2018-05-18 23:28:05 +0000182
183// Write a relocation value without padding and return the number of bytes
184// witten.
185static unsigned writeCompressedReloc(uint8_t *Buf, const WasmRelocation &Rel,
186 uint32_t Value) {
187 switch (Rel.Type) {
Sam Clegg79e33172019-02-04 17:49:33 +0000188 case R_WASM_TYPE_INDEX_LEB:
189 case R_WASM_FUNCTION_INDEX_LEB:
190 case R_WASM_GLOBAL_INDEX_LEB:
191 case R_WASM_EVENT_INDEX_LEB:
192 case R_WASM_MEMORY_ADDR_LEB:
Sam Cleggfb983cd2018-05-18 23:28:05 +0000193 return encodeULEB128(Value, Buf);
Sam Clegg79e33172019-02-04 17:49:33 +0000194 case R_WASM_TABLE_INDEX_SLEB:
195 case R_WASM_MEMORY_ADDR_SLEB:
Sam Cleggfb983cd2018-05-18 23:28:05 +0000196 return encodeSLEB128(static_cast<int32_t>(Value), Buf);
Sam Cleggfb983cd2018-05-18 23:28:05 +0000197 default:
Sam Cleggf3770302018-05-22 20:52:20 +0000198 llvm_unreachable("unexpected relocation type");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000199 }
200}
201
202static unsigned getRelocWidthPadded(const WasmRelocation &Rel) {
203 switch (Rel.Type) {
Sam Clegg79e33172019-02-04 17:49:33 +0000204 case R_WASM_TYPE_INDEX_LEB:
205 case R_WASM_FUNCTION_INDEX_LEB:
206 case R_WASM_GLOBAL_INDEX_LEB:
207 case R_WASM_EVENT_INDEX_LEB:
208 case R_WASM_MEMORY_ADDR_LEB:
209 case R_WASM_TABLE_INDEX_SLEB:
210 case R_WASM_MEMORY_ADDR_SLEB:
Sam Cleggfb983cd2018-05-18 23:28:05 +0000211 return 5;
Sam Cleggfb983cd2018-05-18 23:28:05 +0000212 default:
Sam Cleggf3770302018-05-22 20:52:20 +0000213 llvm_unreachable("unexpected relocation type");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000214 }
215}
216
217static unsigned getRelocWidth(const WasmRelocation &Rel, uint32_t Value) {
218 uint8_t Buf[5];
219 return writeCompressedReloc(Buf, Rel, Value);
220}
221
222// Relocations of type LEB and SLEB in the code section are padded to 5 bytes
223// so that a fast linker can blindly overwrite them without needing to worry
224// about the number of bytes needed to encode the values.
225// However, for optimal output the code section can be compressed to remove
226// the padding then outputting non-relocatable files.
227// In this case we need to perform a size calculation based on the value at each
228// relocation. At best we end up saving 4 bytes for each relocation entry.
229//
230// This function only computes the final output size. It must be called
231// before getSize() is used to calculate of layout of the code section.
232void InputFunction::calculateSize() {
Sam Clegg4aad12c2018-09-27 00:46:54 +0000233 if (!File || !Config->CompressRelocations)
Sam Cleggfb983cd2018-05-18 23:28:05 +0000234 return;
235
Nicola Zaghen5c4fb452018-05-23 14:03:01 +0000236 LLVM_DEBUG(dbgs() << "calculateSize: " << getName() << "\n");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000237
238 const uint8_t *SecStart = File->CodeSection->Content.data();
239 const uint8_t *FuncStart = SecStart + getInputSectionOffset();
240 uint32_t FunctionSizeLength;
241 decodeULEB128(FuncStart, &FunctionSizeLength);
242
243 uint32_t Start = getInputSectionOffset();
244 uint32_t End = Start + Function->Size;
245
246 uint32_t LastRelocEnd = Start + FunctionSizeLength;
Sam Clegg47078f52018-08-22 17:50:51 +0000247 for (const WasmRelocation &Rel : Relocations) {
Nicola Zaghen5c4fb452018-05-23 14:03:01 +0000248 LLVM_DEBUG(dbgs() << " region: " << (Rel.Offset - LastRelocEnd) << "\n");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000249 CompressedFuncSize += Rel.Offset - LastRelocEnd;
250 CompressedFuncSize += getRelocWidth(Rel, File->calcNewValue(Rel));
251 LastRelocEnd = Rel.Offset + getRelocWidthPadded(Rel);
252 }
Nicola Zaghen5c4fb452018-05-23 14:03:01 +0000253 LLVM_DEBUG(dbgs() << " final region: " << (End - LastRelocEnd) << "\n");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000254 CompressedFuncSize += End - LastRelocEnd;
255
256 // Now we know how long the resulting function is we can add the encoding
257 // of its length
258 uint8_t Buf[5];
259 CompressedSize = CompressedFuncSize + encodeULEB128(CompressedFuncSize, Buf);
260
Nicola Zaghen5c4fb452018-05-23 14:03:01 +0000261 LLVM_DEBUG(dbgs() << " calculateSize orig: " << Function->Size << "\n");
262 LLVM_DEBUG(dbgs() << " calculateSize new: " << CompressedSize << "\n");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000263}
264
265// Override the default writeTo method so that we can (optionally) write the
266// compressed version of the function.
267void InputFunction::writeTo(uint8_t *Buf) const {
Sam Clegg4aad12c2018-09-27 00:46:54 +0000268 if (!File || !Config->CompressRelocations)
Sam Cleggfb983cd2018-05-18 23:28:05 +0000269 return InputChunk::writeTo(Buf);
270
271 Buf += OutputOffset;
Heejin Ahn4821ebf2018-08-29 21:03:16 +0000272 uint8_t *Orig = Buf;
273 (void)Orig;
Sam Cleggfb983cd2018-05-18 23:28:05 +0000274
275 const uint8_t *SecStart = File->CodeSection->Content.data();
276 const uint8_t *FuncStart = SecStart + getInputSectionOffset();
277 const uint8_t *End = FuncStart + Function->Size;
278 uint32_t Count;
Sam Clegg65a91282018-05-22 17:06:55 +0000279 decodeULEB128(FuncStart, &Count);
Sam Cleggfb983cd2018-05-18 23:28:05 +0000280 FuncStart += Count;
281
Nicola Zaghen5c4fb452018-05-23 14:03:01 +0000282 LLVM_DEBUG(dbgs() << "write func: " << getName() << "\n");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000283 Buf += encodeULEB128(CompressedFuncSize, Buf);
284 const uint8_t *LastRelocEnd = FuncStart;
285 for (const WasmRelocation &Rel : Relocations) {
286 unsigned ChunkSize = (SecStart + Rel.Offset) - LastRelocEnd;
Nicola Zaghen5c4fb452018-05-23 14:03:01 +0000287 LLVM_DEBUG(dbgs() << " write chunk: " << ChunkSize << "\n");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000288 memcpy(Buf, LastRelocEnd, ChunkSize);
289 Buf += ChunkSize;
290 Buf += writeCompressedReloc(Buf, Rel, File->calcNewValue(Rel));
291 LastRelocEnd = SecStart + Rel.Offset + getRelocWidthPadded(Rel);
292 }
293
294 unsigned ChunkSize = End - LastRelocEnd;
Nicola Zaghen5c4fb452018-05-23 14:03:01 +0000295 LLVM_DEBUG(dbgs() << " write final chunk: " << ChunkSize << "\n");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000296 memcpy(Buf, LastRelocEnd, ChunkSize);
Nicola Zaghen5c4fb452018-05-23 14:03:01 +0000297 LLVM_DEBUG(dbgs() << " total: " << (Buf + ChunkSize - Orig) << "\n");
Sam Cleggfb983cd2018-05-18 23:28:05 +0000298}
Sam Clegg09137be2019-04-04 18:40:51 +0000299
300// Generate code to apply relocations to the data section at runtime.
301// This is only called when generating shared libaries (PIC) where address are
302// not known at static link time.
303void InputSegment::generateRelocationCode(raw_ostream &OS) const {
Sam Cleggb685ddf2019-04-25 17:11:54 +0000304 LLVM_DEBUG(dbgs() << "generating runtime relocations: " << getName()
305 << " count=" << Relocations.size() << "\n");
306
307 // TODO(sbc): Encode the relocations in the data section and write a loop
308 // here to apply them.
Sam Clegg09137be2019-04-04 18:40:51 +0000309 uint32_t SegmentVA = OutputSeg->StartVA + OutputSegmentOffset;
310 for (const WasmRelocation &Rel : Relocations) {
311 uint32_t Offset = Rel.Offset - getInputSectionOffset();
Sam Cleggb685ddf2019-04-25 17:11:54 +0000312 uint32_t OutputOffset = SegmentVA + Offset;
313
314 LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(Rel.Type)
315 << " addend=" << Rel.Addend << " index=" << Rel.Index
316 << " output offset=" << OutputOffset << "\n");
Sam Clegg09137be2019-04-04 18:40:51 +0000317
318 // Get __memory_base
319 writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
320 writeUleb128(OS, WasmSym::MemoryBase->getGlobalIndex(), "memory_base");
321
322 // Add the offset of the relocation
323 writeU8(OS, WASM_OPCODE_I32_CONST, "I32_CONST");
Sam Cleggb685ddf2019-04-25 17:11:54 +0000324 writeSleb128(OS, OutputOffset, "offset");
Sam Clegg09137be2019-04-04 18:40:51 +0000325 writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
326
Sam Cleggb685ddf2019-04-25 17:11:54 +0000327 Symbol *Sym = File->getSymbol(Rel);
Sam Clegg09137be2019-04-04 18:40:51 +0000328 // Now figure out what we want to store
Sam Cleggb685ddf2019-04-25 17:11:54 +0000329 if (Sym->hasGOTIndex()) {
Sam Clegg09137be2019-04-04 18:40:51 +0000330 writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
Sam Cleggb685ddf2019-04-25 17:11:54 +0000331 writeUleb128(OS, Sym->getGOTIndex(), "global index");
332 if (Rel.Addend) {
Sam Clegg09137be2019-04-04 18:40:51 +0000333 writeU8(OS, WASM_OPCODE_I32_CONST, "CONST");
Sam Cleggb685ddf2019-04-25 17:11:54 +0000334 writeSleb128(OS, Rel.Addend, "addend");
Sam Clegg09137be2019-04-04 18:40:51 +0000335 writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
336 }
Sam Cleggb685ddf2019-04-25 17:11:54 +0000337 } else {
338 const GlobalSymbol* BaseSymbol = WasmSym::MemoryBase;
339 if (Rel.Type == R_WASM_TABLE_INDEX_I32)
340 BaseSymbol = WasmSym::TableBase;
341 writeU8(OS, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
342 writeUleb128(OS, BaseSymbol->getGlobalIndex(), "base");
343 writeU8(OS, WASM_OPCODE_I32_CONST, "CONST");
344 writeSleb128(OS, File->calcNewValue(Rel), "offset");
345 writeU8(OS, WASM_OPCODE_I32_ADD, "ADD");
Sam Clegg09137be2019-04-04 18:40:51 +0000346 }
347
348 // Store that value at the virtual address
349 writeU8(OS, WASM_OPCODE_I32_STORE, "I32_STORE");
350 writeUleb128(OS, 2, "align");
351 writeUleb128(OS, 0, "offset");
352 }
353}