jsilnet 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -3
  3. package/REFERENCE.md +191 -0
  4. package/dist/browser/index.js +345 -0
  5. package/dist/browser/worker.js +42480 -0
  6. package/example/README.md +22 -0
  7. package/example/Sample.cs +10 -0
  8. package/example/Sample.csproj +7 -0
  9. package/example/edit-constant.js +48 -0
  10. package/example/inspect.js +37 -0
  11. package/package.json +61 -6
  12. package/src/assembly/instruction-editing.js +165 -0
  13. package/src/assembly/method-removal.js +789 -0
  14. package/src/assembly/pointer-table-editing.js +506 -0
  15. package/src/assembly/portable-pdb-editing.js +441 -0
  16. package/src/binary/errors.js +17 -0
  17. package/src/binary/reader.js +206 -0
  18. package/src/browser/index.d.ts +123 -0
  19. package/src/browser/index.js +373 -0
  20. package/src/browser/worker-runtime.js +287 -0
  21. package/src/browser/worker.js +47 -0
  22. package/src/il/body.js +165 -0
  23. package/src/il/instructions.js +217 -0
  24. package/src/il/opcodes.js +102 -0
  25. package/src/il/rewrite.js +331 -0
  26. package/src/index.d.ts +267 -0
  27. package/src/index.js +13307 -0
  28. package/src/metadata/classic-pdb.js +2530 -0
  29. package/src/metadata/custom-attributes.js +387 -0
  30. package/src/metadata/portable-pdb.js +3347 -0
  31. package/src/metadata/resources.js +37 -0
  32. package/src/metadata/root-writer.js +54 -0
  33. package/src/metadata/signatures.js +339 -0
  34. package/src/metadata/streams.js +285 -0
  35. package/src/metadata/strong-name.js +230 -0
  36. package/src/metadata/tables.js +390 -0
  37. package/src/metadata/writer.js +191 -0
  38. package/src/pe/headers.js +491 -0
  39. package/src/platform/bytes.js +21 -0
  40. package/src/platform/compression.js +24 -0
  41. package/src/platform/crypto.js +17 -0
  42. package/src/resolution/module.js +64 -0
  43. package/src/types/assembly/instruction-editing.d.ts +33 -0
  44. package/src/types/assembly/method-removal.d.ts +3 -0
  45. package/src/types/assembly/pointer-table-editing.d.ts +3 -0
  46. package/src/types/assembly/portable-pdb-editing.d.ts +23 -0
  47. package/src/types/binary/errors.d.ts +7 -0
  48. package/src/types/binary/reader.d.ts +30 -0
  49. package/src/types/il/body.d.ts +51 -0
  50. package/src/types/il/instructions.d.ts +33 -0
  51. package/src/types/il/opcodes.d.ts +2 -0
  52. package/src/types/il/rewrite.d.ts +38 -0
  53. package/src/types/index.d.ts +864 -0
  54. package/src/types/metadata/classic-pdb.d.ts +688 -0
  55. package/src/types/metadata/custom-attributes.d.ts +28 -0
  56. package/src/types/metadata/portable-pdb.d.ts +296 -0
  57. package/src/types/metadata/resources.d.ts +1 -0
  58. package/src/types/metadata/root-writer.d.ts +1 -0
  59. package/src/types/metadata/signatures.d.ts +5 -0
  60. package/src/types/metadata/streams.d.ts +26 -0
  61. package/src/types/metadata/strong-name.d.ts +11 -0
  62. package/src/types/metadata/tables.d.ts +25 -0
  63. package/src/types/metadata/writer.d.ts +16 -0
  64. package/src/types/pe/headers.d.ts +160 -0
  65. package/src/types/platform/bytes.d.ts +3 -0
  66. package/src/types/platform/compression.d.ts +2 -0
  67. package/src/types/platform/crypto.d.ts +4 -0
  68. package/src/types/resolution/module.d.ts +12 -0
  69. package/src/types/writer/patch.d.ts +4 -0
  70. package/src/types/writer/pe.d.ts +9 -0
  71. package/src/types/writer/structural.d.ts +4 -0
  72. package/src/writer/patch.js +42 -0
  73. package/src/writer/pe.js +196 -0
  74. package/src/writer/structural.js +241 -0
@@ -0,0 +1,196 @@
1
+ import { FormatError, UnsupportedFormatError } from "../binary/errors.js";
2
+
3
+ function align(value, alignment) {
4
+ if (!Number.isInteger(alignment) || alignment < 1 || (alignment & (alignment - 1)) !== 0) {
5
+ throw new UnsupportedFormatError(`Unsupported PE alignment ${alignment}`);
6
+ }
7
+ return Math.ceil(value / alignment) * alignment;
8
+ }
9
+
10
+ function writeU16(view, offset, value, label) {
11
+ if (!Number.isInteger(value) || value < 0 || value > 0xffff) throw new RangeError(`${label} exceeds 16-bit range`);
12
+ view.setUint16(offset, value, true);
13
+ }
14
+
15
+ function writeU32(view, offset, value, label) {
16
+ if (!Number.isInteger(value) || value < 0 || value > 0xffffffff) throw new RangeError(`${label} exceeds 32-bit range`);
17
+ view.setUint32(offset, value, true);
18
+ }
19
+
20
+ export function getNextSectionVirtualAddress(pe) {
21
+ let maxVirtualEnd = pe.sizeOfImage;
22
+ for (const section of pe.sections) {
23
+ maxVirtualEnd = Math.max(maxVirtualEnd, section.virtualAddress + Math.max(section.virtualSize, section.rawSize));
24
+ }
25
+ return align(maxVirtualEnd, pe.sectionAlignment);
26
+ }
27
+
28
+ function expandHeaderForSection(input, pe) {
29
+ const firstRawPointer = Math.min(
30
+ ...pe.sections.filter((section) => section.rawSize > 0).map((section) => section.rawPointer),
31
+ pe.sizeOfHeaders
32
+ );
33
+ const sectionHeaderOffset = pe.sectionOffset + pe.sectionCount * 40;
34
+ const newHeaderEnd = sectionHeaderOffset + 40;
35
+ if (sectionHeaderOffset > firstRawPointer) {
36
+ throw new UnsupportedFormatError("PE section headers overlap section data");
37
+ }
38
+ const required = newHeaderEnd - Math.min(pe.sizeOfHeaders, firstRawPointer);
39
+ const delta = required > 0 ? align(required, pe.fileAlignment) : 0;
40
+ const insertionOffset = firstRawPointer;
41
+ const output = new Uint8Array(input.length + delta);
42
+ output.set(input.subarray(0, insertionOffset));
43
+ output.set(input.subarray(insertionOffset), insertionOffset + delta);
44
+ const view = new DataView(output.buffer);
45
+
46
+ if (delta > 0) {
47
+ for (let index = 0; index < pe.sections.length; index++) {
48
+ const header = pe.sectionOffset + index * 40;
49
+ for (const fieldOffset of [20, 24, 28]) {
50
+ const pointer = view.getUint32(header + fieldOffset, true);
51
+ if (pointer >= insertionOffset && pointer !== 0) view.setUint32(header + fieldOffset, pointer + delta, true);
52
+ }
53
+ }
54
+ if (pe.symbolTablePointer >= insertionOffset && pe.symbolTablePointer !== 0) {
55
+ view.setUint32(pe.peOffset + 12, pe.symbolTablePointer + delta, true);
56
+ }
57
+ view.setUint32(pe.optionalHeaderOffset + 60, pe.sizeOfHeaders + delta, true);
58
+ const debugDirectory = pe.directories[6];
59
+ if (debugDirectory?.rva && debugDirectory.size > 0) {
60
+ if (debugDirectory.size % 28 !== 0) {
61
+ throw new UnsupportedFormatError("Debug directory size is not a multiple of IMAGE_DEBUG_DIRECTORY");
62
+ }
63
+ const originalOffset = pe.rvaToOffset(debugDirectory.rva, debugDirectory.size);
64
+ if (originalOffset < insertionOffset && originalOffset + debugDirectory.size > insertionOffset) {
65
+ throw new UnsupportedFormatError("Debug directory crosses the PE header expansion boundary");
66
+ }
67
+ const debugOffset = originalOffset >= insertionOffset ? originalOffset + delta : originalOffset;
68
+ for (let offset = debugOffset; offset < debugOffset + debugDirectory.size; offset += 28) {
69
+ const pointer = view.getUint32(offset + 24, true);
70
+ if (pointer >= insertionOffset && pointer !== 0) view.setUint32(offset + 24, pointer + delta, true);
71
+ }
72
+ }
73
+ }
74
+ return { bytes: output, delta, insertionOffset, sectionHeaderOffset, firstRawPointer: firstRawPointer + delta };
75
+ }
76
+
77
+ export function appendPeSection(
78
+ input,
79
+ pe,
80
+ cli,
81
+ payload,
82
+ metadataOffset,
83
+ metadataSize,
84
+ resourcesDirectory = null,
85
+ preserveStrongName = false,
86
+ debugDirectoryPayload = null
87
+ ) {
88
+ if (payload.length === 0) throw new RangeError("Cannot add an empty PE section");
89
+ if (pe.sectionCount >= 0xffff) throw new UnsupportedFormatError("PE section-count limit reached");
90
+ const fileAlignment = pe.fileAlignment;
91
+ const sectionAlignment = pe.sectionAlignment;
92
+ const expanded = expandHeaderForSection(input, pe);
93
+ const rawPointer = align(expanded.bytes.length, fileAlignment);
94
+ const rawSize = align(payload.length, fileAlignment);
95
+ const sectionHeaderOffset = expanded.sectionHeaderOffset;
96
+ if (sectionHeaderOffset + 40 > pe.sizeOfHeaders + expanded.delta ||
97
+ sectionHeaderOffset + 40 > expanded.firstRawPointer) {
98
+ throw new UnsupportedFormatError("PE headers have no room for an additional section header");
99
+ }
100
+ const virtualAddress = getNextSectionVirtualAddress(pe);
101
+ const imageSize = align(virtualAddress + payload.length, sectionAlignment);
102
+ if (rawPointer + rawSize > 0xffffffff || imageSize > 0xffffffff) {
103
+ throw new RangeError("Rewritten PE exceeds 32-bit image limits");
104
+ }
105
+
106
+ const output = new Uint8Array(rawPointer + rawSize);
107
+ output.set(expanded.bytes);
108
+ output.set(payload, rawPointer);
109
+ const view = new DataView(output.buffer);
110
+ view.setUint16(pe.peOffset + 6, pe.sectionCount + 1, true);
111
+ writeU32(view, pe.optionalHeaderOffset + 4, pe.sizeOfCode + rawSize, "SizeOfCode");
112
+ writeU32(view, pe.optionalHeaderOffset + 56, imageSize, "SizeOfImage");
113
+ view.setUint32(pe.checksumOffset, 0, true);
114
+
115
+ output.set(new TextEncoder().encode(".jsilnet"), sectionHeaderOffset);
116
+ writeU32(view, sectionHeaderOffset + 8, payload.length, "section virtual size");
117
+ writeU32(view, sectionHeaderOffset + 12, virtualAddress, "section virtual address");
118
+ writeU32(view, sectionHeaderOffset + 16, rawSize, "section raw size");
119
+ writeU32(view, sectionHeaderOffset + 20, rawPointer, "section raw pointer");
120
+ writeU32(view, sectionHeaderOffset + 24, 0, "section relocation pointer");
121
+ writeU32(view, sectionHeaderOffset + 28, 0, "section line-number pointer");
122
+ writeU16(view, sectionHeaderOffset + 32, 0, "section relocation count");
123
+ writeU16(view, sectionHeaderOffset + 34, 0, "section line-number count");
124
+ writeU32(view, sectionHeaderOffset + 36, 0x60000020, "section characteristics");
125
+
126
+ const shiftedCliOffset = cli.offset >= expanded.insertionOffset ? cli.offset + expanded.delta : cli.offset;
127
+ if (metadataOffset !== null) {
128
+ const newMetadataRva = virtualAddress + metadataOffset;
129
+ writeU32(view, shiftedCliOffset + 8, newMetadataRva, "CLI metadata RVA");
130
+ writeU32(view, shiftedCliOffset + 12, metadataSize, "CLI metadata size");
131
+ }
132
+ if (resourcesDirectory) {
133
+ writeU32(view, shiftedCliOffset + 24, resourcesDirectory.rva, "CLI resources RVA");
134
+ writeU32(view, shiftedCliOffset + 28, resourcesDirectory.size, "CLI resources size");
135
+ }
136
+ if (!preserveStrongName && (cli.flags & 0x08) !== 0) {
137
+ const signature = cli.strongNameSignature;
138
+ if (signature.rva !== 0 && signature.size !== 0) {
139
+ const oldOffset = pe.rvaToOffset(signature.rva, signature.size);
140
+ const signatureOffset = oldOffset >= expanded.insertionOffset ? oldOffset + expanded.delta : oldOffset;
141
+ output.fill(0, signatureOffset, signatureOffset + signature.size);
142
+ }
143
+ writeU32(view, shiftedCliOffset + 16, cli.flags & ~0x08, "CLI flags");
144
+ writeU32(view, shiftedCliOffset + 32, 0, "strong-name RVA");
145
+ writeU32(view, shiftedCliOffset + 36, 0, "strong-name size");
146
+ }
147
+ if (debugDirectoryPayload) {
148
+ const directoryBase = pe.optionalHeaderOffset +
149
+ (pe.optionalHeaderMagic === 0x10b ? 96 : 112);
150
+ const directoryField = directoryBase + 6 * 8;
151
+ if (directoryField + 8 > pe.optionalHeaderOffset + pe.optionalHeaderSize) {
152
+ throw new UnsupportedFormatError("PE optional header has no debug data-directory slot");
153
+ }
154
+ writeU32(
155
+ view,
156
+ directoryField,
157
+ virtualAddress + debugDirectoryPayload.tableOffset,
158
+ "debug directory RVA"
159
+ );
160
+ writeU32(view, directoryField + 4, debugDirectoryPayload.size, "debug directory size");
161
+ for (const entry of debugDirectoryPayload.preservedEntries) {
162
+ const pointer = entry.pointer >= expanded.insertionOffset && entry.pointer !== 0
163
+ ? entry.pointer + expanded.delta
164
+ : entry.pointer;
165
+ writeU32(view, rawPointer + entry.pointerOffset, pointer, "debug record raw pointer");
166
+ }
167
+ for (const entry of debugDirectoryPayload.relocatedEntries) {
168
+ writeU32(
169
+ view,
170
+ rawPointer + entry.entryOffset + 16,
171
+ entry.size,
172
+ "debug record data size"
173
+ );
174
+ writeU32(
175
+ view,
176
+ rawPointer + entry.entryOffset + 20,
177
+ virtualAddress + entry.dataOffset,
178
+ "debug record data RVA"
179
+ );
180
+ writeU32(
181
+ view,
182
+ rawPointer + entry.entryOffset + 24,
183
+ rawPointer + entry.dataOffset,
184
+ "debug record raw pointer"
185
+ );
186
+ }
187
+ }
188
+ return {
189
+ bytes: output,
190
+ virtualAddress,
191
+ rawPointer,
192
+ rawSize,
193
+ headerExpansion: expanded.delta,
194
+ cliOffset: shiftedCliOffset
195
+ };
196
+ }
@@ -0,0 +1,241 @@
1
+ import { FormatError } from "../binary/errors.js";
2
+ import { serializeMetadataRoot } from "../metadata/root-writer.js";
3
+ import { serializeTablesStream } from "../metadata/writer.js";
4
+ import { encodeMethodBody } from "../il/rewrite.js";
5
+ import { appendPeSection, getNextSectionVirtualAddress } from "./pe.js";
6
+ import { applyPatches } from "./patch.js";
7
+
8
+ function align(value, alignment) {
9
+ return Math.ceil(value / alignment) * alignment;
10
+ }
11
+
12
+ function writeDebugDirectory(payload, tableOffset, assembly, replacements) {
13
+ const entries = assembly.pe.debugDirectoryEntries;
14
+ const tableSize = entries.length * 28;
15
+ const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
16
+ const preservedEntries = [];
17
+ const relocatedEntries = [];
18
+ let dataOffset = align(tableOffset + tableSize, 4);
19
+ for (let index = 0; index < entries.length; index++) {
20
+ const entry = entries[index];
21
+ const entryOffset = tableOffset + index * 28;
22
+ const replacementValue = replacements.get(index);
23
+ const replacement = replacementValue?.bytes ?? replacementValue;
24
+ view.setUint32(entryOffset, entry.characteristics, true);
25
+ view.setUint32(
26
+ entryOffset + 4,
27
+ replacementValue?.timeDateStamp ?? entry.timeDateStamp,
28
+ true
29
+ );
30
+ view.setUint16(entryOffset + 8, entry.majorVersion, true);
31
+ view.setUint16(entryOffset + 10, entry.minorVersion, true);
32
+ view.setUint32(entryOffset + 12, entry.type, true);
33
+ if (replacement) {
34
+ view.setUint32(entryOffset + 16, replacement.length, true);
35
+ relocatedEntries.push({
36
+ entryOffset,
37
+ dataOffset,
38
+ size: replacement.length
39
+ });
40
+ payload.set(replacement, dataOffset);
41
+ dataOffset = align(dataOffset + replacement.length, 4);
42
+ } else {
43
+ view.setUint32(entryOffset + 16, entry.sizeOfData, true);
44
+ view.setUint32(entryOffset + 20, entry.addressOfRawData, true);
45
+ view.setUint32(entryOffset + 24, entry.pointerToRawData, true);
46
+ if (entry.pointerToRawData !== 0) {
47
+ preservedEntries.push({
48
+ pointerOffset: entryOffset + 24,
49
+ pointer: entry.pointerToRawData
50
+ });
51
+ }
52
+ }
53
+ }
54
+ return {
55
+ size: tableSize,
56
+ preservedEntries,
57
+ relocatedEntries,
58
+ endOffset: dataOffset
59
+ };
60
+ }
61
+
62
+ function cloneTables(tables) {
63
+ const rows = new Map();
64
+ for (const [id, tableRows] of tables.rows) {
65
+ rows.set(id, tableRows.map((row) => {
66
+ const copy = { ...row };
67
+ for (const [key, value] of Object.entries(copy)) {
68
+ if (value && typeof value === "object") {
69
+ copy[key] = value.target !== undefined
70
+ ? { ...value, target: value.target ? { ...value.target } : null }
71
+ : { ...value };
72
+ }
73
+ }
74
+ return copy;
75
+ }));
76
+ }
77
+ return { ...tables, rows, counts: [...tables.counts] };
78
+ }
79
+
80
+ function getHeapStreams(assembly) {
81
+ if (assembly._heapStreams) {
82
+ return new Map([...assembly._heapStreams].map(([name, bytes]) => [name, bytes.slice()]));
83
+ }
84
+ return new Map([...assembly.metadataRoot.streams].map(([name, stream]) => [name, stream.bytes.slice()]));
85
+ }
86
+
87
+ function buildMetadata(assembly, tables, heapStreams, tableStreamName) {
88
+ const streams = new Map(heapStreams);
89
+ streams.set(tableStreamName, serializeTablesStream(tables, heapStreams));
90
+ return serializeMetadataRoot(assembly.metadataRoot, streams);
91
+ }
92
+
93
+ function buildResourcePayload(assembly, tables) {
94
+ if (assembly._resourceEdits.size === 0) return null;
95
+ const rows = tables.rows.get(40) ?? [];
96
+ const entries = [];
97
+ let size = 0;
98
+ for (const resource of assembly.resources) {
99
+ if (resource.implementation !== null) continue;
100
+ const data = assembly._resourceEdits.get(resource.rid) ?? resource.data;
101
+ if (!(data instanceof Uint8Array)) {
102
+ throw new FormatError(`Embedded ManifestResource row ${resource.rid} has no payload`);
103
+ }
104
+ const offset = align(size, 8);
105
+ if (offset > 0xffffffff) throw new RangeError("Manifest resource offset exceeds 32-bit range");
106
+ const row = rows[resource.rid - 1];
107
+ if (!row || row.rid !== resource.rid) {
108
+ throw new FormatError(`ManifestResource row ${resource.rid} is missing from the metadata tables`);
109
+ }
110
+ row.Offset = offset;
111
+ if (data.length > 0xffffffff) throw new RangeError("Manifest resource exceeds the CLI size limit");
112
+ entries.push({ offset, data });
113
+ size = offset + 4 + data.length;
114
+ if (size > 0xffffffff) throw new RangeError("CLI resources directory exceeds 32-bit range");
115
+ }
116
+ if (entries.length === 0) throw new FormatError("No embedded resources are available for replacement");
117
+ const output = new Uint8Array(size);
118
+ const view = new DataView(output.buffer);
119
+ for (const entry of entries) {
120
+ view.setUint32(entry.offset, entry.data.length, true);
121
+ output.set(entry.data, entry.offset + 4);
122
+ }
123
+ return output;
124
+ }
125
+
126
+ export function serializeStructural(
127
+ assembly,
128
+ { preserveStrongName = false, strongNameSignatureSize = 0 } = {}
129
+ ) {
130
+ const source = applyPatches(assembly.bytes, assembly._patches, assembly.cli, assembly.pe, {
131
+ preserveStrongName
132
+ });
133
+ const tables = cloneTables(assembly.tables);
134
+ const heapStreams = getHeapStreams(assembly);
135
+ const tableStreamName = assembly.metadataRoot.streams.has("#~") ? "#~" : "#-";
136
+ let metadata = buildMetadata(assembly, tables, heapStreams, tableStreamName);
137
+ const virtualAddress = getNextSectionVirtualAddress(assembly.pe);
138
+ let bodyOffset = align(metadata.length, 4);
139
+ const encodedBodies = [];
140
+ const bodies = new Map(assembly._addedMethodBodies ?? []);
141
+ for (const [methodRid, state] of assembly._structuralBodies) bodies.set(methodRid, state);
142
+ for (const [methodRid, state] of [...bodies].sort(([left], [right]) => left - right)) {
143
+ const methodRow = tables.rows.get(6)?.[methodRid - 1];
144
+ if (!methodRow) throw new FormatError(`MethodDef row ${methodRid} is missing from the metadata tables`);
145
+ const methodRva = virtualAddress + bodyOffset;
146
+ if (methodRva > 0xffffffff) throw new RangeError("Rewritten method RVA exceeds 32-bit range");
147
+ const bytes = state instanceof Uint8Array
148
+ ? state
149
+ : encodeMethodBody(state, methodRva);
150
+ methodRow.RVA = methodRva;
151
+ encodedBodies.push({ offset: bodyOffset, bytes });
152
+ bodyOffset = align(bodyOffset + bytes.length, 4);
153
+ }
154
+ const resourcePayload = buildResourcePayload(assembly, tables);
155
+ const resourceOffset = resourcePayload ? align(bodyOffset, 8) : 0;
156
+ const resourceEnd = resourcePayload ? resourceOffset + resourcePayload.length : bodyOffset;
157
+ const signatureOffset = strongNameSignatureSize > 0 ? align(resourceEnd, 4) : 0;
158
+ const signatureEnd = strongNameSignatureSize > 0
159
+ ? signatureOffset + strongNameSignatureSize
160
+ : resourceEnd;
161
+ const hasDebugEdits = (assembly._embeddedPortablePdbEdits?.size ?? 0) > 0;
162
+ const debugTableOffset = hasDebugEdits ? align(signatureEnd, 4) : 0;
163
+ const debugTableSize = hasDebugEdits
164
+ ? assembly.pe.debugDirectoryEntries.length * 28
165
+ : 0;
166
+ const debugDataStart = hasDebugEdits
167
+ ? align(debugTableOffset + debugTableSize, 4)
168
+ : 0;
169
+ const debugRecordEdits = new Map(assembly._embeddedPortablePdbEdits ?? []);
170
+ for (const [index, edit] of assembly._debugRecordEdits ?? []) {
171
+ debugRecordEdits.set(index, edit);
172
+ }
173
+ let finalPayloadSize = hasDebugEdits ? debugDataStart : signatureEnd;
174
+ if (hasDebugEdits) {
175
+ for (const [index, replacementValue] of [...debugRecordEdits].sort(
176
+ ([left], [right]) => left - right
177
+ )) {
178
+ if (!assembly.pe.debugDirectoryEntries[index]) {
179
+ throw new FormatError(`Debug-directory entry ${index} is missing`);
180
+ }
181
+ const replacement = replacementValue?.bytes ?? replacementValue;
182
+ finalPayloadSize = align(finalPayloadSize, 4) + replacement.length;
183
+ }
184
+ }
185
+ const rewrittenMetadata = buildMetadata(assembly, tables, heapStreams, tableStreamName);
186
+ if (rewrittenMetadata.length !== metadata.length) {
187
+ throw new FormatError("Metadata size changed while assigning method RVAs");
188
+ }
189
+ metadata = rewrittenMetadata;
190
+
191
+ const payload = new Uint8Array(finalPayloadSize);
192
+ payload.set(metadata);
193
+ for (const body of encodedBodies) payload.set(body.bytes, body.offset);
194
+ if (resourcePayload) payload.set(resourcePayload, resourceOffset);
195
+ let debugDirectoryPayload = null;
196
+ if (hasDebugEdits) {
197
+ debugDirectoryPayload = writeDebugDirectory(
198
+ payload,
199
+ debugTableOffset,
200
+ assembly,
201
+ debugRecordEdits
202
+ );
203
+ }
204
+ const resourcesDirectory = resourcePayload
205
+ ? { rva: virtualAddress + resourceOffset, size: resourcePayload.length }
206
+ : assembly._clearResourcesDirectory
207
+ ? { rva: 0, size: 0 }
208
+ : null;
209
+ const appended = appendPeSection(
210
+ source,
211
+ assembly.pe,
212
+ assembly.cli,
213
+ payload,
214
+ 0,
215
+ metadata.length,
216
+ resourcesDirectory,
217
+ preserveStrongName,
218
+ hasDebugEdits
219
+ ? {
220
+ tableOffset: debugTableOffset,
221
+ size: debugDirectoryPayload.size,
222
+ preservedEntries: debugDirectoryPayload.preservedEntries,
223
+ relocatedEntries: debugDirectoryPayload.relocatedEntries
224
+ }
225
+ : null
226
+ );
227
+ if (strongNameSignatureSize > 0) {
228
+ const signatureRva = virtualAddress + signatureOffset;
229
+ if (signatureRva > 0xffffffff) throw new RangeError("Strong-name signature RVA exceeds 32-bit range");
230
+ const view = new DataView(
231
+ appended.bytes.buffer,
232
+ appended.bytes.byteOffset,
233
+ appended.bytes.byteLength
234
+ );
235
+ const directoryOffset = appended.cliOffset +
236
+ assembly.cli.strongNameDirectoryOffset - assembly.cli.offset;
237
+ view.setUint32(directoryOffset, signatureRva, true);
238
+ view.setUint32(directoryOffset + 4, strongNameSignatureSize, true);
239
+ }
240
+ return appended.bytes;
241
+ }