windows_metadata/writer/file/
into_stream.rs1use super::*;
2
3const SECTION_ALIGNMENT: u32 = 4096;
4
5#[repr(C)]
6#[derive(Default)]
7struct METADATA_HEADER {
8 signature: u32,
9 major_version: u16,
10 minor_version: u16,
11 reserved: u32,
12 length: u32,
13 version: [u8; 20],
14 flags: u16,
15 streams: u16,
16}
17
18const METADATA_SIGNATURE: u32 = 0x424A_5342;
19
20#[repr(C)]
21struct STREAM_HEADER<const LEN: usize> {
22 offset: u32,
23 size: u32,
24 name: [u8; LEN],
25}
26
27impl<const LEN: usize> STREAM_HEADER<LEN> {
28 fn new(offset: u32, size: u32, name: &[u8; LEN]) -> Self {
29 Self {
30 offset,
31 size,
32 name: *name,
33 }
34 }
35 fn next_offset(&self) -> u32 {
36 self.offset + self.size
37 }
38}
39
40impl File {
41 pub fn into_stream(mut self) -> Vec<u8> {
42 self.records.Constant.extend(self.Constant.values());
43
44 self.records
45 .Attribute
46 .extend(self.Attribute.values().flatten());
47
48 self.records
49 .GenericParam
50 .extend(self.GenericParam.values().flatten());
51
52 debug_assert!(
53 self.records
54 .ClassLayout
55 .iter()
56 .map(|r| r.Parent)
57 .is_sorted()
58 );
59
60 debug_assert!(self.records.Constant.iter().map(|r| r.Parent).is_sorted());
61 debug_assert!(self.records.Attribute.iter().map(|r| r.Parent).is_sorted());
62
63 debug_assert!(
64 self.records
65 .GenericParam
66 .iter()
67 .map(|r| r.Owner)
68 .is_sorted()
69 );
70
71 debug_assert!(
72 self.records
73 .ImplMap
74 .iter()
75 .map(|r| r.MemberForwarded)
76 .is_sorted()
77 );
78
79 debug_assert!(
80 self.records
81 .NestedClass
82 .iter()
83 .map(|r| r.NestedClass)
84 .is_sorted()
85 );
86
87 let mut strings = self.strings.into_stream();
90 let mut blobs = self.blobs.into_stream();
91 let mut records = self.records.into_stream();
92
93 if [records.len(), strings.len(), blobs.len()]
94 .iter()
95 .any(|len| *len > u32::MAX as usize)
96 {
97 panic!("heap too large");
98 }
99
100 let mut guids = vec![0u8; 16]; let size_of_streams = records.len() + guids.len() + strings.len() + blobs.len();
102
103 let dos = IMAGE_DOS_HEADER {
104 e_magic: IMAGE_DOS_SIGNATURE as u16,
105 e_lfarlc: 64,
106 e_lfanew: size_of::<IMAGE_DOS_HEADER>() as i32,
107 ..Default::default()
108 };
109
110 let file = IMAGE_FILE_HEADER {
111 Machine: IMAGE_FILE_MACHINE_I386 as u16,
112 NumberOfSections: 1,
113 SizeOfOptionalHeader: size_of::<IMAGE_OPTIONAL_HEADER32>() as u16,
114 Characteristics: (IMAGE_FILE_DLL
115 | IMAGE_FILE_32BIT_MACHINE
116 | IMAGE_FILE_EXECUTABLE_IMAGE) as u16,
117 ..Default::default()
118 };
119
120 let mut optional = IMAGE_OPTIONAL_HEADER32 {
121 Magic: IMAGE_NT_OPTIONAL_HDR32_MAGIC as u16,
122 MajorLinkerVersion: 11,
123 SizeOfInitializedData: 1024,
124 ImageBase: 0x400000,
125 SectionAlignment: SECTION_ALIGNMENT,
126 FileAlignment: 512,
127 MajorOperatingSystemVersion: 6,
128 MinorOperatingSystemVersion: 2,
129 MajorSubsystemVersion: 6,
130 MinorSubsystemVersion: 2,
131 SizeOfHeaders: 512,
132 Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI as u16,
133 DllCharacteristics: (IMAGE_DLLCHARACTERISTICS_NX_COMPAT
134 | IMAGE_DLLCHARACTERISTICS_NO_SEH
135 | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) as u16,
136 SizeOfStackReserve: 0x100000,
137 SizeOfHeapReserve: 4096,
138 LoaderFlags: 0x100000,
139 NumberOfRvaAndSizes: 16,
140 ..Default::default()
141 };
142
143 let mut section = IMAGE_SECTION_HEADER {
144 Name: *b".text\0\0\0",
145 Characteristics: 0x4000_0020,
146 VirtualAddress: SECTION_ALIGNMENT,
147 ..Default::default()
148 };
149
150 let mut clr = IMAGE_COR20_HEADER {
151 cb: size_of::<IMAGE_COR20_HEADER>() as u32,
152 MajorRuntimeVersion: 2,
153 MinorRuntimeVersion: 5,
154 Flags: 1,
155 ..Default::default()
156 };
157
158 let metadata = METADATA_HEADER {
159 signature: METADATA_SIGNATURE,
160 major_version: 1,
161 minor_version: 1,
162 length: 20,
163 version: *b"WindowsRuntime 1.4\0\0",
164 streams: 4,
165 ..Default::default()
166 };
167
168 type TablesHeader = STREAM_HEADER<4>;
169 type StringsHeader = STREAM_HEADER<12>;
170 type GuidsHeader = STREAM_HEADER<8>;
171 type BlobsHeader = STREAM_HEADER<8>;
172
173 let size_of_stream_headers = size_of::<TablesHeader>()
174 + size_of::<StringsHeader>()
175 + size_of::<GuidsHeader>()
176 + size_of::<BlobsHeader>();
177
178 let size_of_image = optional.FileAlignment as usize
179 + size_of::<IMAGE_COR20_HEADER>()
180 + size_of::<METADATA_HEADER>()
181 + size_of_stream_headers
182 + size_of_streams;
183
184 let virtual_size = size_of_image as u32 - optional.FileAlignment;
185
186 section.Misc.VirtualSize = virtual_size;
189
190 section.SizeOfRawData =
191 round(virtual_size as usize, optional.FileAlignment as usize) as u32;
192
193 optional.SizeOfImage = round(
194 SECTION_ALIGNMENT as usize + section.SizeOfRawData as usize,
195 optional.SectionAlignment as usize,
196 ) as u32;
197
198 optional.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR as usize] =
199 IMAGE_DATA_DIRECTORY {
200 VirtualAddress: SECTION_ALIGNMENT,
201 Size: size_of::<IMAGE_COR20_HEADER>() as u32,
202 };
203
204 section.PointerToRawData = optional.FileAlignment;
205
206 clr.MetaData.VirtualAddress = SECTION_ALIGNMENT + size_of::<IMAGE_COR20_HEADER>() as u32;
207
208 clr.MetaData.Size = virtual_size - size_of::<IMAGE_COR20_HEADER>() as u32;
209
210 let mut buffer = Vec::<u8>::new();
211 buffer.write_header(&dos);
212 buffer.write_u32(IMAGE_NT_SIGNATURE as u32);
213 buffer.write_header(&file);
214 buffer.write_header(&optional);
215 buffer.write_header(§ion);
216 debug_assert!(buffer.len() < optional.FileAlignment as usize);
217 buffer.resize(optional.FileAlignment as usize, 0);
218 buffer.write_header(&clr);
219 let metadata_offset = buffer.len();
220 buffer.write_header(&metadata);
221
222 let stream_offset = buffer.len() - metadata_offset + size_of_stream_headers;
223
224 let tables_header =
225 TablesHeader::new(stream_offset as u32, records.len() as u32, b"#~\0\0");
226
227 let strings_header = StringsHeader::new(
228 tables_header.next_offset(),
229 strings.len() as u32,
230 b"#Strings\0\0\0\0",
231 );
232
233 let guids_header = GuidsHeader::new(
234 strings_header.next_offset(),
235 guids.len() as u32,
236 b"#GUID\0\0\0",
237 );
238
239 let blobs_header = BlobsHeader::new(
240 guids_header.next_offset(),
241 blobs.len() as u32,
242 b"#Blob\0\0\0",
243 );
244
245 buffer.write_header(&tables_header);
246 buffer.write_header(&strings_header);
247 buffer.write_header(&guids_header);
248 buffer.write_header(&blobs_header);
249
250 buffer.append(&mut records);
251 buffer.append(&mut strings);
252 buffer.append(&mut guids);
253 buffer.append(&mut blobs);
254
255 let unpadded_size = buffer.len();
256 buffer.resize(
257 optional.FileAlignment as usize + section.SizeOfRawData as usize,
258 0,
259 );
260
261 assert_eq!(clr.MetaData.Size as usize, unpadded_size - metadata_offset);
262 assert_eq!(
263 optional.FileAlignment as usize + section.SizeOfRawData as usize,
264 buffer.len()
265 );
266
267 buffer
268 }
269}