diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8a22186dae..9be8e7f3c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -145,8 +145,10 @@ jobs: cargo clippy -p test_weak_ref && cargo clippy -p test_win32 && cargo clippy -p test_win32_arrays && + cargo clippy -p test_writer && cargo clippy -p tool_bindings && cargo clippy -p tool_gnu && + cargo clippy -p tool_ilrs && cargo clippy -p tool_msvc && cargo clippy -p tool_sys && cargo clippy -p tool_windows && diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b6db49e741..2bb3b6592a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -100,8 +100,10 @@ jobs: cargo test --target ${{ matrix.target }} -p test_weak_ref && cargo test --target ${{ matrix.target }} -p test_win32 && cargo test --target ${{ matrix.target }} -p test_win32_arrays && + cargo test --target ${{ matrix.target }} -p test_writer && cargo test --target ${{ matrix.target }} -p tool_bindings && cargo test --target ${{ matrix.target }} -p tool_gnu && + cargo test --target ${{ matrix.target }} -p tool_ilrs && cargo test --target ${{ matrix.target }} -p tool_msvc && cargo test --target ${{ matrix.target }} -p tool_sys && cargo test --target ${{ matrix.target }} -p tool_windows && diff --git a/crates/libs/bindgen/src/lib.rs b/crates/libs/bindgen/src/lib.rs index cc745b1449..d800262692 100644 --- a/crates/libs/bindgen/src/lib.rs +++ b/crates/libs/bindgen/src/lib.rs @@ -23,7 +23,7 @@ use functions::*; pub use gen::*; use helpers::*; use iterator::*; -use metadata::*; +use metadata::reader::*; use method_names::*; use methods::*; use names::*; diff --git a/crates/libs/metadata/src/lib.rs b/crates/libs/metadata/src/lib.rs index 3534ee8945..c9134a0edb 100644 --- a/crates/libs/metadata/src/lib.rs +++ b/crates/libs/metadata/src/lib.rs @@ -1,43 +1,2 @@ -mod array_info; -mod async_kind; -mod blob; -mod cfg; -mod codes; -mod constant_value; -mod file; -mod guid; -mod interface_kind; -mod param_flags; -mod row; -mod signature; -mod signature_kind; -mod tables; -mod traits; -mod r#type; -mod type_kind; -mod type_name; -mod type_reader; -mod type_tree; -mod workspace; - -pub use array_info::*; -pub use async_kind::*; -pub use blob::*; -pub use cfg::*; -pub use codes::*; -pub use constant_value::*; -pub use file::*; -pub use guid::*; -pub use interface_kind::*; -pub use param_flags::*; -pub use r#type::*; -pub use row::*; -pub use signature::*; -pub use signature_kind::*; -pub use tables::*; -pub use traits::*; -pub use type_kind::*; -pub use type_name::*; -pub use type_reader::*; -pub use type_tree::*; -pub use workspace::*; +pub mod reader; +pub mod writer; diff --git a/crates/libs/metadata/src/array_info.rs b/crates/libs/metadata/src/reader/array_info.rs similarity index 100% rename from crates/libs/metadata/src/array_info.rs rename to crates/libs/metadata/src/reader/array_info.rs diff --git a/crates/libs/metadata/src/async_kind.rs b/crates/libs/metadata/src/reader/async_kind.rs similarity index 100% rename from crates/libs/metadata/src/async_kind.rs rename to crates/libs/metadata/src/reader/async_kind.rs diff --git a/crates/libs/metadata/src/blob.rs b/crates/libs/metadata/src/reader/blob.rs similarity index 100% rename from crates/libs/metadata/src/blob.rs rename to crates/libs/metadata/src/reader/blob.rs diff --git a/crates/libs/metadata/src/cfg.rs b/crates/libs/metadata/src/reader/cfg.rs similarity index 100% rename from crates/libs/metadata/src/cfg.rs rename to crates/libs/metadata/src/reader/cfg.rs diff --git a/crates/libs/metadata/src/codes.rs b/crates/libs/metadata/src/reader/codes.rs similarity index 100% rename from crates/libs/metadata/src/codes.rs rename to crates/libs/metadata/src/reader/codes.rs diff --git a/crates/libs/metadata/src/constant_value.rs b/crates/libs/metadata/src/reader/constant_value.rs similarity index 100% rename from crates/libs/metadata/src/constant_value.rs rename to crates/libs/metadata/src/reader/constant_value.rs diff --git a/crates/libs/metadata/src/file.rs b/crates/libs/metadata/src/reader/file.rs similarity index 99% rename from crates/libs/metadata/src/file.rs rename to crates/libs/metadata/src/reader/file.rs index 1a35465e4f..fa262b92e7 100644 --- a/crates/libs/metadata/src/file.rs +++ b/crates/libs/metadata/src/reader/file.rs @@ -474,14 +474,14 @@ impl File { file } - pub(crate) fn new>(filename: P) -> Self { + pub fn new>(filename: P) -> Self { let filename = filename.as_ref(); let bytes = std::fs::read(filename).unwrap_or_else(|e| panic!("Could not read file {:?}: {:?}", filename, e)); Self::from_bytes(filename.file_name().expect("Invalid .winmd path").to_string_lossy().to_string(), bytes) } - pub(crate) fn type_def_table(&self) -> &TableData { + pub fn type_def_table(&self) -> &TableData { &self.tables[TableIndex::TypeDef as usize] } diff --git a/crates/libs/metadata/src/guid.rs b/crates/libs/metadata/src/reader/guid.rs similarity index 100% rename from crates/libs/metadata/src/guid.rs rename to crates/libs/metadata/src/reader/guid.rs diff --git a/crates/libs/metadata/src/interface_kind.rs b/crates/libs/metadata/src/reader/interface_kind.rs similarity index 100% rename from crates/libs/metadata/src/interface_kind.rs rename to crates/libs/metadata/src/reader/interface_kind.rs diff --git a/crates/libs/metadata/src/reader/mod.rs b/crates/libs/metadata/src/reader/mod.rs new file mode 100644 index 0000000000..3534ee8945 --- /dev/null +++ b/crates/libs/metadata/src/reader/mod.rs @@ -0,0 +1,43 @@ +mod array_info; +mod async_kind; +mod blob; +mod cfg; +mod codes; +mod constant_value; +mod file; +mod guid; +mod interface_kind; +mod param_flags; +mod row; +mod signature; +mod signature_kind; +mod tables; +mod traits; +mod r#type; +mod type_kind; +mod type_name; +mod type_reader; +mod type_tree; +mod workspace; + +pub use array_info::*; +pub use async_kind::*; +pub use blob::*; +pub use cfg::*; +pub use codes::*; +pub use constant_value::*; +pub use file::*; +pub use guid::*; +pub use interface_kind::*; +pub use param_flags::*; +pub use r#type::*; +pub use row::*; +pub use signature::*; +pub use signature_kind::*; +pub use tables::*; +pub use traits::*; +pub use type_kind::*; +pub use type_name::*; +pub use type_reader::*; +pub use type_tree::*; +pub use workspace::*; diff --git a/crates/libs/metadata/src/param_flags.rs b/crates/libs/metadata/src/reader/param_flags.rs similarity index 100% rename from crates/libs/metadata/src/param_flags.rs rename to crates/libs/metadata/src/reader/param_flags.rs diff --git a/crates/libs/metadata/src/row.rs b/crates/libs/metadata/src/reader/row.rs similarity index 100% rename from crates/libs/metadata/src/row.rs rename to crates/libs/metadata/src/reader/row.rs diff --git a/crates/libs/metadata/src/signature.rs b/crates/libs/metadata/src/reader/signature.rs similarity index 100% rename from crates/libs/metadata/src/signature.rs rename to crates/libs/metadata/src/reader/signature.rs diff --git a/crates/libs/metadata/src/signature_kind.rs b/crates/libs/metadata/src/reader/signature_kind.rs similarity index 100% rename from crates/libs/metadata/src/signature_kind.rs rename to crates/libs/metadata/src/reader/signature_kind.rs diff --git a/crates/libs/metadata/src/tables/assembly_ref.rs b/crates/libs/metadata/src/reader/tables/assembly_ref.rs similarity index 100% rename from crates/libs/metadata/src/tables/assembly_ref.rs rename to crates/libs/metadata/src/reader/tables/assembly_ref.rs diff --git a/crates/libs/metadata/src/tables/attribute.rs b/crates/libs/metadata/src/reader/tables/attribute.rs similarity index 100% rename from crates/libs/metadata/src/tables/attribute.rs rename to crates/libs/metadata/src/reader/tables/attribute.rs diff --git a/crates/libs/metadata/src/tables/class_layout.rs b/crates/libs/metadata/src/reader/tables/class_layout.rs similarity index 100% rename from crates/libs/metadata/src/tables/class_layout.rs rename to crates/libs/metadata/src/reader/tables/class_layout.rs diff --git a/crates/libs/metadata/src/tables/constant.rs b/crates/libs/metadata/src/reader/tables/constant.rs similarity index 100% rename from crates/libs/metadata/src/tables/constant.rs rename to crates/libs/metadata/src/reader/tables/constant.rs diff --git a/crates/libs/metadata/src/tables/field.rs b/crates/libs/metadata/src/reader/tables/field.rs similarity index 100% rename from crates/libs/metadata/src/tables/field.rs rename to crates/libs/metadata/src/reader/tables/field.rs diff --git a/crates/libs/metadata/src/tables/generic_param.rs b/crates/libs/metadata/src/reader/tables/generic_param.rs similarity index 100% rename from crates/libs/metadata/src/tables/generic_param.rs rename to crates/libs/metadata/src/reader/tables/generic_param.rs diff --git a/crates/libs/metadata/src/tables/impl_map.rs b/crates/libs/metadata/src/reader/tables/impl_map.rs similarity index 100% rename from crates/libs/metadata/src/tables/impl_map.rs rename to crates/libs/metadata/src/reader/tables/impl_map.rs diff --git a/crates/libs/metadata/src/tables/interface_impl.rs b/crates/libs/metadata/src/reader/tables/interface_impl.rs similarity index 100% rename from crates/libs/metadata/src/tables/interface_impl.rs rename to crates/libs/metadata/src/reader/tables/interface_impl.rs diff --git a/crates/libs/metadata/src/tables/member_ref.rs b/crates/libs/metadata/src/reader/tables/member_ref.rs similarity index 100% rename from crates/libs/metadata/src/tables/member_ref.rs rename to crates/libs/metadata/src/reader/tables/member_ref.rs diff --git a/crates/libs/metadata/src/tables/method_def.rs b/crates/libs/metadata/src/reader/tables/method_def.rs similarity index 100% rename from crates/libs/metadata/src/tables/method_def.rs rename to crates/libs/metadata/src/reader/tables/method_def.rs diff --git a/crates/libs/metadata/src/tables/mod.rs b/crates/libs/metadata/src/reader/tables/mod.rs similarity index 100% rename from crates/libs/metadata/src/tables/mod.rs rename to crates/libs/metadata/src/reader/tables/mod.rs diff --git a/crates/libs/metadata/src/tables/module_ref.rs b/crates/libs/metadata/src/reader/tables/module_ref.rs similarity index 100% rename from crates/libs/metadata/src/tables/module_ref.rs rename to crates/libs/metadata/src/reader/tables/module_ref.rs diff --git a/crates/libs/metadata/src/tables/nested_class.rs b/crates/libs/metadata/src/reader/tables/nested_class.rs similarity index 100% rename from crates/libs/metadata/src/tables/nested_class.rs rename to crates/libs/metadata/src/reader/tables/nested_class.rs diff --git a/crates/libs/metadata/src/tables/param.rs b/crates/libs/metadata/src/reader/tables/param.rs similarity index 100% rename from crates/libs/metadata/src/tables/param.rs rename to crates/libs/metadata/src/reader/tables/param.rs diff --git a/crates/libs/metadata/src/tables/type_def.rs b/crates/libs/metadata/src/reader/tables/type_def.rs similarity index 100% rename from crates/libs/metadata/src/tables/type_def.rs rename to crates/libs/metadata/src/reader/tables/type_def.rs diff --git a/crates/libs/metadata/src/tables/type_ref.rs b/crates/libs/metadata/src/reader/tables/type_ref.rs similarity index 100% rename from crates/libs/metadata/src/tables/type_ref.rs rename to crates/libs/metadata/src/reader/tables/type_ref.rs diff --git a/crates/libs/metadata/src/tables/type_spec.rs b/crates/libs/metadata/src/reader/tables/type_spec.rs similarity index 100% rename from crates/libs/metadata/src/tables/type_spec.rs rename to crates/libs/metadata/src/reader/tables/type_spec.rs diff --git a/crates/libs/metadata/src/traits.rs b/crates/libs/metadata/src/reader/traits.rs similarity index 100% rename from crates/libs/metadata/src/traits.rs rename to crates/libs/metadata/src/reader/traits.rs diff --git a/crates/libs/metadata/src/type.rs b/crates/libs/metadata/src/reader/type.rs similarity index 100% rename from crates/libs/metadata/src/type.rs rename to crates/libs/metadata/src/reader/type.rs diff --git a/crates/libs/metadata/src/type_kind.rs b/crates/libs/metadata/src/reader/type_kind.rs similarity index 100% rename from crates/libs/metadata/src/type_kind.rs rename to crates/libs/metadata/src/reader/type_kind.rs diff --git a/crates/libs/metadata/src/type_name.rs b/crates/libs/metadata/src/reader/type_name.rs similarity index 100% rename from crates/libs/metadata/src/type_name.rs rename to crates/libs/metadata/src/reader/type_name.rs diff --git a/crates/libs/metadata/src/type_reader.rs b/crates/libs/metadata/src/reader/type_reader.rs similarity index 100% rename from crates/libs/metadata/src/type_reader.rs rename to crates/libs/metadata/src/reader/type_reader.rs diff --git a/crates/libs/metadata/src/type_tree.rs b/crates/libs/metadata/src/reader/type_tree.rs similarity index 100% rename from crates/libs/metadata/src/type_tree.rs rename to crates/libs/metadata/src/reader/type_tree.rs diff --git a/crates/libs/metadata/src/workspace.rs b/crates/libs/metadata/src/reader/workspace.rs similarity index 92% rename from crates/libs/metadata/src/workspace.rs rename to crates/libs/metadata/src/reader/workspace.rs index 36b863568d..75a6d0abe2 100644 --- a/crates/libs/metadata/src/workspace.rs +++ b/crates/libs/metadata/src/reader/workspace.rs @@ -69,9 +69,9 @@ fn get_workspace_winmds() -> Vec { push_dir(&mut result, &dir); if !result.iter().any(|file| file.name.starts_with("Windows.")) { - result.push(File::from_bytes("Windows.winmd".to_string(), include_bytes!("../default/Windows.winmd").to_vec())); - result.push(File::from_bytes("Windows.Win32.winmd".to_string(), include_bytes!("../default/Windows.Win32.winmd").to_vec())); - result.push(File::from_bytes("Windows.Win32.Interop.winmd".to_string(), include_bytes!("../default/Windows.Win32.Interop.winmd").to_vec())); + result.push(File::from_bytes("Windows.winmd".to_string(), include_bytes!("../../default/Windows.winmd").to_vec())); + result.push(File::from_bytes("Windows.Win32.winmd".to_string(), include_bytes!("../../default/Windows.Win32.winmd").to_vec())); + result.push(File::from_bytes("Windows.Win32.Interop.winmd".to_string(), include_bytes!("../../default/Windows.Win32.Interop.winmd").to_vec())); } result diff --git a/crates/libs/metadata/src/writer/blobs.rs b/crates/libs/metadata/src/writer/blobs.rs new file mode 100644 index 0000000000..2754376924 --- /dev/null +++ b/crates/libs/metadata/src/writer/blobs.rs @@ -0,0 +1,37 @@ +use super::*; + +pub(crate) struct Blobs { + set: BTreeMap, usize>, + stream: Vec, +} + +impl Blobs { + pub fn new() -> Self { + Self { set: BTreeMap::new(), stream: vec![0] } + } + + pub fn insert(&mut self, value: &[u8]) -> u32 { + if value.is_empty() { + return 0; + } + + let pos = self.stream.len(); + let mut insert = false; + + let pos = *self.set.entry(value.to_vec()).or_insert_with(|| { + insert = true; + pos + }); + + if insert { + self.stream.extend_from_slice(value); + } + + pos as _ + } + + pub fn into_stream(mut self) -> Vec { + self.stream.resize(round(self.stream.len(), 4), 0); + self.stream + } +} diff --git a/crates/libs/metadata/src/writer/gen.rs b/crates/libs/metadata/src/writer/gen.rs new file mode 100644 index 0000000000..6ea54b3099 --- /dev/null +++ b/crates/libs/metadata/src/writer/gen.rs @@ -0,0 +1,24 @@ +#[derive(Default)] +pub struct Gen { + // Source files to include. + pub sources: Vec, + + // Winmd files to include. + pub inputs: Vec, + + // Winmd files to reference. + pub references: Vec, + + // Name of resulting winmd file. + pub output: String, +} + +impl Gen { + pub fn new() -> Self { + Self::default() + } +} + +pub fn gen(_gen: &Gen) -> std::io::Result<()> { + Ok(()) +} diff --git a/crates/libs/metadata/src/writer/helpers.rs b/crates/libs/metadata/src/writer/helpers.rs new file mode 100644 index 0000000000..080c108d25 --- /dev/null +++ b/crates/libs/metadata/src/writer/helpers.rs @@ -0,0 +1,19 @@ +use std::mem::*; +use std::slice::*; + +pub fn round(size: usize, round: usize) -> usize { + let round = round - 1; + (size + round) & !round +} + +pub trait Write { + fn write(&mut self, value: &T); +} + +impl Write for Vec { + fn write(&mut self, value: &T) { + unsafe { + self.extend_from_slice(from_raw_parts(value as *const _ as _, size_of::())); + } + } +} diff --git a/crates/libs/metadata/src/writer/mod.rs b/crates/libs/metadata/src/writer/mod.rs new file mode 100644 index 0000000000..49ca0b7ab5 --- /dev/null +++ b/crates/libs/metadata/src/writer/mod.rs @@ -0,0 +1,29 @@ +mod blobs; +mod gen; +mod helpers; +mod pe; +mod strings; +mod tables; +use blobs::*; + +pub use gen::*; +use helpers::*; +use std::collections::*; +use strings::*; +use tables::*; + +pub fn test() { + let mut tables = Tables::new(); + tables.module.push(Module::new("test.winmd")); + tables.type_def.push(TypeDef::module()); + + let mut stringable = TypeDef::winrt_interface("IStringable", "Windows.Foundation"); + stringable.method_list.push(MethodDef::new("ToString")); + tables.type_def.push(stringable); + + let mut closable = TypeDef::winrt_interface("IClosable", "Windows.Foundation"); + closable.method_list.push(MethodDef::new("Close")); + tables.type_def.push(closable); + + pe::write("/git/test.winmd", tables); +} diff --git a/crates/libs/metadata/src/writer/pe.rs b/crates/libs/metadata/src/writer/pe.rs new file mode 100644 index 0000000000..a8bbb51d8b --- /dev/null +++ b/crates/libs/metadata/src/writer/pe.rs @@ -0,0 +1,283 @@ +use super::*; +use std::mem::*; + +pub(crate) fn write(filename: &str, tables: Tables) { + let dos = DosHeader::new(); + let pe = PeHeader::new(); + let mut optional = OptionalHeader::new(); + let mut section = SectionHeader::new(); + let mut clr = ClrHeader::new(); + let metadata = MetadataHeader::new(4); + + let mut strings = Strings::new(); + let mut blobs = Blobs::new(); + + let mut tables = tables.into_stream(&mut strings, &mut blobs); + let mut guids = guid_stream(); + let mut strings = strings.into_stream(); + let mut blobs = blobs.into_stream(); + + type TablesHeader = StreamHeader<4>; + type StringsHeader = StreamHeader<12>; + type GuidsHeader = StreamHeader<8>; + type BlobsHeader = StreamHeader<8>; + + let size_of_stream_headers = size_of::() + size_of::() + size_of::() + size_of::(); + let size_of_image = optional.file_alignment as usize + size_of::() + size_of::() + size_of_stream_headers + guids.len() + strings.len() + blobs.len() + tables.len(); + + optional.size_of_image = round(size_of_image, optional.section_alignment as _) as _; + section.virtual_size = size_of_image as u32 - optional.file_alignment; + section.size_of_raw_data = round(section.virtual_size as _, optional.file_alignment as _) as _; + + optional.data_directory[14] = DataDirectory { virtual_address: SECTION_ALIGNMENT, size: size_of::() as _ }; + section.pointer_to_raw_data = optional.file_alignment; + clr.meta_data = DataDirectory { virtual_address: SECTION_ALIGNMENT + size_of::() as u32, size: section.virtual_size - size_of::() as u32 }; + + let mut buffer = Vec::::new(); + buffer.write(&dos); + buffer.write(&pe); + buffer.write(&optional); + buffer.write(§ion); + debug_assert!(buffer.len() < optional.file_alignment as _); + buffer.resize(optional.file_alignment as _, 0); + buffer.write(&clr); + let metadata_offset = buffer.len(); + buffer.write(&metadata); + + let stream_offset = buffer.len() - metadata_offset + size_of_stream_headers; + let tables_header = TablesHeader::new(stream_offset as _, tables.len() as _, b"#~\0\0"); + let strings_header = StringsHeader::new(tables_header.next_offset(), strings.len() as _, b"#Strings\0\0\0\0"); + let guids_header = GuidsHeader::new(strings_header.next_offset(), guids.len() as _, b"#GUID\0\0\0"); + let blobs_header = BlobsHeader::new(guids_header.next_offset(), blobs.len() as _, b"#Blob\0\0\0"); + + buffer.write(&tables_header); + buffer.write(&strings_header); + buffer.write(&guids_header); + buffer.write(&blobs_header); + + buffer.append(&mut tables); + buffer.append(&mut strings); + buffer.append(&mut guids); + buffer.append(&mut blobs); + + assert_eq!(clr.meta_data.size as usize, buffer.len() - metadata_offset); + assert_eq!(size_of_image, buffer.len()); + + std::fs::write(filename, buffer).unwrap(); +} + +const SECTION_ALIGNMENT: u32 = 4096; + +#[repr(C)] +#[derive(Default)] +struct DosHeader { + magic: u16, + cblp: u16, + cp: u16, + crlc: u16, + cparhdr: u16, + minalloc: u16, + maxalloc: u16, + ss: u16, + sp: u16, + csum: u16, + ip: u16, + cs: u16, + lfarlc: u16, + ovno: u16, + res: [u16; 4], + oemid: u16, + oeminfo: u16, + res2: [u16; 10], + lfanew: i32, +} + +impl DosHeader { + fn new() -> Self { + Self { + magic: 0x5A4D, // MZ + lfarlc: 64, // file address of relocation table + lfanew: size_of::() as _, // file address of next header + ..Default::default() + } + } +} + +#[repr(C)] +#[derive(Default)] +struct PeHeader { + signature: u32, + machine: u16, + number_of_sections: u16, + time_date_stamp: u32, + pointer_to_symbol_table: u32, + number_of_symbols: u32, + size_of_optional_header: u16, + characteristics: u16, +} + +impl PeHeader { + fn new() -> Self { + Self { + signature: 0x4550, // PE\0\0 + machine: 0x14C, // x86 + number_of_sections: 1, + size_of_optional_header: size_of::() as _, + characteristics: 0x2102, // DLL + ..Default::default() + } + } +} + +#[repr(C)] +#[derive(Default)] +struct OptionalHeader { + magic: u16, + major_linker_version: u8, + minor_linker_version: u8, + size_of_code: u32, + size_of_initialized_data: u32, + size_of_uninitialized_data: u32, + address_of_entry_point: u32, + base_of_code: u32, + base_of_data: u32, + image_base: u32, + section_alignment: u32, + file_alignment: u32, + major_operating_system_version: u16, + minor_operating_system_version: u16, + major_image_version: u16, + minor_image_version: u16, + major_subsystem_version: u16, + minor_subsystem_version: u16, + win32_version_value: u32, + size_of_image: u32, + size_of_headers: u32, + check_sum: u32, + subsystem: u16, + dll_characteristics: u16, + size_of_stack_reserve: u32, + size_of_stack_commit: u32, + size_of_heap_reserve: u32, + size_of_heap_commit: u32, + loader_flags: u32, + number_of_rva_and_sizes: u32, + data_directory: [DataDirectory; 16], +} + +impl OptionalHeader { + fn new() -> Self { + Self { + magic: 0x10B, // PE32 + major_linker_version: 11, + size_of_initialized_data: 1024, + image_base: 0x400000, + section_alignment: SECTION_ALIGNMENT, + file_alignment: 512, + major_operating_system_version: 6, + minor_operating_system_version: 2, + major_subsystem_version: 6, + minor_subsystem_version: 2, + size_of_headers: 512, + subsystem: 3, // console + dll_characteristics: 0x540, + size_of_stack_reserve: 0x100000, + size_of_heap_reserve: 4096, + loader_flags: 0x100000, + number_of_rva_and_sizes: 16, + ..Default::default() + } + } +} + +#[repr(C)] +#[derive(Default)] +struct DataDirectory { + virtual_address: u32, + size: u32, +} + +#[repr(C)] +#[derive(Default)] +struct SectionHeader { + name: [u8; 8], + virtual_size: u32, + virtual_address: u32, + size_of_raw_data: u32, + pointer_to_raw_data: u32, + pointer_to_relocations: u32, + pointer_to_line_numbers: u32, + number_of_relocations: u16, + number_of_line_numbers: u16, + characteristics: u32, +} + +impl SectionHeader { + fn new() -> Self { + Self { name: *b".text\0\0\0", characteristics: 0x4000_0020, virtual_address: SECTION_ALIGNMENT, ..Default::default() } + } +} + +#[repr(C)] +#[derive(Default)] +struct ClrHeader { + cb: u32, + major_runtime_version: u16, + minor_runtime_version: u16, + meta_data: DataDirectory, + flags: u32, + entry_point_token_or_entry_point_rva: u32, + resources: DataDirectory, + strong_name_signature: DataDirectory, + code_manager_table: DataDirectory, + vtable_fixups: DataDirectory, + export_address_table_jumps: DataDirectory, + managed_native_header: DataDirectory, +} + +impl ClrHeader { + fn new() -> Self { + Self { cb: size_of::() as _, major_runtime_version: 2, minor_runtime_version: 5, flags: 0x1, ..Default::default() } + } +} + +#[repr(C)] +#[derive(Default)] +struct MetadataHeader { + signature: u32, + major_version: u16, + minor_version: u16, + reserved: u32, + length: u32, + version: [u8; 20], + flags: u16, + streams: u16, +} + +impl MetadataHeader { + fn new(streams: u16) -> Self { + Self { signature: 0x424A_5342, major_version: 1, minor_version: 1, length: 20, version: *b"WindowsRuntime\0\0\0\0\0\0", streams, ..Default::default() } + } +} + +#[repr(C)] +struct StreamHeader { + offset: u32, + size: u32, + name: [u8; LEN], +} + +impl StreamHeader { + fn new(offset: u32, size: u32, name: &[u8; LEN]) -> Self { + Self { offset, size, name: *name } + } + fn next_offset(&self) -> u32 { + self.offset + self.size + } +} + +fn guid_stream() -> Vec { + let mut buffer = Vec::new(); + buffer.resize(16, 0); // zero guid + buffer +} diff --git a/crates/libs/metadata/src/writer/strings.rs b/crates/libs/metadata/src/writer/strings.rs new file mode 100644 index 0000000000..969478d53e --- /dev/null +++ b/crates/libs/metadata/src/writer/strings.rs @@ -0,0 +1,38 @@ +use super::*; + +pub struct Strings { + set: BTreeMap, + stream: Vec, +} + +impl Strings { + pub fn new() -> Self { + Self { set: BTreeMap::new(), stream: vec![0] } + } + + pub fn insert(&mut self, value: &str) -> u32 { + if value.is_empty() { + return 0; + } + + let pos = self.stream.len(); + let mut insert = false; + + let pos = *self.set.entry(value.to_string()).or_insert_with(|| { + insert = true; + pos + }); + + if insert { + self.stream.extend_from_slice(value.as_bytes()); + self.stream.push(0); // terminator + } + + pos as _ + } + + pub fn into_stream(mut self) -> Vec { + self.stream.resize(round(self.stream.len(), 4), 0); + self.stream + } +} diff --git a/crates/libs/metadata/src/writer/tables/class_layout.rs b/crates/libs/metadata/src/writer/tables/class_layout.rs new file mode 100644 index 0000000000..9d59038c0a --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/class_layout.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct ClassLayout {} diff --git a/crates/libs/metadata/src/writer/tables/constant.rs b/crates/libs/metadata/src/writer/tables/constant.rs new file mode 100644 index 0000000000..efd25e11f1 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/constant.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct Constant {} diff --git a/crates/libs/metadata/src/writer/tables/custom_attribute.rs b/crates/libs/metadata/src/writer/tables/custom_attribute.rs new file mode 100644 index 0000000000..3d691987e8 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/custom_attribute.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct CustomAttribute {} diff --git a/crates/libs/metadata/src/writer/tables/field.rs b/crates/libs/metadata/src/writer/tables/field.rs new file mode 100644 index 0000000000..5a17501890 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/field.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct Field {} diff --git a/crates/libs/metadata/src/writer/tables/generic_param.rs b/crates/libs/metadata/src/writer/tables/generic_param.rs new file mode 100644 index 0000000000..4b0ac645b0 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/generic_param.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct GenericParam {} diff --git a/crates/libs/metadata/src/writer/tables/impl_map.rs b/crates/libs/metadata/src/writer/tables/impl_map.rs new file mode 100644 index 0000000000..7806292e72 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/impl_map.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct ImplMap {} diff --git a/crates/libs/metadata/src/writer/tables/interface_impl.rs b/crates/libs/metadata/src/writer/tables/interface_impl.rs new file mode 100644 index 0000000000..85b9e73f98 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/interface_impl.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct InterfaceImpl {} diff --git a/crates/libs/metadata/src/writer/tables/member_ref.rs b/crates/libs/metadata/src/writer/tables/member_ref.rs new file mode 100644 index 0000000000..8ed0f17674 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/member_ref.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct MemberRef {} diff --git a/crates/libs/metadata/src/writer/tables/method_def.rs b/crates/libs/metadata/src/writer/tables/method_def.rs new file mode 100644 index 0000000000..6be3d105b1 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/method_def.rs @@ -0,0 +1,15 @@ +use super::*; + +#[derive(Default)] +pub struct MethodDef { + pub name: String, + pub signature: Vec, + pub param_list: Vec, + pub(crate) param_index: usize, +} + +impl MethodDef { + pub fn new(name: &str) -> Self { + Self { name: name.to_string(), ..Default::default() } + } +} diff --git a/crates/libs/metadata/src/writer/tables/mod.rs b/crates/libs/metadata/src/writer/tables/mod.rs new file mode 100644 index 0000000000..2ea8330eca --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/mod.rs @@ -0,0 +1,212 @@ +pub use super::*; + +mod class_layout; +mod constant; +mod custom_attribute; +mod field; +mod generic_param; +mod impl_map; +mod interface_impl; +mod member_ref; +mod method_def; +mod module; +mod module_ref; +mod nested_class; +mod param; +mod type_def; +mod type_ref; +mod type_spec; + +pub use class_layout::*; +pub use constant::*; +pub use custom_attribute::*; +pub use field::*; +pub use generic_param::*; +pub use impl_map::*; +pub use interface_impl::*; +pub use member_ref::*; +pub use method_def::*; +pub use module::*; +pub use module_ref::*; +pub use nested_class::*; +pub use param::*; +pub use type_def::*; +pub use type_ref::*; +pub use type_spec::*; + +#[derive(Default)] +pub struct Tables { + pub module: Vec, + pub type_ref: Vec, + pub type_def: Vec, + pub field: Vec, + pub method_def: Vec, + pub param: Vec, + pub interface_impl: Vec, + pub member_ref: Vec, + pub constant: Vec, + pub custom_attribute: Vec, + pub class_layout: Vec, + pub module_ref: Vec, + pub type_spec: Vec, + pub impl_map: Vec, + pub nested_class: Vec, + pub generic_param: Vec, +} + +impl Tables { + pub fn new() -> Self { + Self::default() + } + + pub(crate) fn into_stream(mut self, strings: &mut Strings, blobs: &mut Blobs) -> Vec { + self.normalize(); + + let mut buffer = Vec::new(); + let header = Header::new(); + buffer.write(&header); + + buffer.write(&(self.module.len() as u32)); + buffer.write(&(self.type_ref.len() as u32)); + buffer.write(&(self.type_def.len() as u32)); + buffer.write(&(self.field.len() as u32)); + buffer.write(&(self.method_def.len() as u32)); + buffer.write(&(self.param.len() as u32)); + buffer.write(&(self.interface_impl.len() as u32)); + buffer.write(&(self.member_ref.len() as u32)); + buffer.write(&(self.constant.len() as u32)); + buffer.write(&(self.custom_attribute.len() as u32)); + buffer.write(&(self.class_layout.len() as u32)); + buffer.write(&(self.module_ref.len() as u32)); + buffer.write(&(self.type_spec.len() as u32)); + buffer.write(&(self.impl_map.len() as u32)); + buffer.write(&(self.nested_class.len() as u32)); + buffer.write(&(self.generic_param.len() as u32)); + + for module in &self.module { + buffer.write(&0u16); // Generation (reserved) + buffer.write(&strings.insert(&module.name)); + buffer.write(&1u32); // Mvid (zero guid) + buffer.write(&0u32); // EncId (reserved) + buffer.write(&0u32); // EncBaseId (reserved) + } + + for type_def in &self.type_def { + buffer.write(&type_def.flags); + buffer.write(&strings.insert(&type_def.name)); + buffer.write(&strings.insert(&type_def.namespace)); + buffer.write(&0u16); // Extends + write_index(&mut buffer, type_def.field_index, self.field.len()); + write_index(&mut buffer, type_def.method_index, self.method_def.len()); + } + + for method_def in &self.method_def { + buffer.write(&0u32); // RVA + buffer.write(&0u16); // ImplFlags + buffer.write(&0u16); // Flags + buffer.write(&strings.insert(&method_def.name)); + buffer.write(&blobs.insert(&method_def.signature)); + buffer.write(&1u16); // ParamList + } + + for param in &self.param { + buffer.write(¶m.flags); + buffer.write(¶m.sequence); + buffer.write(&strings.insert(¶m.name)); + } + + buffer.resize(round(buffer.len(), 4), 0); + buffer + } + + // Once all of the type information has been added, normalization is the process of packing + // the various relational records into their respective tables and leaving only index behind. + fn normalize(&mut self) { + for type_def in &mut self.type_def { + type_def.field_index = self.field.len(); + type_def.method_index = self.method_def.len(); + self.field.append(&mut type_def.field_list); + self.method_def.append(&mut type_def.method_list); + } + + for method_def in &mut self.method_def { + method_def.param_index = self.param.len(); + self.param.append(&mut method_def.param_list); + } + } +} + +#[repr(C)] +#[derive(Default)] +struct Header { + reserved1: u32, + major_version: u8, + minor_version: u8, + heap_sizes: u8, + reserved2: u8, + valid: u64, + sorted: u64, +} + +impl Header { + fn new() -> Self { + Self { + major_version: 2, + reserved2: 1, + heap_sizes: 0b111, // 4 byte indexes + valid: 1 << 0 | // Module + 1 << 0x01 | // TypeRef + 1 << 0x02 | // TypeDef + 1 << 0x04 | // Field + 1 << 0x06 | // MethodDef + 1 << 0x08 | // Param + 1 << 0x09 | // InterfaceImpl + 1 << 0x0A | // MemberRef + 1 << 0x0B | // Constant + 1 << 0x0C | // CustomAttribute + 1 << 0x0F | // ClassLayout + 1 << 0x1A | // ModuleRef + 1 << 0x1B | // TypeSpec, + 1 << 0x1C | // ImplMap + 1 << 0x29 | // NestedClass + 1 << 0x2A, // GenericParam + // TODO: mark sorted tables? + ..Default::default() + } + } +} + +fn write_index(buffer: &mut Vec, index: usize, len: usize) { + if len < (1 << 16) { + buffer.write(&(index as u16 + 1)) + } else { + buffer.write(&(index as u32 + 1)) + } +} + +// fn coded_index_size(tables: &[usize]) -> u32 { +// fn small(row_count: usize, bits: u8) -> bool { +// (row_count as u64) < (1u64 << (16 - bits)) +// } + +// fn bits_needed(value: usize) -> u8 { +// let mut value = value - 1; +// let mut bits: u8 = 1; +// loop { +// value >>= 1; +// if value == 0 { +// break; +// } +// bits += 1; +// } +// bits +// } + +// let bits_needed = bits_needed(tables.len()); + +// if tables.iter().all(|len| small(*len, bits_needed)) { +// 2 +// } else { +// 4 +// } +// } diff --git a/crates/libs/metadata/src/writer/tables/module.rs b/crates/libs/metadata/src/writer/tables/module.rs new file mode 100644 index 0000000000..1b13527ed0 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/module.rs @@ -0,0 +1,10 @@ +#[derive(Default)] +pub struct Module { + pub name: String, +} + +impl Module { + pub fn new(name: &str) -> Self { + Self { name: name.to_string() } + } +} diff --git a/crates/libs/metadata/src/writer/tables/module_ref.rs b/crates/libs/metadata/src/writer/tables/module_ref.rs new file mode 100644 index 0000000000..f7558774c1 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/module_ref.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct ModuleRef {} diff --git a/crates/libs/metadata/src/writer/tables/nested_class.rs b/crates/libs/metadata/src/writer/tables/nested_class.rs new file mode 100644 index 0000000000..91a8ebbd1f --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/nested_class.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct NestedClass {} diff --git a/crates/libs/metadata/src/writer/tables/param.rs b/crates/libs/metadata/src/writer/tables/param.rs new file mode 100644 index 0000000000..d9611d3155 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/param.rs @@ -0,0 +1,6 @@ +#[derive(Default)] +pub struct Param { + pub flags: u16, + pub sequence: u16, + pub name: String, +} diff --git a/crates/libs/metadata/src/writer/tables/type_def.rs b/crates/libs/metadata/src/writer/tables/type_def.rs new file mode 100644 index 0000000000..4624e97746 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/type_def.rs @@ -0,0 +1,22 @@ +use super::*; + +#[derive(Default)] +pub struct TypeDef { + pub flags: u32, + pub name: String, + pub namespace: String, + pub field_list: Vec, + pub method_list: Vec, + pub(crate) field_index: usize, + pub(crate) method_index: usize, +} + +impl TypeDef { + pub fn module() -> Self { + Self { name: "".to_string(), ..Default::default() } + } + + pub fn winrt_interface(name: &str, namespace: &str) -> Self { + Self { name: name.to_string(), namespace: namespace.to_string(), ..Default::default() } + } +} diff --git a/crates/libs/metadata/src/writer/tables/type_ref.rs b/crates/libs/metadata/src/writer/tables/type_ref.rs new file mode 100644 index 0000000000..aa019e521b --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/type_ref.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct TypeRef {} diff --git a/crates/libs/metadata/src/writer/tables/type_spec.rs b/crates/libs/metadata/src/writer/tables/type_spec.rs new file mode 100644 index 0000000000..f5b2e35551 --- /dev/null +++ b/crates/libs/metadata/src/writer/tables/type_spec.rs @@ -0,0 +1,2 @@ +#[derive(Default)] +pub struct TypeSpec {} diff --git a/crates/tests/writer/Cargo.toml b/crates/tests/writer/Cargo.toml new file mode 100644 index 0000000000..06eff24f3b --- /dev/null +++ b/crates/tests/writer/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "test_writer" +version = "0.0.0" +authors = ["Microsoft"] +edition = "2018" + +[dependencies.windows-metadata] +path = "../../libs/metadata" diff --git a/crates/tests/writer/src/main.rs b/crates/tests/writer/src/main.rs new file mode 100644 index 0000000000..67550a40e5 --- /dev/null +++ b/crates/tests/writer/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + windows_metadata::writer::test(); +} diff --git a/crates/tests/writer/tests/test.rs b/crates/tests/writer/tests/test.rs new file mode 100644 index 0000000000..ca4a06a17a --- /dev/null +++ b/crates/tests/writer/tests/test.rs @@ -0,0 +1,4 @@ +#[test] +fn metagen() { + // windows_metadata::writer::test(); +} diff --git a/crates/tools/gnu/src/main.rs b/crates/tools/gnu/src/main.rs index b5340e7ff4..fdc5670f25 100644 --- a/crates/tools/gnu/src/main.rs +++ b/crates/tools/gnu/src/main.rs @@ -1,4 +1,4 @@ -use metadata::*; +use metadata::reader::*; use std::collections::BTreeMap; use std::io::prelude::*; diff --git a/crates/tools/ilrs/Cargo.toml b/crates/tools/ilrs/Cargo.toml new file mode 100644 index 0000000000..38f1ce6cfa --- /dev/null +++ b/crates/tools/ilrs/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "tool_ilrs" +version = "0.0.0" +edition = "2018" +publish = false + +[dependencies] +metadata = { package = "windows-metadata", path = "../../libs/metadata", version = "0.34.0" } diff --git a/crates/tools/ilrs/src/main.rs b/crates/tools/ilrs/src/main.rs new file mode 100644 index 0000000000..acbdbd55f3 --- /dev/null +++ b/crates/tools/ilrs/src/main.rs @@ -0,0 +1,47 @@ +use std::env::*; + +fn main() { + let mut gen = metadata::writer::Gen::new(); + let mut kind = ArgKind::None; + + for arg in args() { + match kind { + ArgKind::None => match arg.as_str() { + "-s" => kind = ArgKind::Source, + "-i" => kind = ArgKind::Input, + "-r" => kind = ArgKind::Reference, + "-o" => kind = ArgKind::Output, + _ => return print_help(), + }, + ArgKind::Source => gen.sources.push(arg), + ArgKind::Input => gen.inputs.push(arg), + ArgKind::Reference => gen.references.push(arg), + ArgKind::Output => { + if gen.output.is_empty() { + gen.output = arg; + } else { + return print_help(); + } + } + } + } + + if (gen.sources.is_empty() || gen.inputs.is_empty()) || gen.output.is_empty() { + return print_help(); + } + + let _ = metadata::writer::gen(&gen); + // TODO: print error report +} + +fn print_help() { + println!("help"); +} + +enum ArgKind { + None, + Source, + Input, + Reference, + Output, +} diff --git a/crates/tools/msvc/src/main.rs b/crates/tools/msvc/src/main.rs index ee22b8d9ba..9bfd1dd097 100644 --- a/crates/tools/msvc/src/main.rs +++ b/crates/tools/msvc/src/main.rs @@ -1,4 +1,4 @@ -use metadata::*; +use metadata::reader::*; use std::collections::BTreeMap; use std::io::prelude::*; diff --git a/crates/tools/sys/src/main.rs b/crates/tools/sys/src/main.rs index eaf2b1a7e6..921ab7c77d 100644 --- a/crates/tools/sys/src/main.rs +++ b/crates/tools/sys/src/main.rs @@ -1,3 +1,4 @@ +use metadata::reader::*; use rayon::prelude::*; use std::io::prelude::*; @@ -8,7 +9,7 @@ fn main() { let _ = std::fs::remove_dir_all(&output); output.pop(); - let reader = metadata::TypeReader::get(); + let reader = TypeReader::get(); let root = reader.types.get_namespace("Windows").unwrap(); let mut trees = Vec::new(); @@ -92,7 +93,7 @@ deprecated = [] std::fs::copy(".github/license-apache", "crates/libs/sys/license-apache").unwrap(); } -fn collect_trees<'a>(output: &std::path::Path, tree: &'a metadata::TypeTree, trees: &mut Vec<&'a metadata::TypeTree>) { +fn collect_trees<'a>(output: &std::path::Path, tree: &'a TypeTree, trees: &mut Vec<&'a TypeTree>) { if EXCLUDE_NAMESPACES.iter().any(|&x| x == tree.namespace) { return; } @@ -104,7 +105,7 @@ fn collect_trees<'a>(output: &std::path::Path, tree: &'a metadata::TypeTree, tre std::fs::create_dir_all(&path).unwrap(); } -fn gen_tree(output: &std::path::Path, _root: &'static str, tree: &metadata::TypeTree) { +fn gen_tree(output: &std::path::Path, _root: &'static str, tree: &TypeTree) { let mut path = std::path::PathBuf::from(output); path.push(tree.namespace.replace('.', "/")); diff --git a/crates/tools/windows/src/main.rs b/crates/tools/windows/src/main.rs index 3103a6e4b8..d7726a2738 100644 --- a/crates/tools/windows/src/main.rs +++ b/crates/tools/windows/src/main.rs @@ -1,3 +1,4 @@ +use metadata::reader::*; use rayon::prelude::*; use std::io::prelude::*; @@ -8,7 +9,7 @@ fn main() { let _ = std::fs::remove_dir_all(&output); output.pop(); - let reader = metadata::TypeReader::get(); + let reader = TypeReader::get(); let root = reader.types.get_namespace("Windows").unwrap(); let mut trees = Vec::new(); @@ -99,7 +100,7 @@ interface = ["windows-interface"] std::fs::copy(".github/license-apache", "crates/libs/windows/license-apache").unwrap(); } -fn collect_trees<'a>(output: &std::path::Path, tree: &'a metadata::TypeTree, trees: &mut Vec<&'a metadata::TypeTree>) { +fn collect_trees<'a>(output: &std::path::Path, tree: &'a TypeTree, trees: &mut Vec<&'a TypeTree>) { if EXCLUDE_NAMESPACES.iter().any(|&x| x == tree.namespace) { return; } @@ -111,7 +112,7 @@ fn collect_trees<'a>(output: &std::path::Path, tree: &'a metadata::TypeTree, tre std::fs::create_dir_all(&path).unwrap(); } -fn gen_tree(output: &std::path::Path, _root: &'static str, tree: &metadata::TypeTree) { +fn gen_tree(output: &std::path::Path, _root: &'static str, tree: &TypeTree) { println!("{}", tree.namespace); let path = std::path::PathBuf::from(output).join(tree.namespace.replace('.', "/")); diff --git a/crates/tools/yml/src/main.rs b/crates/tools/yml/src/main.rs index eba08f9c42..213b887721 100644 --- a/crates/tools/yml/src/main.rs +++ b/crates/tools/yml/src/main.rs @@ -4,7 +4,7 @@ fn main() { } fn test_yml() { - let root = std::path::PathBuf::from(metadata::workspace_dir()); + let root = std::path::PathBuf::from(metadata::reader::workspace_dir()); let mut yml = r#"name: Test on: @@ -124,7 +124,7 @@ jobs: } fn build_yml() { - let root = std::path::PathBuf::from(metadata::workspace_dir()); + let root = std::path::PathBuf::from(metadata::reader::workspace_dir()); let mut yml = r#"name: Build on: