Skip to main content

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        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        // Serialize the heaps and tables.
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        let mut guids = vec![0u8; 16]; // zero guid
101        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        // Writing a union field is safe; the union variants (VirtualSize and PhysicalAddress)
187        // are both u32, so any bit pattern is valid for either variant.
188        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(&section);
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}