windows_metadata/writer/file/
into_stream.rs

1use 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        // Flatten sorted records...
43
44        self.records.Constant.extend(self.Constant.values());
45
46        self.records
47            .Attribute
48            .extend(self.Attribute.values().flatten());
49
50        self.records
51            .GenericParam
52            .extend(self.GenericParam.values().flatten());
53
54        // Test sorted order...
55
56        debug_assert!(self
57            .records
58            .ClassLayout
59            .iter()
60            .map(|r| r.Parent)
61            .is_sorted());
62
63        debug_assert!(self.records.Constant.iter().map(|r| r.Parent).is_sorted());
64        debug_assert!(self.records.Attribute.iter().map(|r| r.Parent).is_sorted());
65
66        debug_assert!(self
67            .records
68            .GenericParam
69            .iter()
70            .map(|r| r.Owner)
71            .is_sorted());
72
73        debug_assert!(self
74            .records
75            .ImplMap
76            .iter()
77            .map(|r| r.MemberForwarded)
78            .is_sorted());
79
80        debug_assert!(self
81            .records
82            .NestedClass
83            .iter()
84            .map(|r| r.NestedClass)
85            .is_sorted());
86
87        // Serialize...
88
89        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        unsafe {
101            let mut guids = vec![0; 16]; // zero guid
102            let size_of_streams = records.len() + guids.len() + strings.len() + blobs.len();
103
104            let mut dos: IMAGE_DOS_HEADER = core::mem::zeroed();
105            dos.e_magic = IMAGE_DOS_SIGNATURE;
106            dos.e_lfarlc = 64;
107            dos.e_lfanew = core::mem::size_of::<IMAGE_DOS_HEADER>() as i32;
108
109            let mut file: IMAGE_FILE_HEADER = core::mem::zeroed();
110            file.Machine = IMAGE_FILE_MACHINE_I386;
111            file.NumberOfSections = 1;
112            file.SizeOfOptionalHeader = core::mem::size_of::<IMAGE_OPTIONAL_HEADER32>() as u16;
113            file.Characteristics =
114                IMAGE_FILE_DLL | IMAGE_FILE_32BIT_MACHINE | IMAGE_FILE_EXECUTABLE_IMAGE;
115
116            let mut optional: IMAGE_OPTIONAL_HEADER32 = core::mem::zeroed();
117            optional.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC;
118            optional.MajorLinkerVersion = 11;
119            optional.SizeOfInitializedData = 1024;
120            optional.ImageBase = 0x400000;
121            optional.SectionAlignment = SECTION_ALIGNMENT;
122            optional.FileAlignment = 512;
123            optional.MajorOperatingSystemVersion = 6;
124            optional.MinorOperatingSystemVersion = 2;
125            optional.MajorSubsystemVersion = 6;
126            optional.MinorSubsystemVersion = 2;
127            optional.SizeOfHeaders = 512;
128            optional.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
129            optional.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_NX_COMPAT
130                | IMAGE_DLLCHARACTERISTICS_NO_SEH
131                | IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
132            optional.SizeOfStackReserve = 0x100000;
133            optional.SizeOfHeapReserve = 4096;
134            optional.LoaderFlags = 0x100000;
135            optional.NumberOfRvaAndSizes = 16;
136
137            let mut section: IMAGE_SECTION_HEADER = core::mem::zeroed();
138            section.Name = *b".text\0\0\0";
139            section.Characteristics = 0x4000_0020;
140            section.VirtualAddress = SECTION_ALIGNMENT;
141
142            let mut clr: IMAGE_COR20_HEADER = core::mem::zeroed();
143            clr.cb = core::mem::size_of::<IMAGE_COR20_HEADER>() as u32;
144            clr.MajorRuntimeVersion = 2;
145            clr.MinorRuntimeVersion = 5;
146            clr.Flags = 1;
147
148            let metadata = METADATA_HEADER {
149                signature: METADATA_SIGNATURE,
150                major_version: 1,
151                minor_version: 1,
152                length: 20,
153                version: *b"WindowsRuntime 1.4\0\0",
154                streams: 4,
155                ..Default::default()
156            };
157
158            type TablesHeader = STREAM_HEADER<4>;
159            type StringsHeader = STREAM_HEADER<12>;
160            type GuidsHeader = STREAM_HEADER<8>;
161            type BlobsHeader = STREAM_HEADER<8>;
162
163            let size_of_stream_headers = core::mem::size_of::<TablesHeader>()
164                + core::mem::size_of::<StringsHeader>()
165                + core::mem::size_of::<GuidsHeader>()
166                + core::mem::size_of::<BlobsHeader>();
167
168            let size_of_image = optional.FileAlignment as usize
169                + core::mem::size_of::<IMAGE_COR20_HEADER>()
170                + core::mem::size_of::<METADATA_HEADER>()
171                + size_of_stream_headers
172                + size_of_streams;
173
174            optional.SizeOfImage = round(size_of_image, optional.SectionAlignment as usize) as u32;
175            section.Misc.VirtualSize = size_of_image as u32 - optional.FileAlignment;
176
177            section.SizeOfRawData = round(
178                section.Misc.VirtualSize as usize,
179                optional.FileAlignment as usize,
180            ) as u32;
181
182            optional.DataDirectory[14] = IMAGE_DATA_DIRECTORY {
183                VirtualAddress: SECTION_ALIGNMENT,
184                Size: core::mem::size_of::<IMAGE_COR20_HEADER>() as u32,
185            };
186
187            section.PointerToRawData = optional.FileAlignment;
188
189            clr.MetaData.VirtualAddress =
190                SECTION_ALIGNMENT + core::mem::size_of::<IMAGE_COR20_HEADER>() as u32;
191
192            clr.MetaData.Size =
193                section.Misc.VirtualSize - core::mem::size_of::<IMAGE_COR20_HEADER>() as u32;
194
195            let mut buffer = Vec::<u8>::new();
196            buffer.write_header(&dos);
197            buffer.write_u32(IMAGE_NT_SIGNATURE);
198            buffer.write_header(&file);
199            buffer.write_header(&optional);
200            buffer.write_header(&section);
201            debug_assert!(buffer.len() < optional.FileAlignment as usize);
202            buffer.resize(optional.FileAlignment as usize, 0);
203            buffer.write_header(&clr);
204            let metadata_offset = buffer.len();
205            buffer.write_header(&metadata);
206
207            let stream_offset = buffer.len() - metadata_offset + size_of_stream_headers;
208
209            let tables_header =
210                TablesHeader::new(stream_offset as u32, records.len() as u32, b"#~\0\0");
211
212            let strings_header = StringsHeader::new(
213                tables_header.next_offset(),
214                strings.len() as u32,
215                b"#Strings\0\0\0\0",
216            );
217
218            let guids_header = GuidsHeader::new(
219                strings_header.next_offset(),
220                guids.len() as u32,
221                b"#GUID\0\0\0",
222            );
223
224            let blobs_header = BlobsHeader::new(
225                guids_header.next_offset(),
226                blobs.len() as u32,
227                b"#Blob\0\0\0",
228            );
229
230            buffer.write_header(&tables_header);
231            buffer.write_header(&strings_header);
232            buffer.write_header(&guids_header);
233            buffer.write_header(&blobs_header);
234
235            buffer.append(&mut records);
236            buffer.append(&mut strings);
237            buffer.append(&mut guids);
238            buffer.append(&mut blobs);
239
240            assert_eq!(clr.MetaData.Size as usize, buffer.len() - metadata_offset);
241            assert_eq!(size_of_image, buffer.len());
242
243            buffer
244        }
245    }
246}