From f6603b6b4a60f571116451d65c28c7fc873d4ad4 Mon Sep 17 00:00:00 2001 From: novafacing Date: Tue, 25 Jun 2024 12:46:08 -0700 Subject: [PATCH 01/14] Initial implementation, triaging some crashes --- Cargo.toml | 16 + src/haps/mod.rs | 43 +- src/lib.rs | 92 +- src/os/mod.rs | 3 + src/os/windows/debug_info.rs | 510 ++++++ src/os/windows/idt.rs | 38 + src/os/windows/kernel.rs | 488 ++++++ src/os/windows/mod.rs | 221 +++ src/os/windows/paging.rs | 2520 ++++++++++++++++++++++++++++ src/os/windows/pdb.rs | 130 ++ src/os/windows/structs.rs | 3076 ++++++++++++++++++++++++++++++++++ src/os/windows/util.rs | 228 +++ src/tracer/mod.rs | 45 +- 13 files changed, 7352 insertions(+), 58 deletions(-) create mode 100644 src/os/mod.rs create mode 100644 src/os/windows/debug_info.rs create mode 100644 src/os/windows/idt.rs create mode 100644 src/os/windows/kernel.rs create mode 100644 src/os/windows/mod.rs create mode 100644 src/os/windows/paging.rs create mode 100644 src/os/windows/pdb.rs create mode 100644 src/os/windows/structs.rs create mode 100644 src/os/windows/util.rs diff --git a/Cargo.toml b/Cargo.toml index a7c76d15..96e24f53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,22 @@ tracing-subscriber = "0.3.18" tracing = { version = "0.1.40", features = ["log"] } yaxpeax-arm = "0.3.0" chrono = "0.4.38" +vergilius = "0.1.2" +windows = { version = "0.57.0", features = [ + "Win32_Foundation", + "Win32_System", + "Win32_System_SystemServices", + "Win32_System_Diagnostics_Debug", + "Win32_System_Diagnostics", + "Win32_System_SystemInformation", + "Win32_System_Kernel", +] } +reqwest = { version = "0.12.5", features = ["blocking"] } +pdb = "0.8.0" +intervaltree = "0.2.7" +lending-iterator = "0.1.7" +rustc-demangle = "0.1.24" +symbolic = { version = "12.9.2", features = ["demangle"] } [dev-dependencies] simics-test = "0.1.0" diff --git a/src/haps/mod.rs b/src/haps/mod.rs index a7cce6c4..25eef62f 100644 --- a/src/haps/mod.rs +++ b/src/haps/mod.rs @@ -29,6 +29,7 @@ impl Tsffs { let start_processor = self .start_processor() .ok_or_else(|| anyhow!("No start processor"))?; + let start_processor_raw = start_processor.cpu(); let start_info = match magic_number { MagicNumber::StartBufferPtrSizePtr => { @@ -54,6 +55,15 @@ impl Tsffs { .map_err(|_| anyhow!("Failed to set start time"))?; self.coverage_enabled = true; self.save_initial_snapshot()?; + // Collect windows coverage info if enabled + if self.windows && self.symbolic_coverage { + self.windows_os_info.collect( + start_processor_raw, + &self.debuginfo_download_directory, + self.guess_pdb_function_size, + &self.debug_info, + )?; + } self.get_and_write_testcase()?; self.post_timeout_event()?; } @@ -204,6 +214,16 @@ impl Tsffs { self.coverage_enabled = true; self.save_initial_snapshot()?; + // Collect windows coverage info if enabled + if self.windows && self.symbolic_coverage { + self.windows_os_info.collect( + processor, + &self.debuginfo_download_directory, + self.guess_pdb_function_size, + &self.debug_info, + )?; + } + self.get_and_write_testcase()?; self.post_timeout_event()?; @@ -236,6 +256,16 @@ impl Tsffs { self.coverage_enabled = true; self.save_initial_snapshot()?; + // Collect windows coverage info if enabled + if self.windows && self.symbolic_coverage { + self.windows_os_info.collect( + processor, + &self.debuginfo_download_directory, + self.guess_pdb_function_size, + &self.debug_info, + )?; + } + self.post_timeout_event()?; } @@ -571,7 +601,7 @@ impl Tsffs { ) -> Result<()> { trace!( self.as_conf_object(), - "on_magic_instruction({magic_number})" + "Got magic instruction with magic #{magic_number})" ); if object_is_processor(trigger_obj)? { @@ -629,4 +659,15 @@ impl Tsffs { Ok(()) } + + pub fn on_control_register_write( + &mut self, + trigger_obj: *mut ConfObject, + register_nr: i64, + value: i64, + ) -> Result<()> { + self.on_control_register_write_windows_symcov(trigger_obj, register_nr, value)?; + + Ok(()) + } } diff --git a/src/lib.rs b/src/lib.rs index afedc4e2..d5cb3849 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,7 @@ use libafl_targets::AFLppCmpLogMap; use log::LogMessage; use magic::MagicNumber; use num_traits::FromPrimitive as _; +use os::windows::WindowsOsInfo; use serde::{Deserialize, Serialize}; use serde_json::to_writer; use simics::{ @@ -49,9 +50,9 @@ use simics::{ get_processor_number, info, lookup_file, object_clock, run_command, run_python, simics_init, sys::save_flags_t, trace, version_base, warn, write_configuration_to_file, AsConfObject, BreakpointId, ClassCreate, ClassObjectsFinalize, ConfObject, CoreBreakpointMemopHap, - CoreExceptionHap, CoreMagicInstructionHap, CoreSimulationStoppedHap, - CpuInstrumentationSubscribeInterface, Event, EventClassFlag, FromConfObject, HapHandle, - Interface, IntoAttrValueDict, + CoreControlRegisterWriteHap, CoreExceptionHap, CoreMagicInstructionHap, + CoreSimulationStoppedHap, CpuInstrumentationSubscribeInterface, Event, EventClassFlag, + FromConfObject, HapHandle, Interface, }; #[cfg(simics_version_6)] use simics::{ @@ -89,6 +90,7 @@ pub(crate) mod haps; pub(crate) mod interfaces; pub(crate) mod log; pub(crate) mod magic; +pub(crate) mod os; pub(crate) mod state; pub(crate) mod tracer; pub(crate) mod traits; @@ -200,8 +202,8 @@ pub(crate) enum ManualStartSize { }, } -#[class(name = "tsffs", skip_objects_finalize, attr_value)] -#[derive(AsConfObject, FromConfObject, Default, IntoAttrValueDict)] +#[class(name = "tsffs", skip_objects_finalize)] +#[derive(AsConfObject, FromConfObject, Default)] /// The main module class for the TSFFS fuzzer, stores state and configuration information pub(crate) struct Tsffs { #[class(attribute(optional, default = false))] @@ -227,7 +229,6 @@ pub(crate) struct Tsffs { /// most processors will generate exceptions during start-up and during normal operation. pub all_exceptions_are_solutions: bool, #[class(attribute(optional))] - #[attr_value(fallible)] /// The set of exceptions which are treated as solutions. For example on x86_64, setting: /// /// @tsffs.exceptions = [14] @@ -235,7 +236,6 @@ pub(crate) struct Tsffs { /// would treat any page fault as a solution. pub exceptions: BTreeSet, #[class(attribute(optional))] - #[attr_value(fallible)] /// The set of breakpoints which are treated as solutions. For example, to set a solution /// breakpoint on the address $addr (note the breakpoint set from the Simics command is /// accessed through the simenv namespace): @@ -268,7 +268,6 @@ pub(crate) struct Tsffs { /// (and they cannot be nested). This only has an effect if `start_on_harness` is set. pub magic_start_index: u64, #[class(attribute(optional, default = vec![0]))] - #[attr_value(fallible)] /// The magic numbers which is passed to the platform-specific magic instruction HAP /// by a compiled-in harness to signal that the fuzzer should stop execution of the current /// iteration. @@ -279,7 +278,6 @@ pub(crate) struct Tsffs { /// This only has an effect if `stop_on_harness` is set. pub magic_stop_indices: Vec, #[class(attribute(optional, default = vec![0]))] - #[attr_value(fallible)] /// The numbers which are passed to the platform-specific magic instruction HAP by a /// compiled-in harness to signal that the fuzzer should stop execution of the /// current iteration and save the testcase as a solution. @@ -297,7 +295,6 @@ pub(crate) struct Tsffs { /// fuzzing loop. pub initial_random_corpus_size: usize, #[class(attribute(optional, default = lookup_file("%simics%")?.join("corpus")))] - #[attr_value(fallible)] /// The directory to load the corpus from and save new corpus items to. This directory /// may be a SIMICS relative path prefixed with "%simics%". It is an error to provide no /// corpus directory when `set_generate_random_corpus(True)` has not been called prior to @@ -306,7 +303,6 @@ pub(crate) struct Tsffs { /// be used by default. pub corpus_directory: PathBuf, #[class(attribute(optional, default = lookup_file("%simics%")?.join("solutions")))] - #[attr_value(fallible)] /// The directory to save solutions to. This directory may be a SIMICS relative path /// prefixed with "%simics%". If not provided, "%simics%/solutions" will be used by /// default. @@ -330,18 +326,15 @@ pub(crate) struct Tsffs { /// be logged. pub coverage_reporting: bool, #[class(attribute(optional))] - #[attr_value(fallible)] /// A set of executable files to tokenize. Tokens will be extracted from these files and /// used to drive token mutations of testcases. pub token_executables: Vec, #[class(attribute(optional))] - #[attr_value(fallible)] /// A set of source files to tokenize. Tokens will be extracted from these files and used /// to drive token mutations of testcases. C source files are expected, and strings and /// tokens will be extracted from strings in the source files. pub token_src_files: Vec, #[class(attribute(optional))] - #[attr_value(fallible)] /// Files in the format of: /// /// x = "hello" @@ -350,18 +343,15 @@ pub(crate) struct Tsffs { /// which will be used to drive token mutations of testcases. pub token_files: Vec, #[class(attribute(optional))] - #[attr_value(fallible)] /// Sets of tokens to use to drive token mutations of testcases. Each token set is a /// bytes which will be randomically inserted into testcases. pub tokens: Vec>, #[class(attribute(optional, default = lookup_file("%simics%")?.join("checkpoint.ckpt")))] - #[attr_value(fallible)] /// The path to the checkpoint saved prior to fuzzing when using snapshots pub checkpoint_path: PathBuf, #[class(attribute(optional, default = true))] pub pre_snapshot_checkpoint: bool, #[class(attribute(optional, default = lookup_file("%simics%")?.join("log.json")))] - #[attr_value(fallible)] /// The path to the log file which will be used to log the fuzzer's output statistics pub log_path: PathBuf, #[class(attribute(optional, default = true))] @@ -394,7 +384,6 @@ pub(crate) struct Tsffs { /// and should only be used for debugging and testing purposes. pub save_all_execution_traces: bool, #[class(attribute(optional, default = lookup_file("%simics%")?.join("execution-traces")))] - #[attr_value(fallible)] /// The directory to save execution traces to, if any are set to be saved. This /// directory may be a SIMICS relative path prefixed with "%simics%". If not /// provided, "%simics%/execution-traces" will be used by default. @@ -409,22 +398,37 @@ pub(crate) struct Tsffs { /// The interval in seconds between heartbeat messages pub heartbeat_interval: u64, - #[attr_value(skip)] + #[class(attribute(optional, default = false))] + /// Whether symbolic coverage should be used during fuzzing + pub symbolic_coverage: bool, + #[class(attribute(optional, default = false))] + /// Whether windows is being run in the simulation + pub windows: bool, + #[class(attribute(optional, default = lookup_file("%simics%")?.join("debuginfo-cache")))] + /// Directory in which to download PDB and EXE files from symbol servers on Windows + pub debuginfo_download_directory: PathBuf, + #[class(attribute(optional, default = true))] + /// Whether to guess the size of each function provided by a PDB file + pub guess_pdb_function_size: bool, + #[class(attribute(optional))] + /// Mapping of file name (name and extension e.g. fuzzer-app.exe or target.sys) + /// to a tuple of (exe path, debuginfo path) where debuginfo is either a PDB or DWARF + /// file + pub debug_info: HashMap>, + /// Handle for the core simulation stopped hap stop_hap_handle: HapHandle, - #[attr_value(skip)] /// Handle for the core breakpoint memop hap breakpoint_memop_hap_handle: HapHandle, - #[attr_value(skip)] /// Handle for exception HAP exception_hap_handle: HapHandle, - #[attr_value(skip)] /// The handle for the registered magic HAP, used to /// listen for magic start and stop if `start_on_harness` /// or `stop_on_harness` are set. magic_hap_handle: HapHandle, + /// Handle for the core control register write hap + control_register_write_hap_handle: HapHandle, - #[attr_value(skip)] /// A mapping of architecture hints from CPU index to architecture hint. This architecture /// hint overrides the detected architecture of the CPU core. This is useful when the /// architecture of the CPU core is not detected correctly, or when the architecture of the @@ -432,114 +436,85 @@ pub(crate) struct Tsffs { /// report their architecture as x86_64 can be overridden to x86. pub architecture_hints: HashMap, // Threads and message channels - #[attr_value(skip)] /// Fuzzer thread fuzz_thread: OnceCell>>, - #[attr_value(skip)] /// Message sender to the fuzzer thread. TSFFS sends exit kinds to the fuzzer thread to /// report whether testcases resulted in normal exit, timeout, or solutions. fuzzer_tx: OnceCell>, - #[attr_value(skip)] /// Message receiver from the fuzzer thread. TSFFS receives new testcases and run configuration /// from the fuzzer thread. fuzzer_rx: OnceCell>, - #[attr_value(skip)] /// A message sender to inform the fuzzer thread that it should exit. fuzzer_shutdown: OnceCell>, - #[attr_value(skip)] /// Reciever from the fuzzer thread to receive messages from the fuzzer thread /// including status messages and structured introspection data like new edge findings. fuzzer_messages: OnceCell>, // Fuzzer coverage maps - #[attr_value(skip)] /// The coverage map coverage_map: OnceCell>, - #[attr_value(skip)] /// A pointer to the AFL++ comparison map aflpp_cmp_map_ptr: OnceCell<*mut AFLppCmpLogMap>, - #[attr_value(skip)] /// The owned AFL++ comparison map aflpp_cmp_map: OnceCell<&'static mut AFLppCmpLogMap>, - #[attr_value(skip)] /// The previous location for coverage for calculating the hash of edges. coverage_prev_loc: u64, - #[attr_value(skip)] /// The registered timeout event which is registered and used to detect timeouts in /// virtual time timeout_event: OnceCell, - #[attr_value(skip)] /// The set of edges which have been seen at least once. edges_seen: HashSet, - #[attr_value(skip)] /// A map of the new edges to their AFL indices seen since the last time the fuzzer /// provided an update. This is not cleared every execution. edges_seen_since_last: HashMap, - #[attr_value(skip)] /// The set of PCs comprising the current execution trace. This is cleared every execution. execution_trace: ExecutionTrace, - #[attr_value(skip)] /// The name of the fuzz snapshot, if saved snapshot_name: OnceCell, - #[attr_value(skip)] /// The index of the micro checkpoint saved for the fuzzer. Only present if not using /// snapshots. micro_checkpoint_index: OnceCell, - #[attr_value(skip)] /// The reason the current stop occurred stop_reason: Option, - #[attr_value(skip)] /// The buffer and size information, if saved start_info: OnceCell, - #[attr_value(skip)] // #[builder(default = SystemTime::now())] /// The time the fuzzer was started at start_time: OnceCell, - #[attr_value(skip)] // #[builder(default = SystemTime::now())] /// The time the fuzzer was started at last_heartbeat_time: Option, - #[attr_value(skip)] log: OnceCell, - #[attr_value(skip)] /// Whether cmplog is currently enabled coverage_enabled: bool, - #[attr_value(skip)] /// Whether cmplog is currently enabled cmplog_enabled: bool, - #[attr_value(skip)] /// The number of the processor which starts the fuzzing loop (via magic or manual methods) start_processor_number: OnceCell, - #[attr_value(skip)] /// Tracked processors. This always includes the start processor, and may include /// additional processors that are manually added by the user processors: HashMap, - #[attr_value(skip)] /// A testcase to use for repro repro_testcase: Option>, - #[attr_value(skip)] /// Whether a bookmark has been set for repro mode repro_bookmark_set: bool, - #[attr_value(skip)] /// Whether the fuzzer is currently stopped in repro mode stopped_for_repro: bool, - #[attr_value(skip)] /// The number of iterations which have been executed so far iterations: usize, - #[attr_value(skip)] /// Whether snapshots are used. Snapshots are used on Simics 7.0.0 and later. use_snapshots: bool, - #[attr_value(skip)] /// The number of timeouts so far timeouts: usize, - #[attr_value(skip)] /// The number of solutions so far solutions: usize, + + windows_os_info: WindowsOsInfo, } impl ClassObjectsFinalize for Tsffs { @@ -589,6 +564,13 @@ impl ClassObjectsFinalize for Tsffs { .expect("Failed to execute on_magic_instruction callback") } })?; + tsffs.control_register_write_hap_handle = + CoreControlRegisterWriteHap::add_callback(move |trigger_obj, register_nr, value| { + let tsffs: &'static mut Tsffs = instance.into(); + tsffs + .on_control_register_write(trigger_obj, register_nr, value) + .expect("Failed to execute on_control_register_write callback") + })?; tsffs .coverage_map .set(OwnedMutSlice::from(vec![0; Tsffs::COVERAGE_MAP_SIZE])) @@ -726,10 +708,10 @@ impl Tsffs { // Disable VMP if it is enabled info!("Disabling VMP"); - if let Err(e) = run_command("disable-vmp") { warn!(self.as_conf_object(), "Failed to disable VMP: {}", e); } + self.log(LogMessage::startup())?; #[cfg(simics_version_7)] diff --git a/src/os/mod.rs b/src/os/mod.rs new file mode 100644 index 00000000..063ffbf2 --- /dev/null +++ b/src/os/mod.rs @@ -0,0 +1,3 @@ +#![allow(unused)] + +pub mod windows; diff --git a/src/os/windows/debug_info.rs b/src/os/windows/debug_info.rs new file mode 100644 index 00000000..5e60d8b4 --- /dev/null +++ b/src/os/windows/debug_info.rs @@ -0,0 +1,510 @@ +use anyhow::{anyhow, bail, Result}; +use goblin::pe::PE; +use intervaltree::Element; +use pdb::{FallibleIterator, SymbolData, PDB}; +use reqwest::blocking::get; +use std::{ + collections::{HashMap, HashSet}, + fs::{create_dir_all, File}, + io::{copy, Write}, + path::{Path, PathBuf}, +}; + +use lending_iterator::{windows_mut, LendingIterator}; +use simics::{debug, info, ConfObject}; +use windows::Win32::System::{ + Diagnostics::Debug::{ + IMAGE_DEBUG_DIRECTORY, IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DIRECTORY_ENTRY_DEBUG, + IMAGE_NT_HEADERS64, + }, + SystemServices::IMAGE_DOS_HEADER, +}; + +use super::{ + pdb::{CvInfoPdb70, Export}, + util::{read_virtual, read_virtual_dtb}, +}; + +#[derive(Debug)] +pub struct DebugInfo<'a> { + pub exe_path: PathBuf, + pub pdb_path: PathBuf, + pub exe_file_contents: Vec, + pub pdb: PDB<'a, File>, +} + +impl<'a> DebugInfo<'a> { + pub fn new

( + processor: *mut ConfObject, + name: &str, + base: u64, + download_directory: P, + not_found_full_name_cache: &mut HashSet, + user_debug_info: &HashMap>, + ) -> Result + where + P: AsRef, + { + if let Some(info) = user_debug_info.get(name) { + debug!("Have user-provided debug info for {name}"); + let exe_path = info[0].clone(); + let pdb_path = info[1].clone(); + + let exe_file_contents = std::fs::read(&exe_path)?; + + let pdb_file = File::open(&pdb_path)?; + + let pdb = PDB::open(pdb_file)?; + + Ok(Self { + exe_path, + pdb_path, + exe_file_contents, + pdb, + }) + } else { + let dos_header = read_virtual::(processor, base)?; + let nt_header = + read_virtual::(processor, base + dos_header.e_lfanew as u64)?; + let debug_data_directory_offset = nt_header.OptionalHeader.DataDirectory + [IMAGE_DIRECTORY_ENTRY_DEBUG.0 as usize] + .VirtualAddress; + let debug_data_directory_size = + nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG.0 as usize].Size; + let debug_directory = (base + debug_data_directory_offset as u64 + ..base + debug_data_directory_offset as u64 + debug_data_directory_size as u64) + .step_by(std::mem::size_of::()) + .filter_map(|offset| read_virtual::(processor, offset).ok()) + .filter(|dd| dd.Type == IMAGE_DEBUG_TYPE_CODEVIEW) + .take(1) + .next() + .ok_or_else(|| anyhow!("Failed to find debug data directory with codeview type"))?; + + if debug_directory.SizeOfData == 0 || debug_directory.AddressOfRawData == 0 { + bail!("Invalid debug data directory"); + } + + let cv_info_pdb70 = + CvInfoPdb70::new(processor, base + debug_directory.AddressOfRawData as u64)?; + + let exe_guid = format!( + "{:08X}{:05X}", + nt_header.FileHeader.TimeDateStamp, nt_header.OptionalHeader.SizeOfImage + ); + + // Download kernel PDB file + let pdb_url = format!( + "https://msdl.microsoft.com/download/symbols/{}/{}/{}", + cv_info_pdb70.file_name(), + cv_info_pdb70.guid(), + cv_info_pdb70.file_name() + ); + + let exe_url = format!( + "https://msdl.microsoft.com/download/symbols/{}/{}/{}", + name, exe_guid, name + ); + + if !download_directory.as_ref().is_dir() { + create_dir_all(&download_directory)?; + } + + // Download kernel PE file + let exe_path = download_directory + .as_ref() + .join(format!("{}.exe", &exe_guid)); + + if !exe_path.exists() && !not_found_full_name_cache.contains(name) { + info!("Downloading PE file from {}", exe_url); + match get(&exe_url)?.error_for_status() { + Ok(response) => { + let mut file = File::create(&exe_path)?; + copy(&mut response.bytes()?.as_ref(), &mut file)?; + file.flush()?; + } + Err(e) => { + not_found_full_name_cache.insert(name.to_string()); + bail!("Failed to download PE file: {}", e); + } + } + } + + let pdb_path = download_directory + .as_ref() + .join(format!("{}.pdb", cv_info_pdb70.guid())); + + if !pdb_path.exists() && !not_found_full_name_cache.contains(cv_info_pdb70.file_name()) + { + info!("Downloading PDB file from {}", pdb_url); + match get(&pdb_url)?.error_for_status() { + Ok(response) => { + let mut file = File::create(&pdb_path)?; + copy(&mut response.bytes()?.as_ref(), &mut file)?; + file.flush()?; + } + Err(e) => { + not_found_full_name_cache.insert(cv_info_pdb70.guid().to_string()); + bail!("Failed to download PDB file: {}", e); + } + } + } + + let exe_file_contents = std::fs::read(&exe_path)?; + + let pdb_file = File::open(&pdb_path)?; + + let pdb = PDB::open(pdb_file)?; + + Ok(Self { + exe_path, + pdb_path, + exe_file_contents, + pdb, + }) + } + } + + pub fn new_dtb

( + processor: *mut ConfObject, + name: &str, + base: u64, + download_directory: P, + directory_table_base: u64, + not_found_full_name_cache: &mut HashSet, + user_debug_info: &HashMap>, + ) -> Result + where + P: AsRef, + { + if let Some(info) = user_debug_info.get(name) { + debug!("Have user-provided debug info for {name}"); + let exe_path = info[0].clone(); + let pdb_path = info[1].clone(); + + let exe_file_contents = std::fs::read(&exe_path)?; + + let pdb_file = File::open(&pdb_path)?; + + let pdb = PDB::open(pdb_file)?; + + Ok(Self { + exe_path, + pdb_path, + exe_file_contents, + pdb, + }) + } else { + let dos_header = + read_virtual_dtb::(processor, directory_table_base, base)?; + let nt_header = read_virtual_dtb::( + processor, + directory_table_base, + base + dos_header.e_lfanew as u64, + )?; + let debug_data_directory_offset = nt_header.OptionalHeader.DataDirectory + [IMAGE_DIRECTORY_ENTRY_DEBUG.0 as usize] + .VirtualAddress; + let debug_data_directory_size = + nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG.0 as usize].Size; + let debug_directory = (base + debug_data_directory_offset as u64 + ..base + debug_data_directory_offset as u64 + debug_data_directory_size as u64) + .step_by(std::mem::size_of::()) + .filter_map(|offset| { + read_virtual_dtb::( + processor, + directory_table_base, + offset, + ) + .ok() + }) + .filter(|dd| dd.Type == IMAGE_DEBUG_TYPE_CODEVIEW) + .take(1) + .next() + .ok_or_else(|| anyhow!("Failed to find debug data directory with codeview type"))?; + + if debug_directory.SizeOfData == 0 || debug_directory.AddressOfRawData == 0 { + bail!("Invalid debug data directory"); + } + + let cv_info_pdb70 = + CvInfoPdb70::new(processor, base + debug_directory.AddressOfRawData as u64)?; + + let exe_guid = format!( + "{:08X}{:05X}", + nt_header.FileHeader.TimeDateStamp, nt_header.OptionalHeader.SizeOfImage + ); + + // Download kernel PDB file + let pdb_url = format!( + "https://msdl.microsoft.com/download/symbols/{}/{}/{}", + cv_info_pdb70.file_name(), + cv_info_pdb70.guid(), + cv_info_pdb70.file_name() + ); + + let exe_url = format!( + "https://msdl.microsoft.com/download/symbols/{}/{}/{}", + name, exe_guid, name + ); + + if !download_directory.as_ref().is_dir() { + create_dir_all(&download_directory)?; + } + + // Download kernel PE file + let exe_path = download_directory + .as_ref() + .join(format!("{}.exe", &exe_guid)); + + if !exe_path.exists() && !not_found_full_name_cache.contains(name) { + info!("Downloading PE file from {}", exe_url); + match get(&exe_url)?.error_for_status() { + Ok(response) => { + let mut file = File::create(&exe_path)?; + copy(&mut response.bytes()?.as_ref(), &mut file)?; + file.flush()?; + } + Err(e) => { + not_found_full_name_cache.insert(name.to_string()); + bail!("Failed to download PE file: {}", e); + } + } + } + + let exe_file_contents = std::fs::read(&exe_path)?; + + let pdb_path = download_directory + .as_ref() + .join(format!("{}.pdb", cv_info_pdb70.guid())); + + if !pdb_path.exists() && !not_found_full_name_cache.contains(cv_info_pdb70.file_name()) + { + info!("Downloading PDB file from {}", pdb_url); + match get(&pdb_url)?.error_for_status() { + Ok(response) => { + let mut file = File::create(&pdb_path)?; + copy(&mut response.bytes()?.as_ref(), &mut file)?; + file.flush()?; + } + Err(e) => { + not_found_full_name_cache.insert(cv_info_pdb70.guid().to_string()); + bail!("Failed to download PDB file: {}", e); + } + } + } + + let pdb_file = File::open(&pdb_path)?; + + let pdb = PDB::open(pdb_file)?; + + Ok(Self { + exe_path, + pdb_path, + exe_file_contents, + pdb, + }) + } + } + + pub fn exe(&self) -> Result> { + PE::parse(&self.exe_file_contents) + .map_err(move |e| anyhow!("Failed to parse PE file: {}", e)) + } + + pub fn exports(&self) -> Result> { + Ok(self.exe()?.exports.iter().map(Export::from).collect()) + } +} + +#[derive(Debug)] +pub struct ProcessModule { + pub base: u64, + pub size: u64, + pub full_name: String, + pub base_name: String, + pub debug_info: Option>, +} + +impl ProcessModule { + pub fn intervals( + &mut self, + guess_pdb_function_size: bool, + ) -> Result>> { + let mut syms = Vec::new(); + + if let Some(debug_info) = self.debug_info.as_mut() { + let symbol_table = debug_info.pdb.global_symbols()?; + let address_map = debug_info.pdb.address_map()?; + // let debug_information = debug_info.pdb.debug_information()?; + let mut symbols = symbol_table.iter(); + while let Some(symbol) = symbols.next()? { + match symbol.parse() { + Ok(sd) => { + match sd { + SymbolData::Public(p) => { + if p.function { + // NOTE: Public symbols don't have sizes, the address is just + // the RVA of their entry point, so we just do an entry of size 1 + if let Some(rva) = p.offset.to_rva(&address_map) { + let info = SymbolInfo::new( + rva.0 as u64, + 0, + p.name.to_string().to_string(), + self.full_name.clone(), + ); + syms.push(info); + } + } + } + SymbolData::Procedure(p) => { + if let Some(rva) = p.offset.to_rva(&address_map) { + let info = SymbolInfo::new( + rva.0 as u64, + p.len as u64, + p.name.to_string().to_string(), + self.full_name.clone(), + ); + syms.push(info); + } + } + SymbolData::ProcedureReference(_p) => { + // TODO + } + SymbolData::Trampoline(_t) => { + // TODO + } + _ => {} + } + } + Err(e) => { + let _ = e; + } + } + } + } + + if guess_pdb_function_size { + syms.sort_by(|a, b| a.rva.cmp(&b.rva)); + windows_mut(&mut syms).for_each(|w: &mut [SymbolInfo; 2]| { + if w[0].size == 0 { + w[0].size = w[1].rva - w[0].rva; + } + }); + } + + Ok(syms + .into_iter() + .map(|s| (self.base + s.rva..self.base + s.rva + s.size, s).into()) + .collect()) + } +} + +#[derive(Debug)] +pub struct Process { + pub pid: u64, + pub file_name: String, + pub base_address: u64, + pub modules: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SymbolInfo { + pub rva: u64, + pub size: u64, + pub name: String, + pub module: String, +} + +impl SymbolInfo { + pub fn new(rva: u64, size: u64, name: String, module: String) -> Self { + Self { + rva, + size, + name, + module, + } + } +} + +#[derive(Debug)] +pub struct Module { + pub base: u64, + pub entry: u64, + pub size: u64, + pub full_name: String, + pub base_name: String, + pub debug_info: Option>, +} + +impl Module { + pub fn intervals( + &mut self, + guess_pdb_function_size: bool, + ) -> Result>> { + let mut syms = Vec::new(); + + if let Some(debug_info) = self.debug_info.as_mut() { + let symbol_table = debug_info.pdb.global_symbols()?; + let address_map = debug_info.pdb.address_map()?; + let mut symbols = symbol_table.iter(); + while let Some(symbol) = symbols.next()? { + match symbol.parse() { + Ok(sd) => { + match sd { + SymbolData::Public(p) => { + if p.function { + // NOTE: Public symbols don't have sizes, the address is just + // the RVA of their entry point, so we just do an entry of size 1 + if let Some(rva) = p.offset.to_rva(&address_map) { + let info = SymbolInfo::new( + rva.0 as u64, + 1, + p.name.to_string().to_string(), + self.full_name.clone(), + ); + syms.push(info); + } + } + } + SymbolData::Procedure(p) => { + if let Some(rva) = p.offset.to_rva(&address_map) { + let info = SymbolInfo::new( + rva.0 as u64, + p.len as u64, + p.name.to_string().to_string(), + self.full_name.clone(), + ); + syms.push(info); + } + } + SymbolData::ProcedureReference(_p) => { + // TODO + } + SymbolData::Trampoline(_t) => { + // TODO + } + _ => {} + } + } + Err(e) => { + let _ = e; + } + } + } + } + + if guess_pdb_function_size { + syms.sort_by(|a, b| a.rva.cmp(&b.rva)); + windows_mut(&mut syms).for_each(|w: &mut [SymbolInfo; 2]| { + if w[0].size == 0 { + w[0].size = w[1].rva - w[0].rva; + } + }); + } + + Ok(syms + .into_iter() + .map(|s| (self.base + s.rva..self.base + s.rva + s.size, s).into()) + .collect()) + } +} diff --git a/src/os/windows/idt.rs b/src/os/windows/idt.rs new file mode 100644 index 00000000..cd811c39 --- /dev/null +++ b/src/os/windows/idt.rs @@ -0,0 +1,38 @@ +#[repr(C)] // NOTE: Without repr(C) alignment causes corruption +#[derive(Debug, Clone)] +// NOTE: The vergilius generated struct is incorrectly sized +pub struct IdtEntry64 { + offset_low: u16, + selector: u16, + ist: u8, + type_attr: u8, + offset_middle: u16, + offset_high: u32, + _reserved: u32, +} + +impl IdtEntry64 { + pub fn offset(&self) -> u64 { + (self.offset_high as u64) << 32 | (self.offset_middle as u64) << 16 | self.offset_low as u64 + } + + pub fn selector(&self) -> u16 { + self.selector + } + + pub fn ist(&self) -> u8 { + self.ist & 0b111 + } + + pub fn gate_type(&self) -> u8 { + self.type_attr & 0b1111 + } + + pub fn dpl(&self) -> u8 { + (self.type_attr >> 5) & 0b11 + } + + pub fn present(&self) -> bool { + (self.type_attr >> 7) & 1 == 1 + } +} diff --git a/src/os/windows/kernel.rs b/src/os/windows/kernel.rs new file mode 100644 index 00000000..7cb08aad --- /dev/null +++ b/src/os/windows/kernel.rs @@ -0,0 +1,488 @@ +use std::{ + collections::{HashMap, HashSet}, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, bail, Result}; +use pdb::{FallibleIterator, SymbolData}; +use simics::{debug, get_attribute, ConfObject}; +use vergilius::bindings::*; +use windows::Win32::System::{ + Diagnostics::Debug::{IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_NT_HEADERS64}, + Kernel::LIST_ENTRY, + SystemServices::{ + IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, IMAGE_EXPORT_DIRECTORY, IMAGE_NT_SIGNATURE, + }, +}; + +use crate::os::windows::{ + debug_info::DebugInfo, + idt::IdtEntry64, + util::{read_nul_terminated_string, read_unicode_string, read_virtual}, +}; + +use super::{ + debug_info::{Module, Process}, + structs::{WindowsEProcess, WindowsKThread, WindowsKpcr, WindowsKprcb}, +}; + +const KUSER_SHARED_DATA_ADDRESS_X86_64: u64 = 0xFFFFF78000000000; + +pub fn page_is_kernel(processor: *mut ConfObject, address: u64) -> Result { + const OPTIONAL_HEADER_SIGNATURE_PE32: u16 = 0x10b; + const OPTIONAL_HEADER_SIGNATURE_PE32_PLUS: u16 = 0x20b; + + let dos_header = read_virtual::(processor, address)?; + + if dos_header.e_magic != IMAGE_DOS_SIGNATURE { + return Ok(false); + } + + let nt_header = + read_virtual::(processor, address + dos_header.e_lfanew as u64)?; + + if nt_header.Signature != IMAGE_NT_SIGNATURE { + debug!( + "NT Signature {:#x} != {:#x}", + nt_header.Signature, IMAGE_NT_SIGNATURE + ); + return Ok(false); + } + + debug!( + "Found NT signature at {:#x}", + address + dos_header.e_lfanew as u64 + ); + + if nt_header.FileHeader.SizeOfOptionalHeader == 0 { + debug!("Optional header size was 0"); + return Ok(false); + } + + if ![ + OPTIONAL_HEADER_SIGNATURE_PE32, + OPTIONAL_HEADER_SIGNATURE_PE32_PLUS, + ] + .contains(&nt_header.OptionalHeader.Magic.0) + { + debug!( + "Optional header magic {:#x} unrecognized", + nt_header.OptionalHeader.Magic.0 + ); + return Ok(false); + } + + let image_size = nt_header.OptionalHeader.SizeOfImage as u64; + + debug!("Image size is {:#x}", image_size); + + let export_header_offset = nt_header.OptionalHeader.DataDirectory + [IMAGE_DIRECTORY_ENTRY_EXPORT.0 as usize] + .VirtualAddress as u64; + let export_header_size = + nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT.0 as usize].Size as u64; + + if export_header_offset == 0 || export_header_offset >= image_size { + debug!( + "Export header offset {:#x} invalid for image sized {:#x}", + export_header_offset, image_size + ); + return Ok(false); + } + + if export_header_size == 0 || export_header_offset + export_header_size > image_size { + debug!( + "Export header size {:#x} and offset {:#x} invalid for image sized {:#x}", + export_header_size, export_header_offset, image_size + ); + return Ok(false); + } + + debug!( + "Export header offset {:#x} size {:#x}", + export_header_offset, export_header_size + ); + + let export_directory = + read_virtual::(processor, address + export_header_offset)?; + + let name = read_nul_terminated_string(processor, address + export_directory.Name as u64)?; + + debug!("Read image name {}", name); + + if name == "ntoskrnl.exe" { + return Ok(true); + } + + Ok(false) +} + +pub fn find_kernel(processor: *mut ConfObject, start: u64, step: u64) -> Result { + let mut scan_address = start & !(step - 1); + let stop_address = start & !(0x1000000000000 - 1); + + debug!( + "Scanning for kernel from {:#x}->{:#x}", + stop_address, scan_address + ); + + while scan_address >= stop_address { + if page_is_kernel(processor, scan_address)? { + return Ok(scan_address); + } + + scan_address -= step; + } + + bail!("Kernel not found"); +} + +pub fn find_kernel_with_idt(processor: *mut ConfObject, build: u32) -> Result { + let sim_idtr_base: u64 = get_attribute(processor, "idtr_base")?.try_into()?; + + for i in 0..256 { + // try each idtr entry + let idtr_entry0 = read_virtual::( + processor, + sim_idtr_base + (i * std::mem::size_of::() as u64), + )?; + if !idtr_entry0.present() { + debug!("Entry {} not present, skipping", i); + continue; + } + let idtr_entry0_offset = idtr_entry0.offset(); + debug!("Got valid IDT entry with offset {:#x}", idtr_entry0_offset); + return find_kernel( + processor, + idtr_entry0_offset, + if build >= 19000 { 0x200000 } else { 0x1000 }, + ); + } + + bail!("Kernel not found"); +} + +#[derive(Debug)] +pub struct KernelInfo { + pub base: u64, + pub major: u32, + pub minor: u32, + pub build: u32, + pub debug_info: DebugInfo<'static>, +} + +impl KernelInfo { + pub fn new

( + processor: *mut ConfObject, + name: &str, + base: u64, + download_directory: P, + not_found_full_name_cache: &mut HashSet, + user_debug_info: &HashMap>, + ) -> Result + where + P: AsRef, + { + let debug_info = DebugInfo::new( + processor, + name, + base, + download_directory, + not_found_full_name_cache, + user_debug_info, + )?; + + let kuser_shared_data = read_virtual::( + processor, + KUSER_SHARED_DATA_ADDRESS_X86_64, + )?; + + Ok(Self { + base, + major: kuser_shared_data.NtMajorVersion, + minor: kuser_shared_data.NtMinorVersion, + build: kuser_shared_data.NtBuildNumber, + debug_info, + }) + } + + fn find_ps_loaded_module_list_address(&mut self) -> Result { + // PsLoadedModuleList is either a public(publicsymbol) in the PDB file, or if it is not + // in the PDB file, we can find it via the export table in the PE file. + let pdb_symbols = self.debug_info.pdb.global_symbols()?; + let pdb_address_map = self.debug_info.pdb.address_map()?; + if let Ok(Some(ps_loaded_module_list_symbol)) = + pdb_symbols.iter().find_map(|symbol| match symbol.parse() { + Ok(symbol) => { + if let SymbolData::Public(public_symbol) = symbol { + if public_symbol.name.to_string() == "PsLoadedModuleList" { + Ok(Some( + public_symbol + .offset + .to_rva(&pdb_address_map) + .ok_or_else(|| pdb::Error::AddressMapNotFound)? + .0 as u64 + + self.base, + )) + } else { + Ok(None) + } + } else { + Ok(None) + } + } + Err(e) => Err(e), + }) + { + Ok(ps_loaded_module_list_symbol) + } else { + self.debug_info + .exports()? + .iter() + .find(|export| { + export + .name + .as_ref() + .is_some_and(|name| name == "PsLoadedModuleList") + }) + .map(|export| export.rva as u64 + self.base) + .ok_or_else(|| anyhow!("PsLoadedModuleList not found")) + } + } + + pub fn loaded_module_list

( + &mut self, + processor: *mut ConfObject, + download_directory: P, + not_found_full_name_cache: &mut HashSet, + user_debug_info: &HashMap>, + ) -> Result> + where + P: AsRef, + { + let list_address = self.find_ps_loaded_module_list_address()?; + debug!("PsLoadedModuleList: {:#x}", list_address); + let list = read_virtual::(processor, list_address)?; + let mut modules = Vec::new(); + + let mut current = list.Flink; + + while current != list_address as *mut _ { + // NOTE: _KLDR_DATA_TABLE_ENTRY struct is stable for all versions of 10, *except* for the following fields: + // union + // { + // VOID* Spare; //0x90 + // struct _KLDR_DATA_TABLE_ENTRY* NtDataTableEntry; //0x90 + // }; + // ULONG SizeOfImageNotRounded; //0x98 + // ULONG TimeDateStamp; //0x9c + // + // We don't use these, so it's ok to just use the latest version of this struct instead of generalizing + let ldr_data_table_entry = read_virtual::< + windows_10_0_22631_2428_x64::_KLDR_DATA_TABLE_ENTRY, + >(processor, current as u64)?; + + let base = ldr_data_table_entry.DllBase as u64; + let entry = ldr_data_table_entry.EntryPoint as u64; + let size = ldr_data_table_entry.SizeOfImage as u64; + let full_name = read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + )?; + let base_name = read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + )?; + let debug_info = full_name + .split('\\') + .last() + .ok_or_else(|| anyhow!("Failed to get file name")) + .and_then(|fname| { + // No need for DTB version because kernel is always mapped + DebugInfo::new( + processor, + fname, + base, + download_directory.as_ref(), + not_found_full_name_cache, + user_debug_info, + ) + }) + .ok(); + + modules.push(Module { + base, + entry, + size, + full_name, + base_name, + debug_info, + }); + + current = ldr_data_table_entry.InLoadOrderLinks.Flink as *mut _; + } + + Ok(modules) + } + + fn find_ps_initial_system_process_address(&mut self) -> Result { + // PsInitialSystemProcess is either a public(publicsymbol) in the PDB file, or if it is not + // in the PDB file, we can find it via the export table in the PE file. + let pdb_symbols = self.debug_info.pdb.global_symbols()?; + let pdb_address_map = self.debug_info.pdb.address_map()?; + if let Ok(Some(ps_initial_system_process_symbol)) = + pdb_symbols.iter().find_map(|symbol| match symbol.parse() { + Ok(symbol) => { + if let SymbolData::Public(public_symbol) = symbol { + if public_symbol.name.to_string() == "PsInitialSystemProcess" { + Ok(Some( + public_symbol + .offset + .to_rva(&pdb_address_map) + .ok_or_else(|| pdb::Error::AddressMapNotFound)? + .0 as u64 + + self.base, + )) + } else { + Ok(None) + } + } else { + Ok(None) + } + } + Err(e) => Err(e), + }) + { + Ok(ps_initial_system_process_symbol) + } else { + self.debug_info + .exports()? + .iter() + .find(|export| { + export + .name + .as_ref() + .is_some_and(|name| name == "PsInitialSystemProcess") + }) + .map(|export| export.rva as u64 + self.base) + .ok_or_else(|| anyhow!("PsInitialSystemProcess not found")) + } + } + + pub fn current_process

( + &mut self, + processor: *mut ConfObject, + download_directory: P, + not_found_full_name_cache: &mut HashSet, + user_debug_info: &HashMap>, + ) -> Result + where + P: AsRef, + { + let kpcr = WindowsKpcr::new(processor, self.major, self.minor, self.build)?; + let kprcb = WindowsKprcb::new( + processor, + self.major, + self.minor, + self.build, + kpcr.kpcrb_address(), + )?; + let kthread = WindowsKThread::new( + processor, + self.major, + self.minor, + self.build, + kprcb.current_thread(), + )?; + let eprocess = kthread.process(processor, self.major, self.minor, self.build)?; + + Ok(Process { + pid: eprocess.pid(), + file_name: eprocess.file_name(processor)?, + base_address: eprocess.base_address(processor, self.major, self.minor, self.build)?, + modules: eprocess + .modules( + processor, + self.major, + self.minor, + self.build, + download_directory.as_ref(), + not_found_full_name_cache, + user_debug_info, + ) + .unwrap_or_default(), + }) + } + + pub fn process_list

( + &mut self, + processor: *mut ConfObject, + download_directory: P, + not_found_full_name_cache: &mut HashSet, + user_debug_info: &HashMap>, + ) -> Result> + where + P: AsRef, + { + let kpcr = WindowsKpcr::new(processor, self.major, self.minor, self.build)?; + let kprcb = WindowsKprcb::new( + processor, + self.major, + self.minor, + self.build, + kpcr.kpcrb_address(), + )?; + let kthread = WindowsKThread::new( + processor, + self.major, + self.minor, + self.build, + kprcb.current_thread(), + )?; + let eprocess = kthread.process(processor, self.major, self.minor, self.build)?; + // Print initial process info + + let mut processes = Vec::new(); + + let mut list_entry = eprocess.active_process_links(); + let last_entry = list_entry.Blink; + + while !list_entry.Flink.is_null() { + let eprocess = WindowsEProcess::new_from_active_process_links_address( + processor, + self.major, + self.minor, + self.build, + list_entry.Flink as u64, + )?; + + processes.push(Process { + pid: eprocess.pid(), + file_name: eprocess.file_name(processor)?, + base_address: eprocess + .base_address(processor, self.major, self.minor, self.build)?, + modules: eprocess + .modules( + processor, + self.major, + self.minor, + self.build, + download_directory.as_ref(), + not_found_full_name_cache, + user_debug_info, + ) + .unwrap_or_default(), + }); + + list_entry = eprocess.active_process_links(); + + if list_entry.Flink == last_entry { + break; + } + } + + Ok(processes) + } +} diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs new file mode 100644 index 00000000..024fd216 --- /dev/null +++ b/src/os/windows/mod.rs @@ -0,0 +1,221 @@ +use anyhow::{anyhow, ensure, Result}; +use debug_info::{Module, Process, SymbolInfo}; +use ffi::ffi; +use intervaltree::IntervalTree; +use kernel::{find_kernel_with_idt, KernelInfo}; +use raw_cstr::AsRawCstr; +use simics::{ + get_interface, get_processor_number, sys::cpu_cb_handle_t, ConfObject, + CpuInstrumentationSubscribeInterface, IntRegisterInterface, ProcessorInfoV2Interface, +}; +use std::{ + collections::{hash_map::Entry, HashMap, HashSet}, + ffi::c_void, + path::{Path, PathBuf}, +}; +use structs::WindowsKpcr; +use util::read_virtual; + +use vergilius::bindings::*; + +use crate::Tsffs; + +pub mod debug_info; +pub mod idt; +pub mod kernel; +pub mod paging; +pub mod pdb; +pub mod structs; +pub mod util; + +const KUSER_SHARED_DATA_ADDRESS_X86_64: u64 = 0xFFFFF78000000000; + +#[derive(Debug)] +pub struct CpuInstrumentationCbHandle(usize); + +impl From<*mut cpu_cb_handle_t> for CpuInstrumentationCbHandle { + fn from(value: *mut cpu_cb_handle_t) -> Self { + Self(value as usize) + } +} + +impl From for *mut cpu_cb_handle_t { + fn from(value: CpuInstrumentationCbHandle) -> Self { + value.0 as *mut _ + } +} + +#[derive(Debug, Default)] +pub struct WindowsOsInfo { + /// Kernel info + pub kernel_info: Option, + /// Per-CPU process list, there may be overlap between them. + pub processes: HashMap>, + /// Per-CPU kernel module list, there may be overlap between them. + pub modules: HashMap>, + /// Per-CPU Symbol lookup trees + pub symbol_lookup_trees: HashMap>, + /// Cache of full names of both processes and kernel modules which are not found from + /// the pdb server + pub not_found_full_name_cache: HashSet, + /// Callbacks on instruction to do coverage lookups + pub instruction_callbacks: HashMap, +} + +impl WindowsOsInfo { + pub fn collect

( + &mut self, + processor: *mut ConfObject, + download_directory: P, + guess_pdb_function_size: bool, + user_debug_info: &HashMap>, + ) -> Result<()> + where + P: AsRef, + { + let processor_nr = get_processor_number(processor)?; + let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(processor)?; + + if self.kernel_info.is_none() { + // Make sure we're running 64-bit Windows + ensure!( + processor_info_v2.get_logical_address_width()? == 64, + "Only 64-bit Windows is supported" + ); + + let kuser_shared_data = read_virtual::( + processor, + KUSER_SHARED_DATA_ADDRESS_X86_64, + )?; + + let (maj, min, build) = ( + kuser_shared_data.NtMajorVersion, + kuser_shared_data.NtMinorVersion, + kuser_shared_data.NtBuildNumber, + ); + + ensure!(maj == 10, "Only Windows 10/11 is supported"); + + // Initialize the KPCR to make sure we have a valid one at gs_base + let _ = WindowsKpcr::new(processor, maj, min, build)?; + let kernel_base = find_kernel_with_idt(processor, build)?; + + self.kernel_info = Some(KernelInfo::new( + processor, + "ntoskrnl.exe", + kernel_base, + download_directory.as_ref(), + &mut self.not_found_full_name_cache, + user_debug_info, + )?); + } + + self.processes.insert( + processor_nr, + self.kernel_info + .as_mut() + .expect("Kernel Info must be set at this point") + .process_list( + processor, + download_directory.as_ref(), + &mut self.not_found_full_name_cache, + user_debug_info, + )?, + ); + + self.modules.insert( + processor_nr, + self.kernel_info + .as_mut() + .expect("Kernel Info must be set at this point") + .loaded_module_list( + processor, + download_directory.as_ref(), + &mut self.not_found_full_name_cache, + user_debug_info, + )?, + ); + + let elements = self + .modules + .get_mut(&processor_nr) + .ok_or_else(|| anyhow!("No modules for processor {processor_nr}"))? + .iter_mut() + .map(|m| m.intervals(guess_pdb_function_size)) + .collect::>>()? + .into_iter() + .chain( + self.kernel_info + .as_mut() + .expect("Kernel Info must be set at this point") + .current_process( + processor, + download_directory.as_ref(), + &mut self.not_found_full_name_cache, + user_debug_info, + )? + .modules + .iter_mut() + .map(|m| m.intervals(guess_pdb_function_size)) + .collect::>>()?, + ) + .flatten() + .collect::>(); + + let mut filtered_elements = HashSet::new(); + + // Deduplicate elements by their range + let elements = elements + .into_iter() + .filter(|e| filtered_elements.insert(e.range.clone())) + .collect::>(); + + self.symbol_lookup_trees.insert( + processor_nr, + elements.iter().cloned().collect::>(), + ); + + Ok(()) + } +} + +#[ffi(expect, self_ty = "*mut std::ffi::c_void")] +impl Tsffs { + #[ffi(arg(rest), arg(self))] + pub fn on_instruction_before_symcov( + &mut self, + _obj: *mut ConfObject, + cpu: *mut ConfObject, + _handle: *mut simics::sys::instruction_handle_t, + ) -> Result<()> { + let cpu_nr = get_processor_number(cpu)?; + + Ok(()) + } +} + +impl Tsffs { + pub fn on_control_register_write_windows_symcov( + &mut self, + trigger_obj: *mut ConfObject, + register_nr: i64, + _value: i64, + ) -> Result<()> { + let mut int_register: IntRegisterInterface = get_interface(trigger_obj)?; + + // Check if the register was CR3 + if self.windows + && self.symbolic_coverage + && register_nr == int_register.get_number("cr3".as_raw_cstr()?)? as i64 + { + self.windows_os_info.collect( + trigger_obj, + &self.debuginfo_download_directory, + self.guess_pdb_function_size, + &self.debug_info, + )?; + } + + Ok(()) + } +} diff --git a/src/os/windows/paging.rs b/src/os/windows/paging.rs new file mode 100644 index 00000000..2a41ff68 --- /dev/null +++ b/src/os/windows/paging.rs @@ -0,0 +1,2520 @@ +/* automatically generated by rust-bindgen 0.69.1 */ + +#![allow( + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + clippy::useless_transmute, + clippy::unnecessary_cast, + clippy::too_many_arguments, + clippy::upper_case_acronyms, + unused +)] + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct __BindgenBitfieldUnit { + storage: Storage, +} +impl __BindgenBitfieldUnit { + #[inline] + pub const fn new(storage: Storage) -> Self { + Self { storage } + } +} +impl __BindgenBitfieldUnit +where + Storage: AsRef<[u8]> + AsMut<[u8]>, +{ + #[inline] + pub fn get_bit(&self, index: usize) -> bool { + debug_assert!(index / 8 < self.storage.as_ref().len()); + let byte_index = index / 8; + let byte = self.storage.as_ref()[byte_index]; + let bit_index = if cfg!(target_endian = "big") { + 7 - (index % 8) + } else { + index % 8 + }; + let mask = 1 << bit_index; + byte & mask == mask + } + #[inline] + pub fn set_bit(&mut self, index: usize, val: bool) { + debug_assert!(index / 8 < self.storage.as_ref().len()); + let byte_index = index / 8; + let byte = &mut self.storage.as_mut()[byte_index]; + let bit_index = if cfg!(target_endian = "big") { + 7 - (index % 8) + } else { + index % 8 + }; + let mask = 1 << bit_index; + if val { + *byte |= mask; + } else { + *byte &= !mask; + } + } + #[inline] + pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + let mut val = 0; + for i in 0..(bit_width as usize) { + if self.get_bit(i + bit_offset) { + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + val |= 1 << index; + } + } + val + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + for i in 0..(bit_width as usize) { + let mask = 1 << i; + let val_bit_is_set = val & mask == mask; + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + self.set_bit(index + bit_offset, val_bit_is_set); + } + } +} +pub const _STDINT_H: u32 = 1; +pub const _FEATURES_H: u32 = 1; +pub const _DEFAULT_SOURCE: u32 = 1; +pub const __GLIBC_USE_ISOC2X: u32 = 0; +pub const __USE_ISOC11: u32 = 1; +pub const __USE_ISOC99: u32 = 1; +pub const __USE_ISOC95: u32 = 1; +pub const __USE_POSIX_IMPLICITLY: u32 = 1; +pub const _POSIX_SOURCE: u32 = 1; +pub const _POSIX_C_SOURCE: u32 = 200809; +pub const __USE_POSIX: u32 = 1; +pub const __USE_POSIX2: u32 = 1; +pub const __USE_POSIX199309: u32 = 1; +pub const __USE_POSIX199506: u32 = 1; +pub const __USE_XOPEN2K: u32 = 1; +pub const __USE_XOPEN2K8: u32 = 1; +pub const _ATFILE_SOURCE: u32 = 1; +pub const __WORDSIZE: u32 = 64; +pub const __WORDSIZE_TIME64_COMPAT32: u32 = 0; +pub const __TIMESIZE: u32 = 64; +pub const __USE_MISC: u32 = 1; +pub const __USE_ATFILE: u32 = 1; +pub const __USE_FORTIFY_LEVEL: u32 = 0; +pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0; +pub const __GLIBC_USE_DEPRECATED_SCANF: u32 = 0; +pub const __GLIBC_USE_C2X_STRTOL: u32 = 0; +pub const _STDC_PREDEF_H: u32 = 1; +pub const __STDC_IEC_559__: u32 = 1; +pub const __STDC_IEC_60559_BFP__: u32 = 201404; +pub const __STDC_IEC_559_COMPLEX__: u32 = 1; +pub const __STDC_IEC_60559_COMPLEX__: u32 = 201404; +pub const __STDC_ISO_10646__: u32 = 201706; +pub const __GNU_LIBRARY__: u32 = 6; +pub const __GLIBC__: u32 = 2; +pub const __GLIBC_MINOR__: u32 = 38; +pub const _SYS_CDEFS_H: u32 = 1; +pub const __glibc_c99_flexarr_available: u32 = 1; +pub const __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI: u32 = 0; +pub const __HAVE_GENERIC_SELECTION: u32 = 1; +pub const __GLIBC_USE_LIB_EXT2: u32 = 0; +pub const __GLIBC_USE_IEC_60559_BFP_EXT: u32 = 0; +pub const __GLIBC_USE_IEC_60559_BFP_EXT_C2X: u32 = 0; +pub const __GLIBC_USE_IEC_60559_EXT: u32 = 0; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT: u32 = 0; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT_C2X: u32 = 0; +pub const __GLIBC_USE_IEC_60559_TYPES_EXT: u32 = 0; +pub const _BITS_TYPES_H: u32 = 1; +pub const _BITS_TYPESIZES_H: u32 = 1; +pub const __OFF_T_MATCHES_OFF64_T: u32 = 1; +pub const __INO_T_MATCHES_INO64_T: u32 = 1; +pub const __RLIM_T_MATCHES_RLIM64_T: u32 = 1; +pub const __STATFS_MATCHES_STATFS64: u32 = 1; +pub const __FD_SETSIZE: u32 = 1024; +pub const _BITS_TIME64_H: u32 = 1; +pub const _BITS_WCHAR_H: u32 = 1; +pub const _BITS_STDINT_INTN_H: u32 = 1; +pub const _BITS_STDINT_UINTN_H: u32 = 1; +pub const INT8_MIN: i32 = -128; +pub const INT16_MIN: i32 = -32768; +pub const INT32_MIN: i32 = -2147483648; +pub const INT8_MAX: u32 = 127; +pub const INT16_MAX: u32 = 32767; +pub const INT32_MAX: u32 = 2147483647; +pub const UINT8_MAX: u32 = 255; +pub const UINT16_MAX: u32 = 65535; +pub const UINT32_MAX: u32 = 4294967295; +pub const INT_LEAST8_MIN: i32 = -128; +pub const INT_LEAST16_MIN: i32 = -32768; +pub const INT_LEAST32_MIN: i32 = -2147483648; +pub const INT_LEAST8_MAX: u32 = 127; +pub const INT_LEAST16_MAX: u32 = 32767; +pub const INT_LEAST32_MAX: u32 = 2147483647; +pub const UINT_LEAST8_MAX: u32 = 255; +pub const UINT_LEAST16_MAX: u32 = 65535; +pub const UINT_LEAST32_MAX: u32 = 4294967295; +pub const INT_FAST8_MIN: i32 = -128; +pub const INT_FAST16_MIN: i64 = -9223372036854775808; +pub const INT_FAST32_MIN: i64 = -9223372036854775808; +pub const INT_FAST8_MAX: u32 = 127; +pub const INT_FAST16_MAX: u64 = 9223372036854775807; +pub const INT_FAST32_MAX: u64 = 9223372036854775807; +pub const UINT_FAST8_MAX: u32 = 255; +pub const UINT_FAST16_MAX: i32 = -1; +pub const UINT_FAST32_MAX: i32 = -1; +pub const INTPTR_MIN: i64 = -9223372036854775808; +pub const INTPTR_MAX: u64 = 9223372036854775807; +pub const UINTPTR_MAX: i32 = -1; +pub const PTRDIFF_MIN: i64 = -9223372036854775808; +pub const PTRDIFF_MAX: u64 = 9223372036854775807; +pub const SIG_ATOMIC_MIN: i32 = -2147483648; +pub const SIG_ATOMIC_MAX: u32 = 2147483647; +pub const SIZE_MAX: i32 = -1; +pub const WINT_MIN: u32 = 0; +pub const WINT_MAX: u32 = 4294967295; +pub const _LIBC_LIMITS_H_: u32 = 1; +pub const MB_LEN_MAX: u32 = 16; +pub const _BITS_POSIX1_LIM_H: u32 = 1; +pub const _POSIX_AIO_LISTIO_MAX: u32 = 2; +pub const _POSIX_AIO_MAX: u32 = 1; +pub const _POSIX_ARG_MAX: u32 = 4096; +pub const _POSIX_CHILD_MAX: u32 = 25; +pub const _POSIX_DELAYTIMER_MAX: u32 = 32; +pub const _POSIX_HOST_NAME_MAX: u32 = 255; +pub const _POSIX_LINK_MAX: u32 = 8; +pub const _POSIX_LOGIN_NAME_MAX: u32 = 9; +pub const _POSIX_MAX_CANON: u32 = 255; +pub const _POSIX_MAX_INPUT: u32 = 255; +pub const _POSIX_MQ_OPEN_MAX: u32 = 8; +pub const _POSIX_MQ_PRIO_MAX: u32 = 32; +pub const _POSIX_NAME_MAX: u32 = 14; +pub const _POSIX_NGROUPS_MAX: u32 = 8; +pub const _POSIX_OPEN_MAX: u32 = 20; +pub const _POSIX_PATH_MAX: u32 = 256; +pub const _POSIX_PIPE_BUF: u32 = 512; +pub const _POSIX_RE_DUP_MAX: u32 = 255; +pub const _POSIX_RTSIG_MAX: u32 = 8; +pub const _POSIX_SEM_NSEMS_MAX: u32 = 256; +pub const _POSIX_SEM_VALUE_MAX: u32 = 32767; +pub const _POSIX_SIGQUEUE_MAX: u32 = 32; +pub const _POSIX_SSIZE_MAX: u32 = 32767; +pub const _POSIX_STREAM_MAX: u32 = 8; +pub const _POSIX_SYMLINK_MAX: u32 = 255; +pub const _POSIX_SYMLOOP_MAX: u32 = 8; +pub const _POSIX_TIMER_MAX: u32 = 32; +pub const _POSIX_TTY_NAME_MAX: u32 = 9; +pub const _POSIX_TZNAME_MAX: u32 = 6; +pub const _POSIX_CLOCKRES_MIN: u32 = 20000000; +pub const NR_OPEN: u32 = 1024; +pub const NGROUPS_MAX: u32 = 65536; +pub const ARG_MAX: u32 = 131072; +pub const LINK_MAX: u32 = 127; +pub const MAX_CANON: u32 = 255; +pub const MAX_INPUT: u32 = 255; +pub const NAME_MAX: u32 = 255; +pub const PATH_MAX: u32 = 4096; +pub const PIPE_BUF: u32 = 4096; +pub const XATTR_NAME_MAX: u32 = 255; +pub const XATTR_SIZE_MAX: u32 = 65536; +pub const XATTR_LIST_MAX: u32 = 65536; +pub const RTSIG_MAX: u32 = 32; +pub const _POSIX_THREAD_KEYS_MAX: u32 = 128; +pub const PTHREAD_KEYS_MAX: u32 = 1024; +pub const _POSIX_THREAD_DESTRUCTOR_ITERATIONS: u32 = 4; +pub const PTHREAD_DESTRUCTOR_ITERATIONS: u32 = 4; +pub const _POSIX_THREAD_THREADS_MAX: u32 = 64; +pub const AIO_PRIO_DELTA_MAX: u32 = 20; +pub const PTHREAD_STACK_MIN: u32 = 131072; +pub const DELAYTIMER_MAX: u32 = 2147483647; +pub const TTY_NAME_MAX: u32 = 32; +pub const LOGIN_NAME_MAX: u32 = 256; +pub const HOST_NAME_MAX: u32 = 64; +pub const MQ_PRIO_MAX: u32 = 32768; +pub const SEM_VALUE_MAX: u32 = 2147483647; +pub const _BITS_POSIX2_LIM_H: u32 = 1; +pub const _POSIX2_BC_BASE_MAX: u32 = 99; +pub const _POSIX2_BC_DIM_MAX: u32 = 2048; +pub const _POSIX2_BC_SCALE_MAX: u32 = 99; +pub const _POSIX2_BC_STRING_MAX: u32 = 1000; +pub const _POSIX2_COLL_WEIGHTS_MAX: u32 = 2; +pub const _POSIX2_EXPR_NEST_MAX: u32 = 32; +pub const _POSIX2_LINE_MAX: u32 = 2048; +pub const _POSIX2_RE_DUP_MAX: u32 = 255; +pub const _POSIX2_CHARCLASS_NAME_MAX: u32 = 14; +pub const BC_BASE_MAX: u32 = 99; +pub const BC_DIM_MAX: u32 = 2048; +pub const BC_SCALE_MAX: u32 = 99; +pub const BC_STRING_MAX: u32 = 1000; +pub const COLL_WEIGHTS_MAX: u32 = 255; +pub const EXPR_NEST_MAX: u32 = 32; +pub const LINE_MAX: u32 = 2048; +pub const CHARCLASS_NAME_MAX: u32 = 2048; +pub const RE_DUP_MAX: u32 = 32767; +pub const CHAR_MIN: u32 = 0; +pub const PAGE_1GB_SHIFT: u32 = 30; +pub const PAGE_2MB_SHIFT: u32 = 21; +pub const PAGE_4KB_SHIFT: u32 = 12; +pub type __u_char = ::std::os::raw::c_uchar; +pub type __u_short = ::std::os::raw::c_ushort; +pub type __u_int = ::std::os::raw::c_uint; +pub type __u_long = ::std::os::raw::c_ulong; +pub type __int8_t = ::std::os::raw::c_schar; +pub type __uint8_t = ::std::os::raw::c_uchar; +pub type __int16_t = ::std::os::raw::c_short; +pub type __uint16_t = ::std::os::raw::c_ushort; +pub type __int32_t = ::std::os::raw::c_int; +pub type __uint32_t = ::std::os::raw::c_uint; +pub type __int64_t = ::std::os::raw::c_long; +pub type __uint64_t = ::std::os::raw::c_ulong; +pub type __int_least8_t = __int8_t; +pub type __uint_least8_t = __uint8_t; +pub type __int_least16_t = __int16_t; +pub type __uint_least16_t = __uint16_t; +pub type __int_least32_t = __int32_t; +pub type __uint_least32_t = __uint32_t; +pub type __int_least64_t = __int64_t; +pub type __uint_least64_t = __uint64_t; +pub type __quad_t = ::std::os::raw::c_long; +pub type __u_quad_t = ::std::os::raw::c_ulong; +pub type __intmax_t = ::std::os::raw::c_long; +pub type __uintmax_t = ::std::os::raw::c_ulong; +pub type __dev_t = ::std::os::raw::c_ulong; +pub type __uid_t = ::std::os::raw::c_uint; +pub type __gid_t = ::std::os::raw::c_uint; +pub type __ino_t = ::std::os::raw::c_ulong; +pub type __ino64_t = ::std::os::raw::c_ulong; +pub type __mode_t = ::std::os::raw::c_uint; +pub type __nlink_t = ::std::os::raw::c_uint; +pub type __off_t = ::std::os::raw::c_long; +pub type __off64_t = ::std::os::raw::c_long; +pub type __pid_t = ::std::os::raw::c_int; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct __fsid_t { + pub __val: [::std::os::raw::c_int; 2usize], +} +pub type __clock_t = ::std::os::raw::c_long; +pub type __rlim_t = ::std::os::raw::c_ulong; +pub type __rlim64_t = ::std::os::raw::c_ulong; +pub type __id_t = ::std::os::raw::c_uint; +pub type __time_t = ::std::os::raw::c_long; +pub type __useconds_t = ::std::os::raw::c_uint; +pub type __suseconds_t = ::std::os::raw::c_long; +pub type __suseconds64_t = ::std::os::raw::c_long; +pub type __daddr_t = ::std::os::raw::c_int; +pub type __key_t = ::std::os::raw::c_int; +pub type __clockid_t = ::std::os::raw::c_int; +pub type __timer_t = *mut ::std::os::raw::c_void; +pub type __blksize_t = ::std::os::raw::c_int; +pub type __blkcnt_t = ::std::os::raw::c_long; +pub type __blkcnt64_t = ::std::os::raw::c_long; +pub type __fsblkcnt_t = ::std::os::raw::c_ulong; +pub type __fsblkcnt64_t = ::std::os::raw::c_ulong; +pub type __fsfilcnt_t = ::std::os::raw::c_ulong; +pub type __fsfilcnt64_t = ::std::os::raw::c_ulong; +pub type __fsword_t = ::std::os::raw::c_long; +pub type __ssize_t = ::std::os::raw::c_long; +pub type __syscall_slong_t = ::std::os::raw::c_long; +pub type __syscall_ulong_t = ::std::os::raw::c_ulong; +pub type __loff_t = __off64_t; +pub type __caddr_t = *mut ::std::os::raw::c_char; +pub type __intptr_t = ::std::os::raw::c_long; +pub type __socklen_t = ::std::os::raw::c_uint; +pub type __sig_atomic_t = ::std::os::raw::c_int; +pub type int_least8_t = __int_least8_t; +pub type int_least16_t = __int_least16_t; +pub type int_least32_t = __int_least32_t; +pub type int_least64_t = __int_least64_t; +pub type uint_least8_t = __uint_least8_t; +pub type uint_least16_t = __uint_least16_t; +pub type uint_least32_t = __uint_least32_t; +pub type uint_least64_t = __uint_least64_t; +pub type int_fast8_t = ::std::os::raw::c_schar; +pub type int_fast16_t = ::std::os::raw::c_long; +pub type int_fast32_t = ::std::os::raw::c_long; +pub type int_fast64_t = ::std::os::raw::c_long; +pub type uint_fast8_t = ::std::os::raw::c_uchar; +pub type uint_fast16_t = ::std::os::raw::c_ulong; +pub type uint_fast32_t = ::std::os::raw::c_ulong; +pub type uint_fast64_t = ::std::os::raw::c_ulong; +pub type intmax_t = __intmax_t; +pub type uintmax_t = __uintmax_t; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _VIRTUAL_MEMORY_ADDRESS { + pub Bits: _VIRTUAL_MEMORY_ADDRESS__bindgen_ty_1, + pub All: u64, +} +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct _VIRTUAL_MEMORY_ADDRESS__bindgen_ty_1 { + pub _bitfield_align_1: [u16; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>, +} +impl _VIRTUAL_MEMORY_ADDRESS__bindgen_ty_1 { + #[inline] + pub fn PageIndex(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 12u8) as u64) } + } + #[inline] + pub fn set_PageIndex(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 12u8, val as u64) + } + } + #[inline] + pub fn PtIndex(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 9u8) as u64) } + } + #[inline] + pub fn set_PtIndex(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(12usize, 9u8, val as u64) + } + } + #[inline] + pub fn PdIndex(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(21usize, 9u8) as u64) } + } + #[inline] + pub fn set_PdIndex(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(21usize, 9u8, val as u64) + } + } + #[inline] + pub fn PdptIndex(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(30usize, 9u8) as u64) } + } + #[inline] + pub fn set_PdptIndex(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(30usize, 9u8, val as u64) + } + } + #[inline] + pub fn Pml4Index(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(39usize, 9u8) as u64) } + } + #[inline] + pub fn set_Pml4Index(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(39usize, 9u8, val as u64) + } + } + #[inline] + pub fn Unused(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(48usize, 16u8) as u64) } + } + #[inline] + pub fn set_Unused(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(48usize, 16u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + PageIndex: u64, + PtIndex: u64, + PdIndex: u64, + PdptIndex: u64, + Pml4Index: u64, + Unused: u64, + ) -> __BindgenBitfieldUnit<[u8; 8usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 12u8, { + let PageIndex: u64 = unsafe { ::std::mem::transmute(PageIndex) }; + PageIndex as u64 + }); + __bindgen_bitfield_unit.set(12usize, 9u8, { + let PtIndex: u64 = unsafe { ::std::mem::transmute(PtIndex) }; + PtIndex as u64 + }); + __bindgen_bitfield_unit.set(21usize, 9u8, { + let PdIndex: u64 = unsafe { ::std::mem::transmute(PdIndex) }; + PdIndex as u64 + }); + __bindgen_bitfield_unit.set(30usize, 9u8, { + let PdptIndex: u64 = unsafe { ::std::mem::transmute(PdptIndex) }; + PdptIndex as u64 + }); + __bindgen_bitfield_unit.set(39usize, 9u8, { + let Pml4Index: u64 = unsafe { ::std::mem::transmute(Pml4Index) }; + Pml4Index as u64 + }); + __bindgen_bitfield_unit.set(48usize, 16u8, { + let Unused: u64 = unsafe { ::std::mem::transmute(Unused) }; + Unused as u64 + }); + __bindgen_bitfield_unit + } +} +impl Default for _VIRTUAL_MEMORY_ADDRESS { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl ::std::fmt::Debug for _VIRTUAL_MEMORY_ADDRESS { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "_VIRTUAL_MEMORY_ADDRESS {{ union }}") + } +} +pub type VIRTUAL_ADDRESS = _VIRTUAL_MEMORY_ADDRESS; +pub type PVIRTUAL_ADDRESS = *mut _VIRTUAL_MEMORY_ADDRESS; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _DIRECTORY_TABLE_BASE { + pub Bits: _DIRECTORY_TABLE_BASE__bindgen_ty_1, + pub All: u64, +} +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct _DIRECTORY_TABLE_BASE__bindgen_ty_1 { + pub _bitfield_align_1: [u64; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>, +} +impl _DIRECTORY_TABLE_BASE__bindgen_ty_1 { + #[inline] + pub fn Ignored0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 3u8) as u64) } + } + #[inline] + pub fn set_Ignored0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 3u8, val as u64) + } + } + #[inline] + pub fn PageWriteThrough(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageWriteThrough(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageCacheDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageCacheDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored1(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 7u8) as u64) } + } + #[inline] + pub fn set__Ignored1(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 7u8, val as u64) + } + } + #[inline] + pub fn PhysicalAddress(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 36u8) as u64) } + } + #[inline] + pub fn set_PhysicalAddress(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(12usize, 36u8, val as u64) + } + } + #[inline] + pub fn _Reserved0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(48usize, 16u8) as u64) } + } + #[inline] + pub fn set__Reserved0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(48usize, 16u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + Ignored0: u64, + PageWriteThrough: u64, + PageCacheDisable: u64, + _Ignored1: u64, + PhysicalAddress: u64, + _Reserved0: u64, + ) -> __BindgenBitfieldUnit<[u8; 8usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 3u8, { + let Ignored0: u64 = unsafe { ::std::mem::transmute(Ignored0) }; + Ignored0 as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let PageWriteThrough: u64 = unsafe { ::std::mem::transmute(PageWriteThrough) }; + PageWriteThrough as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let PageCacheDisable: u64 = unsafe { ::std::mem::transmute(PageCacheDisable) }; + PageCacheDisable as u64 + }); + __bindgen_bitfield_unit.set(5usize, 7u8, { + let _Ignored1: u64 = unsafe { ::std::mem::transmute(_Ignored1) }; + _Ignored1 as u64 + }); + __bindgen_bitfield_unit.set(12usize, 36u8, { + let PhysicalAddress: u64 = unsafe { ::std::mem::transmute(PhysicalAddress) }; + PhysicalAddress as u64 + }); + __bindgen_bitfield_unit.set(48usize, 16u8, { + let _Reserved0: u64 = unsafe { ::std::mem::transmute(_Reserved0) }; + _Reserved0 as u64 + }); + __bindgen_bitfield_unit + } +} +impl Default for _DIRECTORY_TABLE_BASE { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl ::std::fmt::Debug for _DIRECTORY_TABLE_BASE { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "_DIRECTORY_TABLE_BASE {{ union }}") + } +} +pub type CR3 = _DIRECTORY_TABLE_BASE; +pub type DIR_TABLE_BASE = _DIRECTORY_TABLE_BASE; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _PML4_ENTRY { + pub Bits: _PML4_ENTRY__bindgen_ty_1, + pub All: u64, +} +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct _PML4_ENTRY__bindgen_ty_1 { + pub _bitfield_align_1: [u64; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>, +} +impl _PML4_ENTRY__bindgen_ty_1 { + #[inline] + pub fn Present(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } + } + #[inline] + pub fn set_Present(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn ReadWrite(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } + } + #[inline] + pub fn set_ReadWrite(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 1u8, val as u64) + } + } + #[inline] + pub fn UserSupervisor(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } + } + #[inline] + pub fn set_UserSupervisor(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(2usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageWriteThrough(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageWriteThrough(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageCacheDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageCacheDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn Accessed(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u64) } + } + #[inline] + pub fn set_Accessed(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u64) } + } + #[inline] + pub fn set__Ignored0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(6usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Reserved0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u64) } + } + #[inline] + pub fn set__Reserved0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(7usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored1(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 4u8) as u64) } + } + #[inline] + pub fn set__Ignored1(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 4u8, val as u64) + } + } + #[inline] + pub fn PhysicalAddress(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 40u8) as u64) } + } + #[inline] + pub fn set_PhysicalAddress(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(12usize, 40u8, val as u64) + } + } + #[inline] + pub fn _Ignored2(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(52usize, 11u8) as u64) } + } + #[inline] + pub fn set__Ignored2(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(52usize, 11u8, val as u64) + } + } + #[inline] + pub fn ExecuteDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(63usize, 1u8) as u64) } + } + #[inline] + pub fn set_ExecuteDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(63usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + Present: u64, + ReadWrite: u64, + UserSupervisor: u64, + PageWriteThrough: u64, + PageCacheDisable: u64, + Accessed: u64, + _Ignored0: u64, + _Reserved0: u64, + _Ignored1: u64, + PhysicalAddress: u64, + _Ignored2: u64, + ExecuteDisable: u64, + ) -> __BindgenBitfieldUnit<[u8; 8usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let Present: u64 = unsafe { ::std::mem::transmute(Present) }; + Present as u64 + }); + __bindgen_bitfield_unit.set(1usize, 1u8, { + let ReadWrite: u64 = unsafe { ::std::mem::transmute(ReadWrite) }; + ReadWrite as u64 + }); + __bindgen_bitfield_unit.set(2usize, 1u8, { + let UserSupervisor: u64 = unsafe { ::std::mem::transmute(UserSupervisor) }; + UserSupervisor as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let PageWriteThrough: u64 = unsafe { ::std::mem::transmute(PageWriteThrough) }; + PageWriteThrough as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let PageCacheDisable: u64 = unsafe { ::std::mem::transmute(PageCacheDisable) }; + PageCacheDisable as u64 + }); + __bindgen_bitfield_unit.set(5usize, 1u8, { + let Accessed: u64 = unsafe { ::std::mem::transmute(Accessed) }; + Accessed as u64 + }); + __bindgen_bitfield_unit.set(6usize, 1u8, { + let _Ignored0: u64 = unsafe { ::std::mem::transmute(_Ignored0) }; + _Ignored0 as u64 + }); + __bindgen_bitfield_unit.set(7usize, 1u8, { + let _Reserved0: u64 = unsafe { ::std::mem::transmute(_Reserved0) }; + _Reserved0 as u64 + }); + __bindgen_bitfield_unit.set(8usize, 4u8, { + let _Ignored1: u64 = unsafe { ::std::mem::transmute(_Ignored1) }; + _Ignored1 as u64 + }); + __bindgen_bitfield_unit.set(12usize, 40u8, { + let PhysicalAddress: u64 = unsafe { ::std::mem::transmute(PhysicalAddress) }; + PhysicalAddress as u64 + }); + __bindgen_bitfield_unit.set(52usize, 11u8, { + let _Ignored2: u64 = unsafe { ::std::mem::transmute(_Ignored2) }; + _Ignored2 as u64 + }); + __bindgen_bitfield_unit.set(63usize, 1u8, { + let ExecuteDisable: u64 = unsafe { ::std::mem::transmute(ExecuteDisable) }; + ExecuteDisable as u64 + }); + __bindgen_bitfield_unit + } +} +impl Default for _PML4_ENTRY { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl ::std::fmt::Debug for _PML4_ENTRY { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "_PML4_ENTRY {{ union }}") + } +} +pub type PML4E = _PML4_ENTRY; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _PDPT_ENTRY_LARGE { + pub Bits: _PDPT_ENTRY_LARGE__bindgen_ty_1, + pub All: u64, +} +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct _PDPT_ENTRY_LARGE__bindgen_ty_1 { + pub _bitfield_align_1: [u32; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>, +} +impl _PDPT_ENTRY_LARGE__bindgen_ty_1 { + #[inline] + pub fn Present(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } + } + #[inline] + pub fn set_Present(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn ReadWrite(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } + } + #[inline] + pub fn set_ReadWrite(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 1u8, val as u64) + } + } + #[inline] + pub fn UserSupervisor(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } + } + #[inline] + pub fn set_UserSupervisor(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(2usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageWriteThrough(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageWriteThrough(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageCacheDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageCacheDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn Accessed(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u64) } + } + #[inline] + pub fn set_Accessed(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 1u8, val as u64) + } + } + #[inline] + pub fn Dirty(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u64) } + } + #[inline] + pub fn set_Dirty(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(6usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageSize(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageSize(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(7usize, 1u8, val as u64) + } + } + #[inline] + pub fn Global(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u64) } + } + #[inline] + pub fn set_Global(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(9usize, 3u8) as u64) } + } + #[inline] + pub fn set__Ignored0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(9usize, 3u8, val as u64) + } + } + #[inline] + pub fn PageAttributeTable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageAttributeTable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(12usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Reserved0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(13usize, 17u8) as u64) } + } + #[inline] + pub fn set__Reserved0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(13usize, 17u8, val as u64) + } + } + #[inline] + pub fn PhysicalAddress(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(30usize, 22u8) as u64) } + } + #[inline] + pub fn set_PhysicalAddress(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(30usize, 22u8, val as u64) + } + } + #[inline] + pub fn _Ignored1(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(52usize, 7u8) as u64) } + } + #[inline] + pub fn set__Ignored1(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(52usize, 7u8, val as u64) + } + } + #[inline] + pub fn ProtectionKey(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(59usize, 4u8) as u64) } + } + #[inline] + pub fn set_ProtectionKey(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(59usize, 4u8, val as u64) + } + } + #[inline] + pub fn ExecuteDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(63usize, 1u8) as u64) } + } + #[inline] + pub fn set_ExecuteDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(63usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + Present: u64, + ReadWrite: u64, + UserSupervisor: u64, + PageWriteThrough: u64, + PageCacheDisable: u64, + Accessed: u64, + Dirty: u64, + PageSize: u64, + Global: u64, + _Ignored0: u64, + PageAttributeTable: u64, + _Reserved0: u64, + PhysicalAddress: u64, + _Ignored1: u64, + ProtectionKey: u64, + ExecuteDisable: u64, + ) -> __BindgenBitfieldUnit<[u8; 8usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let Present: u64 = unsafe { ::std::mem::transmute(Present) }; + Present as u64 + }); + __bindgen_bitfield_unit.set(1usize, 1u8, { + let ReadWrite: u64 = unsafe { ::std::mem::transmute(ReadWrite) }; + ReadWrite as u64 + }); + __bindgen_bitfield_unit.set(2usize, 1u8, { + let UserSupervisor: u64 = unsafe { ::std::mem::transmute(UserSupervisor) }; + UserSupervisor as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let PageWriteThrough: u64 = unsafe { ::std::mem::transmute(PageWriteThrough) }; + PageWriteThrough as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let PageCacheDisable: u64 = unsafe { ::std::mem::transmute(PageCacheDisable) }; + PageCacheDisable as u64 + }); + __bindgen_bitfield_unit.set(5usize, 1u8, { + let Accessed: u64 = unsafe { ::std::mem::transmute(Accessed) }; + Accessed as u64 + }); + __bindgen_bitfield_unit.set(6usize, 1u8, { + let Dirty: u64 = unsafe { ::std::mem::transmute(Dirty) }; + Dirty as u64 + }); + __bindgen_bitfield_unit.set(7usize, 1u8, { + let PageSize: u64 = unsafe { ::std::mem::transmute(PageSize) }; + PageSize as u64 + }); + __bindgen_bitfield_unit.set(8usize, 1u8, { + let Global: u64 = unsafe { ::std::mem::transmute(Global) }; + Global as u64 + }); + __bindgen_bitfield_unit.set(9usize, 3u8, { + let _Ignored0: u64 = unsafe { ::std::mem::transmute(_Ignored0) }; + _Ignored0 as u64 + }); + __bindgen_bitfield_unit.set(12usize, 1u8, { + let PageAttributeTable: u64 = unsafe { ::std::mem::transmute(PageAttributeTable) }; + PageAttributeTable as u64 + }); + __bindgen_bitfield_unit.set(13usize, 17u8, { + let _Reserved0: u64 = unsafe { ::std::mem::transmute(_Reserved0) }; + _Reserved0 as u64 + }); + __bindgen_bitfield_unit.set(30usize, 22u8, { + let PhysicalAddress: u64 = unsafe { ::std::mem::transmute(PhysicalAddress) }; + PhysicalAddress as u64 + }); + __bindgen_bitfield_unit.set(52usize, 7u8, { + let _Ignored1: u64 = unsafe { ::std::mem::transmute(_Ignored1) }; + _Ignored1 as u64 + }); + __bindgen_bitfield_unit.set(59usize, 4u8, { + let ProtectionKey: u64 = unsafe { ::std::mem::transmute(ProtectionKey) }; + ProtectionKey as u64 + }); + __bindgen_bitfield_unit.set(63usize, 1u8, { + let ExecuteDisable: u64 = unsafe { ::std::mem::transmute(ExecuteDisable) }; + ExecuteDisable as u64 + }); + __bindgen_bitfield_unit + } +} +impl Default for _PDPT_ENTRY_LARGE { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl ::std::fmt::Debug for _PDPT_ENTRY_LARGE { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "_PDPT_ENTRY_LARGE {{ union }}") + } +} +pub type PDPTE_LARGE = _PDPT_ENTRY_LARGE; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _PDPT_ENTRY { + pub Bits: _PDPT_ENTRY__bindgen_ty_1, + pub All: u64, +} +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct _PDPT_ENTRY__bindgen_ty_1 { + pub _bitfield_align_1: [u64; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>, +} +impl _PDPT_ENTRY__bindgen_ty_1 { + #[inline] + pub fn Present(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } + } + #[inline] + pub fn set_Present(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn ReadWrite(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } + } + #[inline] + pub fn set_ReadWrite(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 1u8, val as u64) + } + } + #[inline] + pub fn UserSupervisor(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } + } + #[inline] + pub fn set_UserSupervisor(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(2usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageWriteThrough(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageWriteThrough(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageCacheDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageCacheDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn Accessed(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u64) } + } + #[inline] + pub fn set_Accessed(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u64) } + } + #[inline] + pub fn set__Ignored0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(6usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageSize(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageSize(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(7usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored1(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 4u8) as u64) } + } + #[inline] + pub fn set__Ignored1(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 4u8, val as u64) + } + } + #[inline] + pub fn PhysicalAddress(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 40u8) as u64) } + } + #[inline] + pub fn set_PhysicalAddress(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(12usize, 40u8, val as u64) + } + } + #[inline] + pub fn _Ignored2(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(52usize, 11u8) as u64) } + } + #[inline] + pub fn set__Ignored2(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(52usize, 11u8, val as u64) + } + } + #[inline] + pub fn ExecuteDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(63usize, 1u8) as u64) } + } + #[inline] + pub fn set_ExecuteDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(63usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + Present: u64, + ReadWrite: u64, + UserSupervisor: u64, + PageWriteThrough: u64, + PageCacheDisable: u64, + Accessed: u64, + _Ignored0: u64, + PageSize: u64, + _Ignored1: u64, + PhysicalAddress: u64, + _Ignored2: u64, + ExecuteDisable: u64, + ) -> __BindgenBitfieldUnit<[u8; 8usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let Present: u64 = unsafe { ::std::mem::transmute(Present) }; + Present as u64 + }); + __bindgen_bitfield_unit.set(1usize, 1u8, { + let ReadWrite: u64 = unsafe { ::std::mem::transmute(ReadWrite) }; + ReadWrite as u64 + }); + __bindgen_bitfield_unit.set(2usize, 1u8, { + let UserSupervisor: u64 = unsafe { ::std::mem::transmute(UserSupervisor) }; + UserSupervisor as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let PageWriteThrough: u64 = unsafe { ::std::mem::transmute(PageWriteThrough) }; + PageWriteThrough as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let PageCacheDisable: u64 = unsafe { ::std::mem::transmute(PageCacheDisable) }; + PageCacheDisable as u64 + }); + __bindgen_bitfield_unit.set(5usize, 1u8, { + let Accessed: u64 = unsafe { ::std::mem::transmute(Accessed) }; + Accessed as u64 + }); + __bindgen_bitfield_unit.set(6usize, 1u8, { + let _Ignored0: u64 = unsafe { ::std::mem::transmute(_Ignored0) }; + _Ignored0 as u64 + }); + __bindgen_bitfield_unit.set(7usize, 1u8, { + let PageSize: u64 = unsafe { ::std::mem::transmute(PageSize) }; + PageSize as u64 + }); + __bindgen_bitfield_unit.set(8usize, 4u8, { + let _Ignored1: u64 = unsafe { ::std::mem::transmute(_Ignored1) }; + _Ignored1 as u64 + }); + __bindgen_bitfield_unit.set(12usize, 40u8, { + let PhysicalAddress: u64 = unsafe { ::std::mem::transmute(PhysicalAddress) }; + PhysicalAddress as u64 + }); + __bindgen_bitfield_unit.set(52usize, 11u8, { + let _Ignored2: u64 = unsafe { ::std::mem::transmute(_Ignored2) }; + _Ignored2 as u64 + }); + __bindgen_bitfield_unit.set(63usize, 1u8, { + let ExecuteDisable: u64 = unsafe { ::std::mem::transmute(ExecuteDisable) }; + ExecuteDisable as u64 + }); + __bindgen_bitfield_unit + } +} +impl Default for _PDPT_ENTRY { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl ::std::fmt::Debug for _PDPT_ENTRY { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "_PDPT_ENTRY {{ union }}") + } +} +pub type PDPTE = _PDPT_ENTRY; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _PD_ENTRY_LARGE { + pub Bits: _PD_ENTRY_LARGE__bindgen_ty_1, + pub All: u64, +} +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct _PD_ENTRY_LARGE__bindgen_ty_1 { + pub _bitfield_align_1: [u32; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>, +} +impl _PD_ENTRY_LARGE__bindgen_ty_1 { + #[inline] + pub fn Present(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } + } + #[inline] + pub fn set_Present(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn ReadWrite(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } + } + #[inline] + pub fn set_ReadWrite(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 1u8, val as u64) + } + } + #[inline] + pub fn UserSupervisor(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } + } + #[inline] + pub fn set_UserSupervisor(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(2usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageWriteThrough(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageWriteThrough(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageCacheDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageCacheDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn Accessed(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u64) } + } + #[inline] + pub fn set_Accessed(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 1u8, val as u64) + } + } + #[inline] + pub fn Dirty(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u64) } + } + #[inline] + pub fn set_Dirty(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(6usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageSize(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageSize(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(7usize, 1u8, val as u64) + } + } + #[inline] + pub fn Global(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u64) } + } + #[inline] + pub fn set_Global(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(9usize, 3u8) as u64) } + } + #[inline] + pub fn set__Ignored0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(9usize, 3u8, val as u64) + } + } + #[inline] + pub fn PageAttributeTalbe(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageAttributeTalbe(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(12usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Reserved0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(13usize, 8u8) as u64) } + } + #[inline] + pub fn set__Reserved0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(13usize, 8u8, val as u64) + } + } + #[inline] + pub fn PhysicalAddress(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(21usize, 29u8) as u64) } + } + #[inline] + pub fn set_PhysicalAddress(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(21usize, 29u8, val as u64) + } + } + #[inline] + pub fn _Reserved1(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(50usize, 2u8) as u64) } + } + #[inline] + pub fn set__Reserved1(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(50usize, 2u8, val as u64) + } + } + #[inline] + pub fn _Ignored1(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(52usize, 7u8) as u64) } + } + #[inline] + pub fn set__Ignored1(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(52usize, 7u8, val as u64) + } + } + #[inline] + pub fn ProtectionKey(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(59usize, 4u8) as u64) } + } + #[inline] + pub fn set_ProtectionKey(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(59usize, 4u8, val as u64) + } + } + #[inline] + pub fn ExecuteDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(63usize, 1u8) as u64) } + } + #[inline] + pub fn set_ExecuteDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(63usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + Present: u64, + ReadWrite: u64, + UserSupervisor: u64, + PageWriteThrough: u64, + PageCacheDisable: u64, + Accessed: u64, + Dirty: u64, + PageSize: u64, + Global: u64, + _Ignored0: u64, + PageAttributeTalbe: u64, + _Reserved0: u64, + PhysicalAddress: u64, + _Reserved1: u64, + _Ignored1: u64, + ProtectionKey: u64, + ExecuteDisable: u64, + ) -> __BindgenBitfieldUnit<[u8; 8usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let Present: u64 = unsafe { ::std::mem::transmute(Present) }; + Present as u64 + }); + __bindgen_bitfield_unit.set(1usize, 1u8, { + let ReadWrite: u64 = unsafe { ::std::mem::transmute(ReadWrite) }; + ReadWrite as u64 + }); + __bindgen_bitfield_unit.set(2usize, 1u8, { + let UserSupervisor: u64 = unsafe { ::std::mem::transmute(UserSupervisor) }; + UserSupervisor as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let PageWriteThrough: u64 = unsafe { ::std::mem::transmute(PageWriteThrough) }; + PageWriteThrough as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let PageCacheDisable: u64 = unsafe { ::std::mem::transmute(PageCacheDisable) }; + PageCacheDisable as u64 + }); + __bindgen_bitfield_unit.set(5usize, 1u8, { + let Accessed: u64 = unsafe { ::std::mem::transmute(Accessed) }; + Accessed as u64 + }); + __bindgen_bitfield_unit.set(6usize, 1u8, { + let Dirty: u64 = unsafe { ::std::mem::transmute(Dirty) }; + Dirty as u64 + }); + __bindgen_bitfield_unit.set(7usize, 1u8, { + let PageSize: u64 = unsafe { ::std::mem::transmute(PageSize) }; + PageSize as u64 + }); + __bindgen_bitfield_unit.set(8usize, 1u8, { + let Global: u64 = unsafe { ::std::mem::transmute(Global) }; + Global as u64 + }); + __bindgen_bitfield_unit.set(9usize, 3u8, { + let _Ignored0: u64 = unsafe { ::std::mem::transmute(_Ignored0) }; + _Ignored0 as u64 + }); + __bindgen_bitfield_unit.set(12usize, 1u8, { + let PageAttributeTalbe: u64 = unsafe { ::std::mem::transmute(PageAttributeTalbe) }; + PageAttributeTalbe as u64 + }); + __bindgen_bitfield_unit.set(13usize, 8u8, { + let _Reserved0: u64 = unsafe { ::std::mem::transmute(_Reserved0) }; + _Reserved0 as u64 + }); + __bindgen_bitfield_unit.set(21usize, 29u8, { + let PhysicalAddress: u64 = unsafe { ::std::mem::transmute(PhysicalAddress) }; + PhysicalAddress as u64 + }); + __bindgen_bitfield_unit.set(50usize, 2u8, { + let _Reserved1: u64 = unsafe { ::std::mem::transmute(_Reserved1) }; + _Reserved1 as u64 + }); + __bindgen_bitfield_unit.set(52usize, 7u8, { + let _Ignored1: u64 = unsafe { ::std::mem::transmute(_Ignored1) }; + _Ignored1 as u64 + }); + __bindgen_bitfield_unit.set(59usize, 4u8, { + let ProtectionKey: u64 = unsafe { ::std::mem::transmute(ProtectionKey) }; + ProtectionKey as u64 + }); + __bindgen_bitfield_unit.set(63usize, 1u8, { + let ExecuteDisable: u64 = unsafe { ::std::mem::transmute(ExecuteDisable) }; + ExecuteDisable as u64 + }); + __bindgen_bitfield_unit + } +} +impl Default for _PD_ENTRY_LARGE { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl ::std::fmt::Debug for _PD_ENTRY_LARGE { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "_PD_ENTRY_LARGE {{ union }}") + } +} +pub type PDE_LARGE = _PD_ENTRY_LARGE; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _PD_ENTRY { + pub Bits: _PD_ENTRY__bindgen_ty_1, + pub All: u64, +} +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct _PD_ENTRY__bindgen_ty_1 { + pub _bitfield_align_1: [u64; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>, +} +impl _PD_ENTRY__bindgen_ty_1 { + #[inline] + pub fn Present(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } + } + #[inline] + pub fn set_Present(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn ReadWrite(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } + } + #[inline] + pub fn set_ReadWrite(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 1u8, val as u64) + } + } + #[inline] + pub fn UserSupervisor(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } + } + #[inline] + pub fn set_UserSupervisor(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(2usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageWriteThrough(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageWriteThrough(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageCacheDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageCacheDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn Accessed(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u64) } + } + #[inline] + pub fn set_Accessed(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u64) } + } + #[inline] + pub fn set__Ignored0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(6usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageSize(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageSize(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(7usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored1(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 4u8) as u64) } + } + #[inline] + pub fn set__Ignored1(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 4u8, val as u64) + } + } + #[inline] + pub fn PhysicalAddress(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 38u8) as u64) } + } + #[inline] + pub fn set_PhysicalAddress(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(12usize, 38u8, val as u64) + } + } + #[inline] + pub fn _Reserved0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(50usize, 2u8) as u64) } + } + #[inline] + pub fn set__Reserved0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(50usize, 2u8, val as u64) + } + } + #[inline] + pub fn _Ignored2(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(52usize, 11u8) as u64) } + } + #[inline] + pub fn set__Ignored2(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(52usize, 11u8, val as u64) + } + } + #[inline] + pub fn ExecuteDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(63usize, 1u8) as u64) } + } + #[inline] + pub fn set_ExecuteDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(63usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + Present: u64, + ReadWrite: u64, + UserSupervisor: u64, + PageWriteThrough: u64, + PageCacheDisable: u64, + Accessed: u64, + _Ignored0: u64, + PageSize: u64, + _Ignored1: u64, + PhysicalAddress: u64, + _Reserved0: u64, + _Ignored2: u64, + ExecuteDisable: u64, + ) -> __BindgenBitfieldUnit<[u8; 8usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let Present: u64 = unsafe { ::std::mem::transmute(Present) }; + Present as u64 + }); + __bindgen_bitfield_unit.set(1usize, 1u8, { + let ReadWrite: u64 = unsafe { ::std::mem::transmute(ReadWrite) }; + ReadWrite as u64 + }); + __bindgen_bitfield_unit.set(2usize, 1u8, { + let UserSupervisor: u64 = unsafe { ::std::mem::transmute(UserSupervisor) }; + UserSupervisor as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let PageWriteThrough: u64 = unsafe { ::std::mem::transmute(PageWriteThrough) }; + PageWriteThrough as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let PageCacheDisable: u64 = unsafe { ::std::mem::transmute(PageCacheDisable) }; + PageCacheDisable as u64 + }); + __bindgen_bitfield_unit.set(5usize, 1u8, { + let Accessed: u64 = unsafe { ::std::mem::transmute(Accessed) }; + Accessed as u64 + }); + __bindgen_bitfield_unit.set(6usize, 1u8, { + let _Ignored0: u64 = unsafe { ::std::mem::transmute(_Ignored0) }; + _Ignored0 as u64 + }); + __bindgen_bitfield_unit.set(7usize, 1u8, { + let PageSize: u64 = unsafe { ::std::mem::transmute(PageSize) }; + PageSize as u64 + }); + __bindgen_bitfield_unit.set(8usize, 4u8, { + let _Ignored1: u64 = unsafe { ::std::mem::transmute(_Ignored1) }; + _Ignored1 as u64 + }); + __bindgen_bitfield_unit.set(12usize, 38u8, { + let PhysicalAddress: u64 = unsafe { ::std::mem::transmute(PhysicalAddress) }; + PhysicalAddress as u64 + }); + __bindgen_bitfield_unit.set(50usize, 2u8, { + let _Reserved0: u64 = unsafe { ::std::mem::transmute(_Reserved0) }; + _Reserved0 as u64 + }); + __bindgen_bitfield_unit.set(52usize, 11u8, { + let _Ignored2: u64 = unsafe { ::std::mem::transmute(_Ignored2) }; + _Ignored2 as u64 + }); + __bindgen_bitfield_unit.set(63usize, 1u8, { + let ExecuteDisable: u64 = unsafe { ::std::mem::transmute(ExecuteDisable) }; + ExecuteDisable as u64 + }); + __bindgen_bitfield_unit + } +} +impl Default for _PD_ENTRY { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl ::std::fmt::Debug for _PD_ENTRY { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "_PD_ENTRY {{ union }}") + } +} +pub type PDE = _PD_ENTRY; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _PT_ENTRY { + pub Bits: _PT_ENTRY__bindgen_ty_1, + pub All: u64, +} +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct _PT_ENTRY__bindgen_ty_1 { + pub _bitfield_align_1: [u64; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>, +} +impl _PT_ENTRY__bindgen_ty_1 { + #[inline] + pub fn Present(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } + } + #[inline] + pub fn set_Present(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn ReadWrite(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } + } + #[inline] + pub fn set_ReadWrite(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 1u8, val as u64) + } + } + #[inline] + pub fn UserSupervisor(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } + } + #[inline] + pub fn set_UserSupervisor(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(2usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageWriteThrough(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageWriteThrough(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageCacheDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageCacheDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn Accessed(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u64) } + } + #[inline] + pub fn set_Accessed(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 1u8, val as u64) + } + } + #[inline] + pub fn Dirty(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u64) } + } + #[inline] + pub fn set_Dirty(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(6usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageAttributeTable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u64) } + } + #[inline] + pub fn set_PageAttributeTable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(7usize, 1u8, val as u64) + } + } + #[inline] + pub fn Global(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u64) } + } + #[inline] + pub fn set_Global(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 1u8, val as u64) + } + } + #[inline] + pub fn _Ignored0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(9usize, 3u8) as u64) } + } + #[inline] + pub fn set__Ignored0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(9usize, 3u8, val as u64) + } + } + #[inline] + pub fn PhysicalAddress(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 38u8) as u64) } + } + #[inline] + pub fn set_PhysicalAddress(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(12usize, 38u8, val as u64) + } + } + #[inline] + pub fn _Reserved0(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(50usize, 2u8) as u64) } + } + #[inline] + pub fn set__Reserved0(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(50usize, 2u8, val as u64) + } + } + #[inline] + pub fn _Ignored1(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(52usize, 7u8) as u64) } + } + #[inline] + pub fn set__Ignored1(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(52usize, 7u8, val as u64) + } + } + #[inline] + pub fn ProtectionKey(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(59usize, 4u8) as u64) } + } + #[inline] + pub fn set_ProtectionKey(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(59usize, 4u8, val as u64) + } + } + #[inline] + pub fn ExecuteDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(63usize, 1u8) as u64) } + } + #[inline] + pub fn set_ExecuteDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(63usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + Present: u64, + ReadWrite: u64, + UserSupervisor: u64, + PageWriteThrough: u64, + PageCacheDisable: u64, + Accessed: u64, + Dirty: u64, + PageAttributeTable: u64, + Global: u64, + _Ignored0: u64, + PhysicalAddress: u64, + _Reserved0: u64, + _Ignored1: u64, + ProtectionKey: u64, + ExecuteDisable: u64, + ) -> __BindgenBitfieldUnit<[u8; 8usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let Present: u64 = unsafe { ::std::mem::transmute(Present) }; + Present as u64 + }); + __bindgen_bitfield_unit.set(1usize, 1u8, { + let ReadWrite: u64 = unsafe { ::std::mem::transmute(ReadWrite) }; + ReadWrite as u64 + }); + __bindgen_bitfield_unit.set(2usize, 1u8, { + let UserSupervisor: u64 = unsafe { ::std::mem::transmute(UserSupervisor) }; + UserSupervisor as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let PageWriteThrough: u64 = unsafe { ::std::mem::transmute(PageWriteThrough) }; + PageWriteThrough as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let PageCacheDisable: u64 = unsafe { ::std::mem::transmute(PageCacheDisable) }; + PageCacheDisable as u64 + }); + __bindgen_bitfield_unit.set(5usize, 1u8, { + let Accessed: u64 = unsafe { ::std::mem::transmute(Accessed) }; + Accessed as u64 + }); + __bindgen_bitfield_unit.set(6usize, 1u8, { + let Dirty: u64 = unsafe { ::std::mem::transmute(Dirty) }; + Dirty as u64 + }); + __bindgen_bitfield_unit.set(7usize, 1u8, { + let PageAttributeTable: u64 = unsafe { ::std::mem::transmute(PageAttributeTable) }; + PageAttributeTable as u64 + }); + __bindgen_bitfield_unit.set(8usize, 1u8, { + let Global: u64 = unsafe { ::std::mem::transmute(Global) }; + Global as u64 + }); + __bindgen_bitfield_unit.set(9usize, 3u8, { + let _Ignored0: u64 = unsafe { ::std::mem::transmute(_Ignored0) }; + _Ignored0 as u64 + }); + __bindgen_bitfield_unit.set(12usize, 38u8, { + let PhysicalAddress: u64 = unsafe { ::std::mem::transmute(PhysicalAddress) }; + PhysicalAddress as u64 + }); + __bindgen_bitfield_unit.set(50usize, 2u8, { + let _Reserved0: u64 = unsafe { ::std::mem::transmute(_Reserved0) }; + _Reserved0 as u64 + }); + __bindgen_bitfield_unit.set(52usize, 7u8, { + let _Ignored1: u64 = unsafe { ::std::mem::transmute(_Ignored1) }; + _Ignored1 as u64 + }); + __bindgen_bitfield_unit.set(59usize, 4u8, { + let ProtectionKey: u64 = unsafe { ::std::mem::transmute(ProtectionKey) }; + ProtectionKey as u64 + }); + __bindgen_bitfield_unit.set(63usize, 1u8, { + let ExecuteDisable: u64 = unsafe { ::std::mem::transmute(ExecuteDisable) }; + ExecuteDisable as u64 + }); + __bindgen_bitfield_unit + } +} +impl Default for _PT_ENTRY { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl ::std::fmt::Debug for _PT_ENTRY { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "_PT_ENTRY {{ union }}") + } +} +pub type PTE = _PT_ENTRY; +#[repr(C)] +#[derive(Copy, Clone)] +pub union _MMPTE_HARDWARE { + pub Bits: _MMPTE_HARDWARE__bindgen_ty_1, + pub All: u64, +} +#[repr(C)] +#[repr(align(8))] +#[derive(Debug, Default, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] +pub struct _MMPTE_HARDWARE__bindgen_ty_1 { + pub _bitfield_align_1: [u64; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize]>, +} +impl _MMPTE_HARDWARE__bindgen_ty_1 { + #[inline] + pub fn Valid(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } + } + #[inline] + pub fn set_Valid(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 1u8, val as u64) + } + } + #[inline] + pub fn Dirty1(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } + } + #[inline] + pub fn set_Dirty1(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(1usize, 1u8, val as u64) + } + } + #[inline] + pub fn Owner(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } + } + #[inline] + pub fn set_Owner(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(2usize, 1u8, val as u64) + } + } + #[inline] + pub fn WriteThrough(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } + } + #[inline] + pub fn set_WriteThrough(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(3usize, 1u8, val as u64) + } + } + #[inline] + pub fn CacheDisable(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } + } + #[inline] + pub fn set_CacheDisable(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(4usize, 1u8, val as u64) + } + } + #[inline] + pub fn Accessed(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u64) } + } + #[inline] + pub fn set_Accessed(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(5usize, 1u8, val as u64) + } + } + #[inline] + pub fn Dirty(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u64) } + } + #[inline] + pub fn set_Dirty(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(6usize, 1u8, val as u64) + } + } + #[inline] + pub fn LargePage(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u64) } + } + #[inline] + pub fn set_LargePage(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(7usize, 1u8, val as u64) + } + } + #[inline] + pub fn Global(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u64) } + } + #[inline] + pub fn set_Global(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(8usize, 1u8, val as u64) + } + } + #[inline] + pub fn CopyOnWrite(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(9usize, 1u8) as u64) } + } + #[inline] + pub fn set_CopyOnWrite(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(9usize, 1u8, val as u64) + } + } + #[inline] + pub fn Unused(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(10usize, 1u8) as u64) } + } + #[inline] + pub fn set_Unused(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(10usize, 1u8, val as u64) + } + } + #[inline] + pub fn Write(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(11usize, 1u8) as u64) } + } + #[inline] + pub fn set_Write(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(11usize, 1u8, val as u64) + } + } + #[inline] + pub fn PageFrameNumber(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 36u8) as u64) } + } + #[inline] + pub fn set_PageFrameNumber(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(12usize, 36u8, val as u64) + } + } + #[inline] + pub fn ReservedForHardware(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(48usize, 4u8) as u64) } + } + #[inline] + pub fn set_ReservedForHardware(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(48usize, 4u8, val as u64) + } + } + #[inline] + pub fn ReservedForSoftware(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(52usize, 4u8) as u64) } + } + #[inline] + pub fn set_ReservedForSoftware(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(52usize, 4u8, val as u64) + } + } + #[inline] + pub fn WsleAge(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(56usize, 4u8) as u64) } + } + #[inline] + pub fn set_WsleAge(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(56usize, 4u8, val as u64) + } + } + #[inline] + pub fn WsleProtection(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(60usize, 3u8) as u64) } + } + #[inline] + pub fn set_WsleProtection(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(60usize, 3u8, val as u64) + } + } + #[inline] + pub fn NoExecute(&self) -> u64 { + unsafe { ::std::mem::transmute(self._bitfield_1.get(63usize, 1u8) as u64) } + } + #[inline] + pub fn set_NoExecute(&mut self, val: u64) { + unsafe { + let val: u64 = ::std::mem::transmute(val); + self._bitfield_1.set(63usize, 1u8, val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + Valid: u64, + Dirty1: u64, + Owner: u64, + WriteThrough: u64, + CacheDisable: u64, + Accessed: u64, + Dirty: u64, + LargePage: u64, + Global: u64, + CopyOnWrite: u64, + Unused: u64, + Write: u64, + PageFrameNumber: u64, + ReservedForHardware: u64, + ReservedForSoftware: u64, + WsleAge: u64, + WsleProtection: u64, + NoExecute: u64, + ) -> __BindgenBitfieldUnit<[u8; 8usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 1u8, { + let Valid: u64 = unsafe { ::std::mem::transmute(Valid) }; + Valid as u64 + }); + __bindgen_bitfield_unit.set(1usize, 1u8, { + let Dirty1: u64 = unsafe { ::std::mem::transmute(Dirty1) }; + Dirty1 as u64 + }); + __bindgen_bitfield_unit.set(2usize, 1u8, { + let Owner: u64 = unsafe { ::std::mem::transmute(Owner) }; + Owner as u64 + }); + __bindgen_bitfield_unit.set(3usize, 1u8, { + let WriteThrough: u64 = unsafe { ::std::mem::transmute(WriteThrough) }; + WriteThrough as u64 + }); + __bindgen_bitfield_unit.set(4usize, 1u8, { + let CacheDisable: u64 = unsafe { ::std::mem::transmute(CacheDisable) }; + CacheDisable as u64 + }); + __bindgen_bitfield_unit.set(5usize, 1u8, { + let Accessed: u64 = unsafe { ::std::mem::transmute(Accessed) }; + Accessed as u64 + }); + __bindgen_bitfield_unit.set(6usize, 1u8, { + let Dirty: u64 = unsafe { ::std::mem::transmute(Dirty) }; + Dirty as u64 + }); + __bindgen_bitfield_unit.set(7usize, 1u8, { + let LargePage: u64 = unsafe { ::std::mem::transmute(LargePage) }; + LargePage as u64 + }); + __bindgen_bitfield_unit.set(8usize, 1u8, { + let Global: u64 = unsafe { ::std::mem::transmute(Global) }; + Global as u64 + }); + __bindgen_bitfield_unit.set(9usize, 1u8, { + let CopyOnWrite: u64 = unsafe { ::std::mem::transmute(CopyOnWrite) }; + CopyOnWrite as u64 + }); + __bindgen_bitfield_unit.set(10usize, 1u8, { + let Unused: u64 = unsafe { ::std::mem::transmute(Unused) }; + Unused as u64 + }); + __bindgen_bitfield_unit.set(11usize, 1u8, { + let Write: u64 = unsafe { ::std::mem::transmute(Write) }; + Write as u64 + }); + __bindgen_bitfield_unit.set(12usize, 36u8, { + let PageFrameNumber: u64 = unsafe { ::std::mem::transmute(PageFrameNumber) }; + PageFrameNumber as u64 + }); + __bindgen_bitfield_unit.set(48usize, 4u8, { + let ReservedForHardware: u64 = unsafe { ::std::mem::transmute(ReservedForHardware) }; + ReservedForHardware as u64 + }); + __bindgen_bitfield_unit.set(52usize, 4u8, { + let ReservedForSoftware: u64 = unsafe { ::std::mem::transmute(ReservedForSoftware) }; + ReservedForSoftware as u64 + }); + __bindgen_bitfield_unit.set(56usize, 4u8, { + let WsleAge: u64 = unsafe { ::std::mem::transmute(WsleAge) }; + WsleAge as u64 + }); + __bindgen_bitfield_unit.set(60usize, 3u8, { + let WsleProtection: u64 = unsafe { ::std::mem::transmute(WsleProtection) }; + WsleProtection as u64 + }); + __bindgen_bitfield_unit.set(63usize, 1u8, { + let NoExecute: u64 = unsafe { ::std::mem::transmute(NoExecute) }; + NoExecute as u64 + }); + __bindgen_bitfield_unit + } +} +impl Default for _MMPTE_HARDWARE { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} +impl ::std::fmt::Debug for _MMPTE_HARDWARE { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "_MMPTE_HARDWARE {{ union }}") + } +} +pub type MMPTE_HARDWARE = _MMPTE_HARDWARE; diff --git a/src/os/windows/pdb.rs b/src/os/windows/pdb.rs new file mode 100644 index 00000000..2b6df113 --- /dev/null +++ b/src/os/windows/pdb.rs @@ -0,0 +1,130 @@ +use anyhow::{ensure, Result}; +use simics::ConfObject; + +use crate::os::windows::util::{ + read_nul_terminated_string, read_nul_terminated_string_dtb, read_virtual, read_virtual_dtb, +}; + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct Guid { + pub data0: u32, + pub data1: u16, + pub data2: u16, + pub data3: u64, +} + +#[derive(Debug, Clone)] +pub struct CvInfoPdb70 { + pub cv_signature: u32, + pub signature: Guid, + pub age: u32, + pub file_name: String, +} + +impl CvInfoPdb70 { + pub fn new(processor: *mut ConfObject, address: u64) -> Result { + let cv_signature = read_virtual::(processor, address)?; + let signature = + read_virtual::(processor, address + std::mem::size_of::() as u64)?; + let age = read_virtual::( + processor, + address + std::mem::size_of::() as u64 + std::mem::size_of::() as u64, + )?; + let file_name = read_nul_terminated_string( + processor, + address + + std::mem::size_of::() as u64 + + std::mem::size_of::() as u64 + + std::mem::size_of::() as u64, + )?; + + ensure!(cv_signature == 0x53445352, "Invalid CV signature"); + + Ok(Self { + cv_signature, + signature, + age, + file_name, + }) + } + + pub fn new_dtb( + processor: *mut ConfObject, + directory_table_base: u64, + address: u64, + ) -> Result { + let cv_signature = read_virtual_dtb::(processor, directory_table_base, address)?; + let signature = read_virtual_dtb::( + processor, + directory_table_base, + address + std::mem::size_of::() as u64, + )?; + let age = read_virtual_dtb::( + processor, + directory_table_base, + address + std::mem::size_of::() as u64 + std::mem::size_of::() as u64, + )?; + let file_name = read_nul_terminated_string_dtb( + processor, + directory_table_base, + address + + std::mem::size_of::() as u64 + + std::mem::size_of::() as u64 + + std::mem::size_of::() as u64, + )?; + + ensure!(cv_signature == 0x53445352, "Invalid CV signature"); + + Ok(Self { + cv_signature, + signature, + age, + file_name, + }) + } + + pub fn guid(&self) -> String { + // Reverse the order of data3 + let data3 = u64::from_be_bytes(self.signature.data3.to_le_bytes()); + + format!( + "{:08X}{:04X}{:04X}{:016X}{:01X}", + self.signature.data0, self.signature.data1, self.signature.data2, data3, self.age + ) + } + + pub fn file_name(&self) -> &str { + &self.file_name + } +} + +#[derive(Debug, Clone)] +pub struct Export { + pub name: Option, + pub offset: Option, + pub rva: usize, + pub size: usize, +} + +impl From> for Export { + fn from(export: goblin::pe::export::Export) -> Self { + Self { + name: export.name.map(|s| s.to_string()), + offset: export.offset, + rva: export.rva, + size: export.size, + } + } +} + +impl From<&goblin::pe::export::Export<'_>> for Export { + fn from(export: &goblin::pe::export::Export) -> Self { + Self { + name: export.name.map(|s| s.to_string()), + offset: export.offset, + rva: export.rva, + size: export.size, + } + } +} diff --git a/src/os/windows/structs.rs b/src/os/windows/structs.rs new file mode 100644 index 00000000..cfed87bd --- /dev/null +++ b/src/os/windows/structs.rs @@ -0,0 +1,3076 @@ +use std::{ + cmp::max, + collections::{HashMap, HashSet}, + path::{Path, PathBuf}, +}; + +use anyhow::{anyhow, bail, ensure, Result}; +use raw_cstr::AsRawCstr; +use simics::{debug, get_attribute, get_interface, ConfObject, IntRegisterInterface}; +use vergilius::bindings::*; +use windows::Win32::{Foundation::UNICODE_STRING, System::Kernel::LIST_ENTRY}; + +use crate::os::windows::{debug_info::DebugInfo, util::read_virtual}; + +use super::{ + debug_info::ProcessModule, + util::{read_unicode_string, read_unicode_string_dtb, read_virtual_dtb}, +}; + +pub enum WindowsKpcr { + Windows10_0_10240_16384 { + kpcr: windows_10_0_10240_16384_x64::_KPCR, + }, + Windows10_0_10586_0 { + kpcr: windows_10_0_10586_0_x64::_KPCR, + }, + Windows10_0_14393_0 { + kpcr: windows_10_0_14393_0_x64::_KPCR, + }, + Windows10_0_15063_0 { + kpcr: windows_10_0_15063_0_x64::_KPCR, + }, + Windows10_0_16299_15 { + kpcr: windows_10_0_16299_15_x64::_KPCR, + }, + Windows10_0_17134_1 { + kpcr: windows_10_0_17134_1_x64::_KPCR, + }, + Windows10_0_17763_107 { + kpcr: windows_10_0_17763_107_x64::_KPCR, + }, + Windows10_0_18362_418 { + kpcr: windows_10_0_18362_418_x64::_KPCR, + }, + Windows10_0_19041_1288 { + kpcr: windows_10_0_19041_1288_x64::_KPCR, + }, + Windows10_0_19045_2965 { + kpcr: windows_10_0_19045_2965_x64::_KPCR, + }, + Windows10_0_22000_194 { + kpcr: windows_10_0_22000_194_x64::_KPCR, + }, + Windows10_0_22621_382 { + kpcr: windows_10_0_22621_382_x64::_KPCR, + }, + Windows10_0_22631_2428 { + kpcr: windows_10_0_22631_2428_x64::_KPCR, + }, +} + +impl WindowsKpcr { + pub fn new(processor: *mut ConfObject, maj: u32, min: u32, build: u32) -> Result { + let mut int_register = get_interface::(processor)?; + let ia32_kernel_gs_base_nr = + int_register.get_number("ia32_kernel_gs_base".as_raw_cstr()?)?; + let ia32_gs_base_nr = int_register.get_number("ia32_gs_base".as_raw_cstr()?)?; + let ia32_kernel_gs_base = int_register.read(ia32_kernel_gs_base_nr)?; + let ia32_gs_base = int_register.read(ia32_gs_base_nr)?; + let sim_idtr_base: u64 = get_attribute(processor, "idtr_base")?.try_into()?; + debug!("Got SIM IDTR Base {:#x}", sim_idtr_base); + + let kpcr_address = max(ia32_gs_base, ia32_kernel_gs_base); + debug!("Got KPCR address {:#x}", kpcr_address); + + debug!("Initializing KPCR for Windows {}.{}.{}", maj, min, build); + + match (maj, min, build) { + (10, 0, 10240) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_10240_16384 { kpcr }) + } + (10, 0, 10586) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_10586_0 { kpcr }) + } + (10, 0, 14393) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_14393_0 { kpcr }) + } + (10, 0, 15063) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_15063_0 { kpcr }) + } + (10, 0, 16299) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_16299_15 { kpcr }) + } + (10, 0, 17134) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_17134_1 { kpcr }) + } + (10, 0, 17763) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_17763_107 { kpcr }) + } + (10, 0, 18362) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_18362_418 { kpcr }) + } + (10, 0, 19041) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_19041_1288 { kpcr }) + } + (10, 0, 19045) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_19045_2965 { kpcr }) + } + (10, 0, 22000) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_22000_194 { kpcr }) + } + (10, 0, 22621) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_22621_382 { kpcr }) + } + (10, 0, 22631) => { + let kpcr = + read_virtual::(processor, kpcr_address)?; + ensure!( + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.Self_ + == kpcr_address as *mut _, + "Invalid KPCR: Self != KPCR address" + ); + ensure!( + kpcr.IdtBase == sim_idtr_base as *mut _, + "Invalid KPCR: IdtBase != IDTR base" + ); + + Ok(WindowsKpcr::Windows10_0_22631_2428 { kpcr }) + } + (_, _, _) => bail!("Unsupported Windows version"), + } + } + + pub fn kpcrb_address(&self) -> u64 { + match self { + WindowsKpcr::Windows10_0_10240_16384 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_10586_0 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_14393_0 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_15063_0 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_16299_15 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_17134_1 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_17763_107 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_18362_418 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_19041_1288 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_19045_2965 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_22000_194 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_22621_382 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + WindowsKpcr::Windows10_0_22631_2428 { kpcr } => { + unsafe { kpcr.__bindgen_anon_1.__bindgen_anon_1 }.CurrentPrcb as u64 + } + } + } +} + +pub enum WindowsKprcb { + Windows10_0_10240_16384 { + kprcb: windows_10_0_10240_16384_x64::_KPRCB, + }, + Windows10_0_10586_0 { + kprcb: windows_10_0_10586_0_x64::_KPRCB, + }, + Windows10_0_14393_0 { + kprcb: windows_10_0_14393_0_x64::_KPRCB, + }, + Windows10_0_15063_0 { + kprcb: windows_10_0_15063_0_x64::_KPRCB, + }, + Windows10_0_16299_15 { + kprcb: windows_10_0_16299_15_x64::_KPRCB, + }, + Windows10_0_17134_1 { + kprcb: windows_10_0_17134_1_x64::_KPRCB, + }, + Windows10_0_17763_107 { + kprcb: windows_10_0_17763_107_x64::_KPRCB, + }, + Windows10_0_18362_418 { + kprcb: windows_10_0_18362_418_x64::_KPRCB, + }, + Windows10_0_19041_1288 { + kprcb: windows_10_0_19041_1288_x64::_KPRCB, + }, + Windows10_0_19045_2965 { + kprcb: windows_10_0_19045_2965_x64::_KPRCB, + }, + Windows10_0_22000_194 { + kprcb: windows_10_0_22000_194_x64::_KPRCB, + }, + Windows10_0_22621_382 { + kprcb: windows_10_0_22621_382_x64::_KPRCB, + }, + Windows10_0_22631_2428 { + kprcb: windows_10_0_22631_2428_x64::_KPRCB, + }, +} + +impl WindowsKprcb { + pub fn new( + processor: *mut ConfObject, + maj: u32, + min: u32, + build: u32, + kpcrb_address: u64, + ) -> Result { + debug!("Initializing KPRCB for Windows {}.{}.{}", maj, min, build); + + match (maj, min, build) { + (10, 0, 10240) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_10240_16384 { kprcb }) + } + (10, 0, 10586) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_10586_0 { kprcb }) + } + (10, 0, 14393) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_14393_0 { kprcb }) + } + (10, 0, 15063) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_15063_0 { kprcb }) + } + (10, 0, 16299) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_16299_15 { kprcb }) + } + (10, 0, 17134) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_17134_1 { kprcb }) + } + (10, 0, 17763) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_17763_107 { kprcb }) + } + (10, 0, 18362) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_18362_418 { kprcb }) + } + (10, 0, 19041) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_19041_1288 { kprcb }) + } + (10, 0, 19045) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_19045_2965 { kprcb }) + } + (10, 0, 22000) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_22000_194 { kprcb }) + } + (10, 0, 22621) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_22621_382 { kprcb }) + } + (10, 0, 22631) => { + let kprcb = + read_virtual::(processor, kpcrb_address)?; + + Ok(WindowsKprcb::Windows10_0_22631_2428 { kprcb }) + } + (_, _, _) => bail!("Unsupported Windows version"), + } + } + + pub fn current_thread(&self) -> u64 { + match self { + WindowsKprcb::Windows10_0_10240_16384 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_10586_0 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_14393_0 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_15063_0 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_16299_15 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_17134_1 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_17763_107 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_18362_418 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_19041_1288 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_19045_2965 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_22000_194 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_22621_382 { kprcb } => kprcb.CurrentThread as u64, + WindowsKprcb::Windows10_0_22631_2428 { kprcb } => kprcb.CurrentThread as u64, + } + } +} + +pub enum WindowsLdrDataTableEntry { + Windows10_0_10240_16384 { + ldr_data_table_entry: windows_10_0_10240_16384_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_10586_0 { + ldr_data_table_entry: windows_10_0_10586_0_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_14393_0 { + ldr_data_table_entry: windows_10_0_14393_0_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_15063_0 { + ldr_data_table_entry: windows_10_0_15063_0_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_16299_15 { + ldr_data_table_entry: windows_10_0_16299_15_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_17134_1 { + ldr_data_table_entry: windows_10_0_17134_1_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_17763_107 { + ldr_data_table_entry: windows_10_0_17763_107_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_18362_418 { + ldr_data_table_entry: windows_10_0_18362_418_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_19041_1288 { + ldr_data_table_entry: windows_10_0_19041_1288_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_19045_2965 { + ldr_data_table_entry: windows_10_0_19045_2965_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_22000_194 { + ldr_data_table_entry: windows_10_0_22000_194_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_22621_382 { + ldr_data_table_entry: windows_10_0_22621_382_x64::_LDR_DATA_TABLE_ENTRY, + }, + Windows10_0_22631_2428 { + ldr_data_table_entry: windows_10_0_22631_2428_x64::_LDR_DATA_TABLE_ENTRY, + }, +} + +impl WindowsLdrDataTableEntry { + pub fn new( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + ldr_data_table_entry_address: u64, + ) -> Result { + match (major, minor, build) { + (10, 0, 10240) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_10240_16384_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + }) + } + (10, 0, 10586) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_10586_0_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + }) + } + (10, 0, 14393) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_14393_0_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + }) + } + (10, 0, 15063) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_15063_0_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + }) + } + (10, 0, 16299) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_16299_15_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + }) + } + (10, 0, 17134) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_17134_1_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + }) + } + (10, 0, 17763) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_17763_107_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + }) + } + (10, 0, 18362) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_18362_418_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + }) + } + (10, 0, 19041) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_19041_1288_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + }) + } + (10, 0, 19045) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_19045_2965_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + }) + } + (10, 0, 22000) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_22000_194_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + }) + } + (10, 0, 22621) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_22621_382_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + }) + } + (10, 0, 22631) => { + let ldr_data_table_entry = read_virtual::< + windows_10_0_22631_2428_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, ldr_data_table_entry_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + }) + } + (_, _, _) => bail!("Unsupported Windows version"), + } + } + + pub fn new_dtb( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + directory_table_base: u64, + virtual_address: u64, + ) -> Result { + match (major, minor, build) { + (10, 0, 10240) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_10240_16384_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + }) + } + (10, 0, 10586) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_10586_0_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + }) + } + (10, 0, 14393) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_14393_0_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + }) + } + (10, 0, 15063) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_15063_0_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + }) + } + (10, 0, 16299) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_16299_15_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + }) + } + (10, 0, 17134) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_17134_1_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + }) + } + (10, 0, 17763) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_17763_107_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + }) + } + (10, 0, 18362) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_18362_418_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + }) + } + (10, 0, 19041) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_19041_1288_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + }) + } + (10, 0, 19045) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_19045_2965_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + }) + } + (10, 0, 22000) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_22000_194_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + }) + } + (10, 0, 22621) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_22621_382_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + }) + } + (10, 0, 22631) => { + let ldr_data_table_entry = read_virtual_dtb::< + windows_10_0_22631_2428_x64::_LDR_DATA_TABLE_ENTRY, + >( + processor, directory_table_base, virtual_address + )?; + Ok(WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + }) + } + (_, _, _) => bail!("Unsupported Windows version"), + } + } + + pub fn new_from_in_memory_order_links( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + in_memory_order_links_address: u64, + ) -> Result { + let in_memory_order_links_offset = match (major, minor, build) { + (10, 0, 10240) => { + std::mem::offset_of!( + windows_10_0_10240_16384_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 10586) => { + std::mem::offset_of!( + windows_10_0_10586_0_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 14393) => { + std::mem::offset_of!( + windows_10_0_14393_0_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 15063) => { + std::mem::offset_of!( + windows_10_0_15063_0_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 16299) => { + std::mem::offset_of!( + windows_10_0_16299_15_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 17134) => { + std::mem::offset_of!( + windows_10_0_17134_1_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 17763) => { + std::mem::offset_of!( + windows_10_0_17763_107_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 18362) => { + std::mem::offset_of!( + windows_10_0_18362_418_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 19041) => { + std::mem::offset_of!( + windows_10_0_19041_1288_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 19045) => { + std::mem::offset_of!( + windows_10_0_19045_2965_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 22000) => { + std::mem::offset_of!( + windows_10_0_22000_194_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 22621) => { + std::mem::offset_of!( + windows_10_0_22621_382_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (10, 0, 22631) => { + std::mem::offset_of!( + windows_10_0_22631_2428_x64::_LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks + ) + } + (_, _, _) => bail!("Unsupported Windows version"), + }; + + let ldr_data_table_entry_address = + in_memory_order_links_address - in_memory_order_links_offset as u64; + + Self::new(processor, major, minor, build, ldr_data_table_entry_address) + } + + pub fn dll_base(&self) -> u64 { + match self { + WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + } => ldr_data_table_entry.DllBase as u64, + } + } + + pub fn entry_point(&self) -> u64 { + match self { + WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + } => ldr_data_table_entry.EntryPoint as u64, + } + } + + pub fn size_of_image(&self) -> u64 { + match self { + WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + } => ldr_data_table_entry.SizeOfImage as u64, + } + } + + pub fn full_name(&self, processor: *mut ConfObject) -> Result { + match self { + WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + ), + } + } + + pub fn full_name_dtb( + &self, + processor: *mut ConfObject, + directory_table_base: u64, + ) -> Result { + match self { + WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.FullDllName.Length as usize, + ldr_data_table_entry.FullDllName.Buffer, + directory_table_base, + ), + } + } + + pub fn base_name(&self, processor: *mut ConfObject) -> Result { + match self { + WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + } => read_unicode_string( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + ), + } + } + + pub fn base_name_dtb( + &self, + processor: *mut ConfObject, + directory_table_base: u64, + ) -> Result { + match self { + WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + } => read_unicode_string_dtb( + processor, + ldr_data_table_entry.BaseDllName.Length as usize, + ldr_data_table_entry.BaseDllName.Buffer, + directory_table_base, + ), + } + } + + pub fn in_load_order_links(&self) -> LIST_ENTRY { + match self { + WindowsLdrDataTableEntry::Windows10_0_10240_16384 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_10240_16384_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_10586_0 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_10586_0_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_14393_0 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_14393_0_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_15063_0 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_15063_0_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_16299_15 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_16299_15_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_17134_1 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_17134_1_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_17763_107 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_17763_107_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_18362_418 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_18362_418_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_19041_1288 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_19041_1288_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_19045_2965 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_19045_2965_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_22000_194 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_22000_194_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_22621_382 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_22621_382_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + WindowsLdrDataTableEntry::Windows10_0_22631_2428 { + ldr_data_table_entry, + } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_22631_2428_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data_table_entry.InLoadOrderLinks) + }, + } + } +} + +pub enum WindowsPebLdrData { + Windows10_0_10240_16384 { + ldr_data: windows_10_0_10240_16384_x64::_PEB_LDR_DATA, + }, + Windows10_0_10586_0 { + ldr_data: windows_10_0_10586_0_x64::_PEB_LDR_DATA, + }, + Windows10_0_14393_0 { + ldr_data: windows_10_0_14393_0_x64::_PEB_LDR_DATA, + }, + Windows10_0_15063_0 { + ldr_data: windows_10_0_15063_0_x64::_PEB_LDR_DATA, + }, + Windows10_0_16299_15 { + ldr_data: windows_10_0_16299_15_x64::_PEB_LDR_DATA, + }, + Windows10_0_17134_1 { + ldr_data: windows_10_0_17134_1_x64::_PEB_LDR_DATA, + }, + Windows10_0_17763_107 { + ldr_data: windows_10_0_17763_107_x64::_PEB_LDR_DATA, + }, + Windows10_0_18362_418 { + ldr_data: windows_10_0_18362_418_x64::_PEB_LDR_DATA, + }, + Windows10_0_19041_1288 { + ldr_data: windows_10_0_19041_1288_x64::_PEB_LDR_DATA, + }, + Windows10_0_19045_2965 { + ldr_data: windows_10_0_19045_2965_x64::_PEB_LDR_DATA, + }, + Windows10_0_22000_194 { + ldr_data: windows_10_0_22000_194_x64::_PEB_LDR_DATA, + }, + Windows10_0_22621_382 { + ldr_data: windows_10_0_22621_382_x64::_PEB_LDR_DATA, + }, + Windows10_0_22631_2428 { + ldr_data: windows_10_0_22631_2428_x64::_PEB_LDR_DATA, + }, +} + +impl WindowsPebLdrData { + pub fn new( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + ldr_data_address: u64, + ) -> Result { + match (major, minor, build) { + (10, 0, 10240) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_10240_16384 { ldr_data }) + } + (10, 0, 10586) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_10586_0 { ldr_data }) + } + (10, 0, 14393) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_14393_0 { ldr_data }) + } + (10, 0, 15063) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_15063_0 { ldr_data }) + } + (10, 0, 16299) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_16299_15 { ldr_data }) + } + (10, 0, 17134) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_17134_1 { ldr_data }) + } + (10, 0, 17763) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_17763_107 { ldr_data }) + } + (10, 0, 18362) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_18362_418 { ldr_data }) + } + (10, 0, 19041) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_19041_1288 { ldr_data }) + } + (10, 0, 19045) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_19045_2965 { ldr_data }) + } + (10, 0, 22000) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_22000_194 { ldr_data }) + } + (10, 0, 22621) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_22621_382 { ldr_data }) + } + (10, 0, 22631) => { + let ldr_data = read_virtual::( + processor, + ldr_data_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_22631_2428 { ldr_data }) + } + (_, _, _) => bail!("Unsupported Windows version"), + } + } + + pub fn new_dtb( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + directory_table_base: u64, + virtual_address: u64, + ) -> Result { + match (major, minor, build) { + (10, 0, 10240) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_10240_16384 { ldr_data }) + } + (10, 0, 10586) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_10586_0 { ldr_data }) + } + (10, 0, 14393) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_14393_0 { ldr_data }) + } + (10, 0, 15063) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_15063_0 { ldr_data }) + } + (10, 0, 16299) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_16299_15 { ldr_data }) + } + (10, 0, 17134) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_17134_1 { ldr_data }) + } + (10, 0, 17763) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_17763_107 { ldr_data }) + } + (10, 0, 18362) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_18362_418 { ldr_data }) + } + (10, 0, 19041) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_19041_1288 { ldr_data }) + } + (10, 0, 19045) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_19045_2965 { ldr_data }) + } + (10, 0, 22000) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_22000_194 { ldr_data }) + } + (10, 0, 22621) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_22621_382 { ldr_data }) + } + (10, 0, 22631) => { + let ldr_data = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPebLdrData::Windows10_0_22631_2428 { ldr_data }) + } + (_, _, _) => bail!("Unsupported Windows version"), + } + } + + pub fn length(&self) -> usize { + match self { + WindowsPebLdrData::Windows10_0_10240_16384 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_10586_0 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_14393_0 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_15063_0 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_16299_15 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_17134_1 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_17763_107 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_18362_418 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_19041_1288 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_19045_2965 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_22000_194 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_22621_382 { ldr_data } => ldr_data.Length as usize, + WindowsPebLdrData::Windows10_0_22631_2428 { ldr_data } => ldr_data.Length as usize, + } + } + + pub fn in_load_order_module_list(&self) -> LIST_ENTRY { + match self { + WindowsPebLdrData::Windows10_0_10240_16384 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_10240_16384_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_10586_0 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_10586_0_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_14393_0 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_14393_0_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_15063_0 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_15063_0_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_16299_15 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_16299_15_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_17134_1 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_17134_1_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_17763_107 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_17763_107_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_18362_418 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_18362_418_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_19041_1288 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_19041_1288_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_19045_2965 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_19045_2965_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_22000_194 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_22000_194_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_22621_382 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_22621_382_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + WindowsPebLdrData::Windows10_0_22631_2428 { ldr_data } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_22631_2428_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(ldr_data.InLoadOrderModuleList) + }, + } + } +} + +pub enum WindowsPeb { + Windows10_0_10240_16384 { + peb: windows_10_0_10240_16384_x64::_PEB, + }, + Windows10_0_10586_0 { + peb: windows_10_0_10586_0_x64::_PEB, + }, + Windows10_0_14393_0 { + peb: windows_10_0_14393_0_x64::_PEB, + }, + Windows10_0_15063_0 { + peb: windows_10_0_15063_0_x64::_PEB, + }, + Windows10_0_16299_15 { + peb: windows_10_0_16299_15_x64::_PEB, + }, + Windows10_0_17134_1 { + peb: windows_10_0_17134_1_x64::_PEB, + }, + Windows10_0_17763_107 { + peb: windows_10_0_17763_107_x64::_PEB, + }, + Windows10_0_18362_418 { + peb: windows_10_0_18362_418_x64::_PEB, + }, + Windows10_0_19041_1288 { + peb: windows_10_0_19041_1288_x64::_PEB, + }, + Windows10_0_19045_2965 { + peb: windows_10_0_19045_2965_x64::_PEB, + }, + Windows10_0_22000_194 { + peb: windows_10_0_22000_194_x64::_PEB, + }, + Windows10_0_22621_382 { + peb: windows_10_0_22621_382_x64::_PEB, + }, + Windows10_0_22631_2428 { + peb: windows_10_0_22631_2428_x64::_PEB, + }, +} + +impl WindowsPeb { + pub fn new( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + peb_address: u64, + ) -> Result { + match (major, minor, build) { + (10, 0, 10240) => { + let peb = + read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_10240_16384 { peb }) + } + (10, 0, 10586) => { + let peb = read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_10586_0 { peb }) + } + (10, 0, 14393) => { + let peb = read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_14393_0 { peb }) + } + (10, 0, 15063) => { + let peb = read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_15063_0 { peb }) + } + (10, 0, 16299) => { + let peb = read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_16299_15 { peb }) + } + (10, 0, 17134) => { + let peb = read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_17134_1 { peb }) + } + (10, 0, 17763) => { + let peb = read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_17763_107 { peb }) + } + (10, 0, 18362) => { + let peb = read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_18362_418 { peb }) + } + (10, 0, 19041) => { + let peb = + read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_19041_1288 { peb }) + } + (10, 0, 19045) => { + let peb = + read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_19045_2965 { peb }) + } + (10, 0, 22000) => { + let peb = read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_22000_194 { peb }) + } + (10, 0, 22621) => { + let peb = read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_22621_382 { peb }) + } + (10, 0, 22631) => { + let peb = + read_virtual::(processor, peb_address)?; + Ok(WindowsPeb::Windows10_0_22631_2428 { peb }) + } + (_, _, _) => { + bail!("Unsupported Windows version") + } + } + } + + pub fn new_dtb( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + directory_table_base: u64, + virtual_address: u64, + ) -> Result { + match (major, minor, build) { + (10, 0, 10240) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_10240_16384 { peb }) + } + (10, 0, 10586) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_10586_0 { peb }) + } + (10, 0, 14393) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_14393_0 { peb }) + } + (10, 0, 15063) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_15063_0 { peb }) + } + (10, 0, 16299) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_16299_15 { peb }) + } + (10, 0, 17134) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_17134_1 { peb }) + } + (10, 0, 17763) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_17763_107 { peb }) + } + (10, 0, 18362) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_18362_418 { peb }) + } + (10, 0, 19041) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_19041_1288 { peb }) + } + (10, 0, 19045) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_19045_2965 { peb }) + } + (10, 0, 22000) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_22000_194 { peb }) + } + (10, 0, 22621) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_22621_382 { peb }) + } + (10, 0, 22631) => { + let peb = read_virtual_dtb::( + processor, + directory_table_base, + virtual_address, + )?; + Ok(WindowsPeb::Windows10_0_22631_2428 { peb }) + } + (_, _, _) => { + bail!("Unsupported Windows version") + } + } + } + + pub fn base(&self) -> u64 { + match self { + WindowsPeb::Windows10_0_10240_16384 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_10586_0 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_14393_0 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_15063_0 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_16299_15 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_17134_1 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_17763_107 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_18362_418 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_19041_1288 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_19045_2965 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_22000_194 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_22621_382 { peb } => peb.ImageBaseAddress as u64, + WindowsPeb::Windows10_0_22631_2428 { peb } => peb.ImageBaseAddress as u64, + } + } + + pub fn ldr_address(&self) -> u64 { + match self { + WindowsPeb::Windows10_0_10240_16384 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_10586_0 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_14393_0 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_15063_0 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_16299_15 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_17134_1 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_17763_107 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_18362_418 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_19041_1288 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_19045_2965 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_22000_194 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_22621_382 { peb } => peb.Ldr as u64, + WindowsPeb::Windows10_0_22631_2428 { peb } => peb.Ldr as u64, + } + } +} + +pub enum WindowsTeb { + Windows10_0_10240_16384 { + teb: windows_10_0_10240_16384_x64::_TEB, + }, + Windows10_0_10586_0 { + teb: windows_10_0_10586_0_x64::_TEB, + }, + Windows10_0_14393_0 { + teb: windows_10_0_14393_0_x64::_TEB, + }, + Windows10_0_15063_0 { + teb: windows_10_0_15063_0_x64::_TEB, + }, + Windows10_0_16299_15 { + teb: windows_10_0_16299_15_x64::_TEB, + }, + Windows10_0_17134_1 { + teb: windows_10_0_17134_1_x64::_TEB, + }, + Windows10_0_17763_107 { + teb: windows_10_0_17763_107_x64::_TEB, + }, + Windows10_0_18362_418 { + teb: windows_10_0_18362_418_x64::_TEB, + }, + Windows10_0_19041_1288 { + teb: windows_10_0_19041_1288_x64::_TEB, + }, + Windows10_0_19045_2965 { + teb: windows_10_0_19045_2965_x64::_TEB, + }, + Windows10_0_22000_194 { + teb: windows_10_0_22000_194_x64::_TEB, + }, + Windows10_0_22621_382 { + teb: windows_10_0_22621_382_x64::_TEB, + }, + Windows10_0_22631_2428 { + teb: windows_10_0_22631_2428_x64::_TEB, + }, +} + +impl WindowsTeb { + pub fn new( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + teb_address: u64, + ) -> Result { + match (major, minor, build) { + (10, 0, 10240) => { + let teb = + read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_10240_16384 { teb }) + } + (10, 0, 10586) => { + let teb = read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_10586_0 { teb }) + } + (10, 0, 14393) => { + let teb = read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_14393_0 { teb }) + } + (10, 0, 15063) => { + let teb = read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_15063_0 { teb }) + } + (10, 0, 16299) => { + let teb = read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_16299_15 { teb }) + } + (10, 0, 17134) => { + let teb = read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_17134_1 { teb }) + } + (10, 0, 17763) => { + let teb = read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_17763_107 { teb }) + } + (10, 0, 18362) => { + let teb = read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_18362_418 { teb }) + } + (10, 0, 19041) => { + let teb = + read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_19041_1288 { teb }) + } + (10, 0, 19045) => { + let teb = + read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_19045_2965 { teb }) + } + (10, 0, 22000) => { + let teb = read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_22000_194 { teb }) + } + (10, 0, 22621) => { + let teb = read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_22621_382 { teb }) + } + (10, 0, 22631) => { + let teb = + read_virtual::(processor, teb_address)?; + Ok(WindowsTeb::Windows10_0_22631_2428 { teb }) + } + (_, _, _) => { + bail!("Unsupported Windows version") + } + } + } + + pub fn peb( + &self, + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + ) -> Result { + let peb_address = match self { + WindowsTeb::Windows10_0_10240_16384 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_10586_0 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_14393_0 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_15063_0 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_16299_15 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_17134_1 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_17763_107 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_18362_418 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_19041_1288 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_19045_2965 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_22000_194 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_22621_382 { teb } => teb.ProcessEnvironmentBlock as u64, + WindowsTeb::Windows10_0_22631_2428 { teb } => teb.ProcessEnvironmentBlock as u64, + }; + debug!("peb_address: {:#x}", peb_address); + WindowsPeb::new(processor, major, minor, build, peb_address) + } +} + +pub enum WindowsEProcess { + Windows10_0_10240_16384 { + eprocess: windows_10_0_10240_16384_x64::_EPROCESS, + }, + Windows10_0_10586_0 { + eprocess: windows_10_0_10586_0_x64::_EPROCESS, + }, + Windows10_0_14393_0 { + eprocess: windows_10_0_14393_0_x64::_EPROCESS, + }, + Windows10_0_15063_0 { + eprocess: windows_10_0_15063_0_x64::_EPROCESS, + }, + Windows10_0_16299_15 { + eprocess: windows_10_0_16299_15_x64::_EPROCESS, + }, + Windows10_0_17134_1 { + eprocess: windows_10_0_17134_1_x64::_EPROCESS, + }, + Windows10_0_17763_107 { + eprocess: windows_10_0_17763_107_x64::_EPROCESS, + }, + Windows10_0_18362_418 { + eprocess: windows_10_0_18362_418_x64::_EPROCESS, + }, + Windows10_0_19041_1288 { + eprocess: windows_10_0_19041_1288_x64::_EPROCESS, + }, + Windows10_0_19045_2965 { + eprocess: windows_10_0_19045_2965_x64::_EPROCESS, + }, + Windows10_0_22000_194 { + eprocess: windows_10_0_22000_194_x64::_EPROCESS, + }, + Windows10_0_22621_382 { + eprocess: windows_10_0_22621_382_x64::_EPROCESS, + }, + Windows10_0_22631_2428 { + eprocess: windows_10_0_22631_2428_x64::_EPROCESS, + }, +} + +impl WindowsEProcess { + pub fn new( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + eprocess_address: u64, + ) -> Result { + match (major, minor, build) { + (10, 0, 10240) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_10240_16384 { eprocess }) + } + (10, 0, 10586) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_10586_0 { eprocess }) + } + (10, 0, 14393) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_14393_0 { eprocess }) + } + (10, 0, 15063) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_15063_0 { eprocess }) + } + (10, 0, 16299) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_16299_15 { eprocess }) + } + (10, 0, 17134) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_17134_1 { eprocess }) + } + (10, 0, 17763) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_17763_107 { eprocess }) + } + (10, 0, 18362) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_18362_418 { eprocess }) + } + (10, 0, 19041) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_19041_1288 { eprocess }) + } + (10, 0, 19045) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_19045_2965 { eprocess }) + } + (10, 0, 22000) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_22000_194 { eprocess }) + } + (10, 0, 22621) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_22621_382 { eprocess }) + } + (10, 0, 22631) => { + let eprocess = read_virtual::( + processor, + eprocess_address, + )?; + Ok(WindowsEProcess::Windows10_0_22631_2428 { eprocess }) + } + (_, _, _) => { + bail!("Unsupported Windows version") + } + } + } + + pub fn new_from_active_process_links_address( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + active_process_links_address: u64, + ) -> Result { + let active_process_links_offset = match (major, minor, build) { + (10, 0, 10240) => { + std::mem::offset_of!(windows_10_0_10240_16384_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 10586) => { + std::mem::offset_of!(windows_10_0_10586_0_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 14393) => { + std::mem::offset_of!(windows_10_0_14393_0_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 15063) => { + std::mem::offset_of!(windows_10_0_15063_0_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 16299) => { + std::mem::offset_of!(windows_10_0_16299_15_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 17134) => { + std::mem::offset_of!(windows_10_0_17134_1_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 17763) => { + std::mem::offset_of!(windows_10_0_17763_107_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 18362) => { + std::mem::offset_of!(windows_10_0_18362_418_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 19041) => { + std::mem::offset_of!(windows_10_0_19041_1288_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 19045) => { + std::mem::offset_of!(windows_10_0_19045_2965_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 22000) => { + std::mem::offset_of!(windows_10_0_22000_194_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 22621) => { + std::mem::offset_of!(windows_10_0_22621_382_x64::_EPROCESS, ActiveProcessLinks) + } + (10, 0, 22631) => { + std::mem::offset_of!(windows_10_0_22631_2428_x64::_EPROCESS, ActiveProcessLinks) + } + (_, _, _) => { + bail!("Unsupported Windows version") + } + }; + let eprocess_address = active_process_links_address - active_process_links_offset as u64; + + Self::new(processor, major, minor, build, eprocess_address) + } + + pub fn active_process_links(&self) -> LIST_ENTRY { + match self { + WindowsEProcess::Windows10_0_10240_16384 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_10240_16384_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_10586_0 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_10586_0_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_14393_0 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_14393_0_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_15063_0 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_15063_0_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_16299_15 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_16299_15_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_17134_1 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_17134_1_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_17763_107 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_17763_107_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_18362_418 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_18362_418_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_19041_1288 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_19041_1288_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_19045_2965 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_19045_2965_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_22000_194 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_22000_194_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_22621_382 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_22621_382_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + WindowsEProcess::Windows10_0_22631_2428 { eprocess } => unsafe { + std::mem::transmute::< + vergilius::windows_10_0_22631_2428_x64::_LIST_ENTRY, + windows::Win32::System::Kernel::LIST_ENTRY, + >(eprocess.ActiveProcessLinks) + }, + } + } + + pub fn pid(&self) -> u64 { + match self { + WindowsEProcess::Windows10_0_10240_16384 { eprocess } => { + eprocess.UniqueProcessId as u64 + } + WindowsEProcess::Windows10_0_10586_0 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_14393_0 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_15063_0 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_16299_15 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_17134_1 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_17763_107 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_18362_418 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_19041_1288 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_19045_2965 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_22000_194 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_22621_382 { eprocess } => eprocess.UniqueProcessId as u64, + WindowsEProcess::Windows10_0_22631_2428 { eprocess } => eprocess.UniqueProcessId as u64, + } + } + + pub fn file_name(&self, processor: *mut ConfObject) -> Result { + // 1. Read _EPROCESS.SeAuditProcessCreationInfo.ImageFileName + let object_name_information_addr = match self { + WindowsEProcess::Windows10_0_10240_16384 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_10586_0 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_14393_0 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_15063_0 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_16299_15 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_17134_1 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_17763_107 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_18362_418 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_19041_1288 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_19045_2965 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_22000_194 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_22621_382 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + WindowsEProcess::Windows10_0_22631_2428 { eprocess } => { + eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 + } + }; + let object_name_information = + read_virtual::(processor, object_name_information_addr)?; + read_unicode_string( + processor, + object_name_information.Length as usize, + object_name_information.Buffer.0, + ) + } + + pub fn base_address( + &self, + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + ) -> Result { + let peb_address = match self { + WindowsEProcess::Windows10_0_10240_16384 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_10586_0 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_14393_0 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_15063_0 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_16299_15 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_17134_1 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_17763_107 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_18362_418 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_19041_1288 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_19045_2965 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_22000_194 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_22621_382 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_22631_2428 { eprocess } => eprocess.Peb as u64, + }; + let peb = WindowsPeb::new(processor, major, minor, build, peb_address)?; + Ok(peb.base()) + } + + #[allow(clippy::too_many_arguments)] + pub fn modules

( + &self, + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + download_directory: P, + not_found_full_name_cache: &mut HashSet, + user_debug_info: &HashMap>, + ) -> Result> + where + P: AsRef, + { + let peb_address = match self { + WindowsEProcess::Windows10_0_10240_16384 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_10586_0 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_14393_0 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_15063_0 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_16299_15 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_17134_1 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_17763_107 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_18362_418 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_19041_1288 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_19045_2965 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_22000_194 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_22621_382 { eprocess } => eprocess.Peb as u64, + WindowsEProcess::Windows10_0_22631_2428 { eprocess } => eprocess.Peb as u64, + }; + let mut directory_table_base = match self { + WindowsEProcess::Windows10_0_10240_16384 { eprocess } => { + eprocess.Pcb.DirectoryTableBase + } + WindowsEProcess::Windows10_0_10586_0 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_14393_0 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_15063_0 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_16299_15 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_17134_1 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_17763_107 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_18362_418 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_19041_1288 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_19045_2965 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_22000_194 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_22621_382 { eprocess } => eprocess.Pcb.DirectoryTableBase, + WindowsEProcess::Windows10_0_22631_2428 { eprocess } => eprocess.Pcb.DirectoryTableBase, + }; + debug!("Using directory table base {:#x}", directory_table_base); + + if directory_table_base == 0 { + directory_table_base = match self { + WindowsEProcess::Windows10_0_10240_16384 { .. } => { + bail!("No UserDirectoryTableBase before 1803"); + } + WindowsEProcess::Windows10_0_10586_0 { .. } => { + bail!("No UserDirectoryTableBase before 1803") + } + WindowsEProcess::Windows10_0_14393_0 { .. } => { + bail!("No UserDirectoryTableBase before 1803") + } + WindowsEProcess::Windows10_0_15063_0 { .. } => { + bail!("No UserDirectoryTableBase before 1803") + } + WindowsEProcess::Windows10_0_16299_15 { .. } => { + bail!("No UserDirectoryTableBase before 1803") + } + WindowsEProcess::Windows10_0_17134_1 { eprocess } => { + eprocess.Pcb.UserDirectoryTableBase + } + WindowsEProcess::Windows10_0_17763_107 { eprocess } => { + eprocess.Pcb.UserDirectoryTableBase + } + WindowsEProcess::Windows10_0_18362_418 { eprocess } => { + eprocess.Pcb.UserDirectoryTableBase + } + WindowsEProcess::Windows10_0_19041_1288 { eprocess } => { + eprocess.Pcb.UserDirectoryTableBase + } + WindowsEProcess::Windows10_0_19045_2965 { eprocess } => { + eprocess.Pcb.UserDirectoryTableBase + } + WindowsEProcess::Windows10_0_22000_194 { eprocess } => { + eprocess.Pcb.UserDirectoryTableBase + } + WindowsEProcess::Windows10_0_22621_382 { eprocess } => { + eprocess.Pcb.UserDirectoryTableBase + } + WindowsEProcess::Windows10_0_22631_2428 { eprocess } => { + eprocess.Pcb.UserDirectoryTableBase + } + }; + debug!("Invalid DTB, using user DTB: {:#x}", directory_table_base); + } + + let mut modules = Vec::new(); + + debug!("PEB ADDRESS: {:x}", peb_address); + if peb_address != 0 { + let peb = WindowsPeb::new_dtb( + processor, + major, + minor, + build, + directory_table_base, + peb_address, + )?; + let ldr_address = peb.ldr_address(); + let ldr = WindowsPebLdrData::new_dtb( + processor, + major, + minor, + build, + directory_table_base, + ldr_address, + )?; + let mut list_entry = ldr.in_load_order_module_list(); + let last_entry = list_entry.Blink; + + while !list_entry.Flink.is_null() { + let ldr_data_entry = WindowsLdrDataTableEntry::new_dtb( + processor, + major, + minor, + build, + directory_table_base, + list_entry.Flink as u64, + )?; + + let base = ldr_data_entry.dll_base(); + let size = ldr_data_entry.size_of_image(); + let full_name = ldr_data_entry.full_name_dtb(processor, directory_table_base)?; + let base_name = ldr_data_entry.base_name_dtb(processor, directory_table_base)?; + let debug_info = full_name + .split('\\') + .last() + .ok_or_else(|| anyhow!("Failed to get file name")) + .and_then(|fname| { + // No need for DTB version because kernel is always mapped + DebugInfo::new( + processor, + fname, + base, + download_directory.as_ref(), + not_found_full_name_cache, + user_debug_info, + ) + }) + .ok(); + + modules.push(ProcessModule { + base, + size, + full_name, + base_name, + debug_info, + }); + + list_entry = ldr_data_entry.in_load_order_links(); + + if list_entry.Flink == last_entry { + break; + } + } + } + + Ok(modules) + } +} + +pub enum WindowsKThread { + Windows10_0_10240_16384 { + kthread: windows_10_0_10240_16384_x64::_KTHREAD, + }, + Windows10_0_10586_0 { + kthread: windows_10_0_10586_0_x64::_KTHREAD, + }, + Windows10_0_14393_0 { + kthread: windows_10_0_14393_0_x64::_KTHREAD, + }, + Windows10_0_15063_0 { + kthread: windows_10_0_15063_0_x64::_KTHREAD, + }, + Windows10_0_16299_15 { + kthread: windows_10_0_16299_15_x64::_KTHREAD, + }, + Windows10_0_17134_1 { + kthread: windows_10_0_17134_1_x64::_KTHREAD, + }, + Windows10_0_17763_107 { + kthread: windows_10_0_17763_107_x64::_KTHREAD, + }, + Windows10_0_18362_418 { + kthread: windows_10_0_18362_418_x64::_KTHREAD, + }, + Windows10_0_19041_1288 { + kthread: windows_10_0_19041_1288_x64::_KTHREAD, + }, + Windows10_0_19045_2965 { + kthread: windows_10_0_19045_2965_x64::_KTHREAD, + }, + Windows10_0_22000_194 { + kthread: windows_10_0_22000_194_x64::_KTHREAD, + }, + Windows10_0_22621_382 { + kthread: windows_10_0_22621_382_x64::_KTHREAD, + }, + Windows10_0_22631_2428 { + kthread: windows_10_0_22631_2428_x64::_KTHREAD, + }, +} + +impl WindowsKThread { + pub fn new( + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + kthread_address: u64, + ) -> Result { + match (major, minor, build) { + (10, 0, 10240) => { + let kthread = read_virtual::( + processor, + kthread_address, + )?; + Ok(WindowsKThread::Windows10_0_10240_16384 { kthread }) + } + (10, 0, 10586) => { + let kthread = + read_virtual::(processor, kthread_address)?; + Ok(WindowsKThread::Windows10_0_10586_0 { kthread }) + } + (10, 0, 14393) => { + let kthread = + read_virtual::(processor, kthread_address)?; + Ok(WindowsKThread::Windows10_0_14393_0 { kthread }) + } + (10, 0, 15063) => { + let kthread = + read_virtual::(processor, kthread_address)?; + Ok(WindowsKThread::Windows10_0_15063_0 { kthread }) + } + (10, 0, 16299) => { + let kthread = read_virtual::( + processor, + kthread_address, + )?; + Ok(WindowsKThread::Windows10_0_16299_15 { kthread }) + } + (10, 0, 17134) => { + let kthread = + read_virtual::(processor, kthread_address)?; + Ok(WindowsKThread::Windows10_0_17134_1 { kthread }) + } + (10, 0, 17763) => { + let kthread = read_virtual::( + processor, + kthread_address, + )?; + Ok(WindowsKThread::Windows10_0_17763_107 { kthread }) + } + (10, 0, 18362) => { + let kthread = read_virtual::( + processor, + kthread_address, + )?; + Ok(WindowsKThread::Windows10_0_18362_418 { kthread }) + } + (10, 0, 19041) => { + let kthread = read_virtual::( + processor, + kthread_address, + )?; + Ok(WindowsKThread::Windows10_0_19041_1288 { kthread }) + } + (10, 0, 19045) => { + let kthread = read_virtual::( + processor, + kthread_address, + )?; + Ok(WindowsKThread::Windows10_0_19045_2965 { kthread }) + } + (10, 0, 22000) => { + let kthread = read_virtual::( + processor, + kthread_address, + )?; + Ok(WindowsKThread::Windows10_0_22000_194 { kthread }) + } + (10, 0, 22621) => { + let kthread = read_virtual::( + processor, + kthread_address, + )?; + Ok(WindowsKThread::Windows10_0_22621_382 { kthread }) + } + (10, 0, 22631) => { + let kthread = read_virtual::( + processor, + kthread_address, + )?; + Ok(WindowsKThread::Windows10_0_22631_2428 { kthread }) + } + (_, _, _) => { + bail!("Unsupported Windows version") + } + } + } + + pub fn process( + &self, + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + ) -> Result { + let process_address = match self { + WindowsKThread::Windows10_0_10240_16384 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_10586_0 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_14393_0 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_15063_0 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_16299_15 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_17134_1 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_17763_107 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_18362_418 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_19041_1288 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_19045_2965 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_22000_194 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_22621_382 { kthread } => kthread.Process as u64, + WindowsKThread::Windows10_0_22631_2428 { kthread } => kthread.Process as u64, + }; + WindowsEProcess::new(processor, major, minor, build, process_address) + } + + pub fn teb( + &self, + processor: *mut ConfObject, + major: u32, + minor: u32, + build: u32, + ) -> Result { + let teb_address = match self { + WindowsKThread::Windows10_0_10240_16384 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_10586_0 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_14393_0 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_15063_0 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_16299_15 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_17134_1 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_17763_107 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_18362_418 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_19041_1288 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_19045_2965 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_22000_194 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_22621_382 { kthread } => kthread.Teb as u64, + WindowsKThread::Windows10_0_22631_2428 { kthread } => kthread.Teb as u64, + }; + WindowsTeb::new(processor, major, minor, build, teb_address) + } +} diff --git a/src/os/windows/util.rs b/src/os/windows/util.rs new file mode 100644 index 00000000..8b7ab41f --- /dev/null +++ b/src/os/windows/util.rs @@ -0,0 +1,228 @@ +use std::mem::MaybeUninit; + +use anyhow::{anyhow, bail, Result}; +use simics::{get_interface, read_byte, Access, ConfObject, ProcessorInfoV2Interface}; + +use super::paging::{ + DIR_TABLE_BASE, PAGE_1GB_SHIFT, PAGE_2MB_SHIFT, PAGE_4KB_SHIFT, PDE, PDPTE, PDPTE_LARGE, PML4E, + PTE, VIRTUAL_ADDRESS, +}; + +pub fn read_virtual(processor: *mut ConfObject, virtual_address: u64) -> Result +where + T: Sized, +{ + let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(processor)?; + + let size = std::mem::size_of::(); + + let mut t = MaybeUninit::::uninit(); + + let memory = processor_info_v2.get_physical_memory()?; + + let contents = (0..size) + .map(|i| { + processor_info_v2 + .logical_to_physical(virtual_address + i as u64, Access::Sim_Access_Read) + .and_then(|b| read_byte(memory, b.address)) + .map_err(|e| anyhow!("Failed to read memory: {}", e)) + }) + .collect::>>()?; + + unsafe { + std::ptr::copy_nonoverlapping(contents.as_ptr(), t.as_mut_ptr() as *mut u8, size); + Ok(t.assume_init()) + } +} + +pub fn read_physical(processor: *mut ConfObject, physical_address: u64) -> Result { + let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(processor)?; + + let size = std::mem::size_of::(); + + let mut t = MaybeUninit::::uninit(); + + let memory = processor_info_v2.get_physical_memory()?; + + let contents = (0..size) + .map(|i| { + read_byte(memory, physical_address + i as u64) + .map_err(|e| anyhow!("Failed to read memory: {}", e)) + }) + .collect::>>()?; + + unsafe { + std::ptr::copy_nonoverlapping(contents.as_ptr(), t.as_mut_ptr() as *mut u8, size); + Ok(t.assume_init()) + } +} + +pub fn read_virtual_dtb( + processor: *mut ConfObject, + directory_table_base: u64, + virtual_address: u64, +) -> Result { + let physical_address = virtual_to_physical(processor, directory_table_base, virtual_address)?; + read_physical(processor, physical_address) +} + +pub fn virtual_to_physical( + processor: *mut ConfObject, + directory_table_base: u64, + virtual_address: u64, + // build: u32, +) -> Result { + let virtual_address = VIRTUAL_ADDRESS { + All: virtual_address, + }; + let dir_table_base = DIR_TABLE_BASE { + All: directory_table_base, + }; + let pml4e = read_physical::( + processor, + (unsafe { dir_table_base.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT as u64) + + (unsafe { virtual_address.Bits }.Pml4Index() * 8), + )?; + + if unsafe { pml4e.Bits }.Present() == 0 { + bail!("PML4E not present"); + } + + let pdpte = read_physical::( + processor, + (unsafe { pml4e.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT) + + (unsafe { virtual_address.Bits }.PdptIndex() * 8), + )?; + + if unsafe { pdpte.Bits }.Present() == 0 { + bail!("PDPTE not present"); + } + + if (unsafe { pdpte.All } >> 7) & 1 != 0 { + let pdpte_large = PDPTE_LARGE { + All: unsafe { pdpte.All }, + }; + return Ok( + ((unsafe { pdpte_large.Bits }.PhysicalAddress()) << PAGE_1GB_SHIFT) + + (unsafe { virtual_address.All } & (!(u64::MAX << PAGE_1GB_SHIFT))), + ); + } + + let pde = read_physical::( + processor, + (unsafe { pdpte.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT) + + (unsafe { virtual_address.Bits }.PdIndex() * 8), + )?; + + if unsafe { pde.Bits }.Present() == 0 { + bail!("PDE not present"); + } + + if (unsafe { pde.All } >> 7) & 1 != 0 { + let pde_large = PDPTE_LARGE { + All: unsafe { pde.All }, + }; + return Ok( + ((unsafe { pde_large.Bits }.PhysicalAddress()) << PAGE_2MB_SHIFT) + + (unsafe { virtual_address.All } & (!(u64::MAX << PAGE_2MB_SHIFT))), + ); + } + + let pte = read_physical::( + processor, + (unsafe { pde.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT) + + (unsafe { virtual_address.Bits }.PtIndex() * 8), + )?; + + if unsafe { pte.Bits }.Present() == 0 { + bail!("PTE not present"); + } + + Ok((unsafe { pte.Bits }.PhysicalAddress() << PAGE_4KB_SHIFT) + + unsafe { virtual_address.Bits }.PageIndex()) +} + +pub fn read_unicode_string( + processor: *mut ConfObject, + length: usize, + buffer: *const u16, +) -> Result { + let mut string = Vec::new(); + let mut address = buffer as u64; + + for _ in 0..length { + let character = read_virtual::(processor, address)?; + + if character == 0 { + break; + } + + string.push(character); + address += 2; + } + + String::from_utf16(&string).map_err(|e| anyhow!("Failed to convert string: {}", e)) +} + +pub fn read_unicode_string_dtb( + processor: *mut ConfObject, + length: usize, + buffer: *const u16, + directory_table_base: u64, +) -> Result { + let mut string = Vec::new(); + let mut address = buffer as u64; + + for _ in 0..length { + let character = read_virtual_dtb::(processor, directory_table_base, address)?; + + if character == 0 { + break; + } + + string.push(character); + address += 2; + } + + String::from_utf16(&string).map_err(|e| anyhow!("Failed to convert string: {}", e)) +} + +pub fn read_nul_terminated_string(processor: *mut ConfObject, address: u64) -> Result { + let mut string = String::new(); + let mut address = address; + + loop { + let character = read_virtual::(processor, address)?; + + if character == 0 { + break; + } + + string.push(character as char); + address += 1; + } + + Ok(string) +} + +pub fn read_nul_terminated_string_dtb( + processor: *mut ConfObject, + address: u64, + directory_table_base: u64, +) -> Result { + let mut string = String::new(); + let mut address = address; + + loop { + let character = read_virtual_dtb::(processor, directory_table_base, address)?; + + if character == 0 { + break; + } + + string.push(character as char); + address += 1; + } + + Ok(string) +} diff --git a/src/tracer/mod.rs b/src/tracer/mod.rs index c08c125c..b8a5996e 100644 --- a/src/tracer/mod.rs +++ b/src/tracer/mod.rs @@ -12,16 +12,29 @@ use simics::{ get_processor_number, sys::instruction_handle_t, AsConfObject, AttrValue, AttrValueType, ConfObject, }, - trace, + get_interface, trace, ProcessorInfoV2Interface, }; use std::{ collections::HashMap, ffi::c_void, fmt::Display, hash::Hash, num::Wrapping, slice::from_raw_parts, str::FromStr, }; +use symbolic::demangle::Demangle; use typed_builder::TypedBuilder; use crate::{arch::ArchitectureOperations, Tsffs}; +#[derive(Deserialize, Serialize, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct ExecutionTraceSymbol { + /// The symbol name + pub symbol: String, + /// The demangled symbol name, if it demangles correctly + pub symbol_demangled: Option, + /// The offset into the symbol + pub offset: u64, + /// The containing module's name (usually the path to the executable) + pub module: String, +} + #[derive(Deserialize, Serialize, Debug, Default)] pub(crate) struct ExecutionTrace(pub HashMap>); @@ -43,6 +56,8 @@ pub(crate) struct ExecutionTraceEntry { insn: Option, #[builder(default, setter(into, strip_option))] insn_bytes: Option>, + #[builder(default, setter(into))] + symbol: Option, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -356,7 +371,7 @@ impl Tsffs { ) -> Result<()> { let processor_number = get_processor_number(cpu)?; - if self.cmplog && self.cmplog_enabled { + if self.coverage_enabled && self.cmplog && self.cmplog_enabled { if let Some(arch) = self.processors.get_mut(&processor_number) { match arch.trace_cmp(handle) { Ok(r) => { @@ -373,6 +388,30 @@ impl Tsffs { } } + let symcov = if self.coverage_enabled && self.windows && self.symbolic_coverage { + // Get the current instruction address + let mut processor_information_v2 = get_interface::(cpu)?; + let pc = processor_information_v2.get_program_counter()?; + self.windows_os_info + .symbol_lookup_trees + .get(&processor_number) + .and_then(|lookup_tree| { + lookup_tree.query(pc..pc + 1).next().map(|q| { + let offset = pc - q.range.start; + let symbol_demangled = symbolic::common::Name::from(&q.value.name) + .demangle(symbolic::demangle::DemangleOptions::complete()); + ExecutionTraceSymbol { + symbol: q.value.name.clone(), + symbol_demangled, + offset, + module: q.value.module.clone(), + } + }) + }) + } else { + None + }; + if self.coverage_enabled && (self.save_all_execution_traces || self.save_interesting_execution_traces @@ -401,12 +440,14 @@ impl Tsffs { .pc(arch.processor_info_v2().get_program_counter()?) .insn(disassembly_string) .insn_bytes(instruction_bytes.to_vec()) + .symbol(symcov) .build() } else { ExecutionTraceEntry::builder() .pc(arch.processor_info_v2().get_program_counter()?) .insn("(unknown)".to_string()) .insn_bytes(instruction_bytes.to_vec()) + .symbol(symcov) .build() } }); From b81da69cbcb1d08a3e01337ca41a5a19c1e2acd8 Mon Sep 17 00:00:00 2001 From: novafacing Date: Tue, 25 Jun 2024 17:04:29 -0700 Subject: [PATCH 02/14] Fix some logging and debug issues --- src/haps/mod.rs | 3 +++ src/lib.rs | 5 ++-- src/os/windows/debug_info.rs | 26 +++++++++++++++------ src/os/windows/kernel.rs | 20 ++++++++++------ src/os/windows/mod.rs | 44 ++++++++++++++++++++---------------- src/os/windows/structs.rs | 12 +--------- 6 files changed, 64 insertions(+), 46 deletions(-) diff --git a/src/haps/mod.rs b/src/haps/mod.rs index 25eef62f..7cb924da 100644 --- a/src/haps/mod.rs +++ b/src/haps/mod.rs @@ -57,6 +57,7 @@ impl Tsffs { self.save_initial_snapshot()?; // Collect windows coverage info if enabled if self.windows && self.symbolic_coverage { + info!(self.as_conf_object(), "Collecting initial coverage info"); self.windows_os_info.collect( start_processor_raw, &self.debuginfo_download_directory, @@ -216,6 +217,7 @@ impl Tsffs { // Collect windows coverage info if enabled if self.windows && self.symbolic_coverage { + info!(self.as_conf_object(), "Collecting initial coverage info"); self.windows_os_info.collect( processor, &self.debuginfo_download_directory, @@ -258,6 +260,7 @@ impl Tsffs { // Collect windows coverage info if enabled if self.windows && self.symbolic_coverage { + info!(self.as_conf_object(), "Collecting initial coverage info"); self.windows_os_info.collect( processor, &self.debuginfo_download_directory, diff --git a/src/lib.rs b/src/lib.rs index d5cb3849..fcf11248 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -515,6 +515,7 @@ pub(crate) struct Tsffs { solutions: usize, windows_os_info: WindowsOsInfo, + cr3_cache: HashMap, } impl ClassObjectsFinalize for Tsffs { @@ -707,7 +708,7 @@ impl Tsffs { } // Disable VMP if it is enabled - info!("Disabling VMP"); + info!(self.as_conf_object(), "Disabling VMP"); if let Err(e) = run_command("disable-vmp") { warn!(self.as_conf_object(), "Failed to disable VMP: {}", e); } @@ -968,7 +969,7 @@ fn init() { ) "#}) .map_err(|e| { - error!("{e}"); + error!(tsffs, "{e}"); e }) .expect("Failed to run python"); diff --git a/src/os/windows/debug_info.rs b/src/os/windows/debug_info.rs index 5e60d8b4..b779446e 100644 --- a/src/os/windows/debug_info.rs +++ b/src/os/windows/debug_info.rs @@ -11,7 +11,7 @@ use std::{ }; use lending_iterator::{windows_mut, LendingIterator}; -use simics::{debug, info, ConfObject}; +use simics::{debug, get_object, info, ConfObject}; use windows::Win32::System::{ Diagnostics::Debug::{ IMAGE_DEBUG_DIRECTORY, IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DIRECTORY_ENTRY_DEBUG, @@ -46,7 +46,10 @@ impl<'a> DebugInfo<'a> { P: AsRef, { if let Some(info) = user_debug_info.get(name) { - debug!("Have user-provided debug info for {name}"); + debug!( + get_object("tsffs")?, + "Have user-provided debug info for {name}" + ); let exe_path = info[0].clone(); let pdb_path = info[1].clone(); @@ -115,7 +118,7 @@ impl<'a> DebugInfo<'a> { .join(format!("{}.exe", &exe_guid)); if !exe_path.exists() && !not_found_full_name_cache.contains(name) { - info!("Downloading PE file from {}", exe_url); + info!(get_object("tsffs")?, "Downloading PE file from {}", exe_url); match get(&exe_url)?.error_for_status() { Ok(response) => { let mut file = File::create(&exe_path)?; @@ -135,7 +138,10 @@ impl<'a> DebugInfo<'a> { if !pdb_path.exists() && !not_found_full_name_cache.contains(cv_info_pdb70.file_name()) { - info!("Downloading PDB file from {}", pdb_url); + info!( + get_object("tsffs")?, + "Downloading PDB file from {}", pdb_url + ); match get(&pdb_url)?.error_for_status() { Ok(response) => { let mut file = File::create(&pdb_path)?; @@ -177,7 +183,10 @@ impl<'a> DebugInfo<'a> { P: AsRef, { if let Some(info) = user_debug_info.get(name) { - debug!("Have user-provided debug info for {name}"); + debug!( + get_object("tsffs")?, + "Have user-provided debug info for {name}" + ); let exe_path = info[0].clone(); let pdb_path = info[1].clone(); @@ -257,7 +266,7 @@ impl<'a> DebugInfo<'a> { .join(format!("{}.exe", &exe_guid)); if !exe_path.exists() && !not_found_full_name_cache.contains(name) { - info!("Downloading PE file from {}", exe_url); + info!(get_object("tsffs")?, "Downloading PE file from {}", exe_url); match get(&exe_url)?.error_for_status() { Ok(response) => { let mut file = File::create(&exe_path)?; @@ -279,7 +288,10 @@ impl<'a> DebugInfo<'a> { if !pdb_path.exists() && !not_found_full_name_cache.contains(cv_info_pdb70.file_name()) { - info!("Downloading PDB file from {}", pdb_url); + info!( + get_object("tsffs")?, + "Downloading PDB file from {}", pdb_url + ); match get(&pdb_url)?.error_for_status() { Ok(response) => { let mut file = File::create(&pdb_path)?; diff --git a/src/os/windows/kernel.rs b/src/os/windows/kernel.rs index 7cb08aad..88735b13 100644 --- a/src/os/windows/kernel.rs +++ b/src/os/windows/kernel.rs @@ -5,7 +5,7 @@ use std::{ use anyhow::{anyhow, bail, Result}; use pdb::{FallibleIterator, SymbolData}; -use simics::{debug, get_attribute, ConfObject}; +use simics::{debug, get_attribute, get_object, ConfObject}; use vergilius::bindings::*; use windows::Win32::System::{ Diagnostics::Debug::{IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_NT_HEADERS64}, @@ -55,7 +55,7 @@ pub fn page_is_kernel(processor: *mut ConfObject, address: u64) -> Result ); if nt_header.FileHeader.SizeOfOptionalHeader == 0 { - debug!("Optional header size was 0"); + debug!(get_object("tsffs")?, "Optional header size was 0"); return Ok(false); } @@ -74,7 +74,7 @@ pub fn page_is_kernel(processor: *mut ConfObject, address: u64) -> Result let image_size = nt_header.OptionalHeader.SizeOfImage as u64; - debug!("Image size is {:#x}", image_size); + debug!(get_object("tsffs")?, "Image size is {:#x}", image_size); let export_header_offset = nt_header.OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_EXPORT.0 as usize] @@ -108,7 +108,7 @@ pub fn page_is_kernel(processor: *mut ConfObject, address: u64) -> Result let name = read_nul_terminated_string(processor, address + export_directory.Name as u64)?; - debug!("Read image name {}", name); + debug!(get_object("tsffs")?, "Read image name {}", name); if name == "ntoskrnl.exe" { return Ok(true); @@ -147,11 +147,14 @@ pub fn find_kernel_with_idt(processor: *mut ConfObject, build: u32) -> Result() as u64), )?; if !idtr_entry0.present() { - debug!("Entry {} not present, skipping", i); + debug!(get_object("tsffs")?, "Entry {} not present, skipping", i); continue; } let idtr_entry0_offset = idtr_entry0.offset(); - debug!("Got valid IDT entry with offset {:#x}", idtr_entry0_offset); + debug!( + get_object("tsffs")?, + "Got valid IDT entry with offset {:#x}", idtr_entry0_offset + ); return find_kernel( processor, idtr_entry0_offset, @@ -261,7 +264,10 @@ impl KernelInfo { P: AsRef, { let list_address = self.find_ps_loaded_module_list_address()?; - debug!("PsLoadedModuleList: {:#x}", list_address); + debug!( + get_object("tsffs")?, + "PsLoadedModuleList: {:#x}", list_address + ); let list = read_virtual::(processor, list_address)?; let mut modules = Vec::new(); diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 024fd216..9edc3941 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -5,7 +5,7 @@ use intervaltree::IntervalTree; use kernel::{find_kernel_with_idt, KernelInfo}; use raw_cstr::AsRawCstr; use simics::{ - get_interface, get_processor_number, sys::cpu_cb_handle_t, ConfObject, + get_interface, get_object, get_processor_number, info, sys::cpu_cb_handle_t, ConfObject, CpuInstrumentationSubscribeInterface, IntRegisterInterface, ProcessorInfoV2Interface, }; use std::{ @@ -73,10 +73,12 @@ impl WindowsOsInfo { where P: AsRef, { + info!(get_object("tsffs")?, "Collecting Windows OS information"); let processor_nr = get_processor_number(processor)?; let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(processor)?; if self.kernel_info.is_none() { + info!(get_object("tsffs")?, "Collecting kernel information"); // Make sure we're running 64-bit Windows ensure!( processor_info_v2.get_logical_address_width()? == 64, @@ -100,6 +102,8 @@ impl WindowsOsInfo { let _ = WindowsKpcr::new(processor, maj, min, build)?; let kernel_base = find_kernel_with_idt(processor, build)?; + info!(get_object("tsffs")?, "Found kernel base {kernel_base:#x}"); + self.kernel_info = Some(KernelInfo::new( processor, "ntoskrnl.exe", @@ -110,6 +114,8 @@ impl WindowsOsInfo { )?); } + info!(get_object("tsffs")?, "Collecting process list"); + self.processes.insert( processor_nr, self.kernel_info @@ -123,6 +129,8 @@ impl WindowsOsInfo { )?, ); + info!(get_object("tsffs")?, "Collecting module list"); + self.modules.insert( processor_nr, self.kernel_info @@ -179,41 +187,39 @@ impl WindowsOsInfo { } } -#[ffi(expect, self_ty = "*mut std::ffi::c_void")] -impl Tsffs { - #[ffi(arg(rest), arg(self))] - pub fn on_instruction_before_symcov( - &mut self, - _obj: *mut ConfObject, - cpu: *mut ConfObject, - _handle: *mut simics::sys::instruction_handle_t, - ) -> Result<()> { - let cpu_nr = get_processor_number(cpu)?; - - Ok(()) - } -} - impl Tsffs { pub fn on_control_register_write_windows_symcov( &mut self, trigger_obj: *mut ConfObject, register_nr: i64, - _value: i64, + value: i64, ) -> Result<()> { let mut int_register: IntRegisterInterface = get_interface(trigger_obj)?; + let processor_nr = get_processor_number(trigger_obj)?; - // Check if the register was CR3 - if self.windows + if self.processors.contains_key(&processor_nr) + && self.coverage_enabled + && self.windows && self.symbolic_coverage && register_nr == int_register.get_number("cr3".as_raw_cstr()?)? as i64 + && self + .cr3_cache + .get(&processor_nr) + .is_some_and(|v| *v != value) { + info!( + get_object("tsffs")?, + "Got write {value:#x} to CR3 for processor {processor_nr}, refreshing kernel & process mappings" + ); + self.windows_os_info.collect( trigger_obj, &self.debuginfo_download_directory, self.guess_pdb_function_size, &self.debug_info, )?; + + self.cr3_cache.insert(processor_nr, value); } Ok(()) diff --git a/src/os/windows/structs.rs b/src/os/windows/structs.rs index cfed87bd..6eca1a66 100644 --- a/src/os/windows/structs.rs +++ b/src/os/windows/structs.rs @@ -6,7 +6,7 @@ use std::{ use anyhow::{anyhow, bail, ensure, Result}; use raw_cstr::AsRawCstr; -use simics::{debug, get_attribute, get_interface, ConfObject, IntRegisterInterface}; +use simics::{debug, get_attribute, get_interface, get_object, ConfObject, IntRegisterInterface}; use vergilius::bindings::*; use windows::Win32::{Foundation::UNICODE_STRING, System::Kernel::LIST_ENTRY}; @@ -68,12 +68,8 @@ impl WindowsKpcr { let ia32_kernel_gs_base = int_register.read(ia32_kernel_gs_base_nr)?; let ia32_gs_base = int_register.read(ia32_gs_base_nr)?; let sim_idtr_base: u64 = get_attribute(processor, "idtr_base")?.try_into()?; - debug!("Got SIM IDTR Base {:#x}", sim_idtr_base); let kpcr_address = max(ia32_gs_base, ia32_kernel_gs_base); - debug!("Got KPCR address {:#x}", kpcr_address); - - debug!("Initializing KPCR for Windows {}.{}.{}", maj, min, build); match (maj, min, build) { (10, 0, 10240) => { @@ -370,8 +366,6 @@ impl WindowsKprcb { build: u32, kpcrb_address: u64, ) -> Result { - debug!("Initializing KPRCB for Windows {}.{}.{}", maj, min, build); - match (maj, min, build) { (10, 0, 10240) => { let kprcb = @@ -2326,7 +2320,6 @@ impl WindowsTeb { WindowsTeb::Windows10_0_22621_382 { teb } => teb.ProcessEnvironmentBlock as u64, WindowsTeb::Windows10_0_22631_2428 { teb } => teb.ProcessEnvironmentBlock as u64, }; - debug!("peb_address: {:#x}", peb_address); WindowsPeb::new(processor, major, minor, build, peb_address) } } @@ -2762,7 +2755,6 @@ impl WindowsEProcess { WindowsEProcess::Windows10_0_22621_382 { eprocess } => eprocess.Pcb.DirectoryTableBase, WindowsEProcess::Windows10_0_22631_2428 { eprocess } => eprocess.Pcb.DirectoryTableBase, }; - debug!("Using directory table base {:#x}", directory_table_base); if directory_table_base == 0 { directory_table_base = match self { @@ -2806,12 +2798,10 @@ impl WindowsEProcess { eprocess.Pcb.UserDirectoryTableBase } }; - debug!("Invalid DTB, using user DTB: {:#x}", directory_table_base); } let mut modules = Vec::new(); - debug!("PEB ADDRESS: {:x}", peb_address); if peb_address != 0 { let peb = WindowsPeb::new_dtb( processor, From da469d6e46023a1876353f4b8a7b3fba70abf697 Mon Sep 17 00:00:00 2001 From: novafacing Date: Wed, 26 Jun 2024 09:47:53 -0700 Subject: [PATCH 03/14] Use rustls to avoid openssl dependency --- Cargo.toml | 9 +++++++-- src/os/windows/debug_info.rs | 8 +++++++- src/tracer/mod.rs | 15 +++++++++++---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 96e24f53..4d8152fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,12 +86,17 @@ windows = { version = "0.57.0", features = [ "Win32_System_SystemInformation", "Win32_System_Kernel", ] } -reqwest = { version = "0.12.5", features = ["blocking"] } +reqwest = { version = "0.12.5", features = [ + "blocking", + # NOTE: rustls is used because native-tls does not build with the + # compatibility builder and we don't need any advanced features + "rustls-tls", +], default-features = false } pdb = "0.8.0" intervaltree = "0.2.7" lending-iterator = "0.1.7" rustc-demangle = "0.1.24" -symbolic = { version = "12.9.2", features = ["demangle"] } +cpp_demangle = "0.4.3" [dev-dependencies] simics-test = "0.1.0" diff --git a/src/os/windows/debug_info.rs b/src/os/windows/debug_info.rs index b779446e..6cb41bc2 100644 --- a/src/os/windows/debug_info.rs +++ b/src/os/windows/debug_info.rs @@ -360,6 +360,7 @@ impl ProcessModule { if let Some(rva) = p.offset.to_rva(&address_map) { let info = SymbolInfo::new( rva.0 as u64, + self.base, 0, p.name.to_string().to_string(), self.full_name.clone(), @@ -372,6 +373,7 @@ impl ProcessModule { if let Some(rva) = p.offset.to_rva(&address_map) { let info = SymbolInfo::new( rva.0 as u64, + self.base, p.len as u64, p.name.to_string().to_string(), self.full_name.clone(), @@ -422,15 +424,17 @@ pub struct Process { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SymbolInfo { pub rva: u64, + pub base: u64, pub size: u64, pub name: String, pub module: String, } impl SymbolInfo { - pub fn new(rva: u64, size: u64, name: String, module: String) -> Self { + pub fn new(rva: u64, base: u64, size: u64, name: String, module: String) -> Self { Self { rva, + base, size, name, module, @@ -470,6 +474,7 @@ impl Module { if let Some(rva) = p.offset.to_rva(&address_map) { let info = SymbolInfo::new( rva.0 as u64, + self.base, 1, p.name.to_string().to_string(), self.full_name.clone(), @@ -482,6 +487,7 @@ impl Module { if let Some(rva) = p.offset.to_rva(&address_map) { let info = SymbolInfo::new( rva.0 as u64, + self.base, p.len as u64, p.name.to_string().to_string(), self.full_name.clone(), diff --git a/src/tracer/mod.rs b/src/tracer/mod.rs index b8a5996e..978bc72f 100644 --- a/src/tracer/mod.rs +++ b/src/tracer/mod.rs @@ -2,10 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::{anyhow, bail, Error, Result}; +use cpp_demangle::{DemangleOptions, Symbol}; use ffi::ffi; use libafl::prelude::CmpValues; use libafl_bolts::{AsMutSlice, AsSlice}; use libafl_targets::{AFLppCmpLogOperands, AFL_CMP_TYPE_INS, CMPLOG_MAP_H}; +use rustc_demangle::try_demangle; use serde::{Deserialize, Serialize}; use simics::{ api::{ @@ -18,7 +20,6 @@ use std::{ collections::HashMap, ffi::c_void, fmt::Display, hash::Hash, num::Wrapping, slice::from_raw_parts, str::FromStr, }; -use symbolic::demangle::Demangle; use typed_builder::TypedBuilder; use crate::{arch::ArchitectureOperations, Tsffs}; @@ -397,9 +398,15 @@ impl Tsffs { .get(&processor_number) .and_then(|lookup_tree| { lookup_tree.query(pc..pc + 1).next().map(|q| { - let offset = pc - q.range.start; - let symbol_demangled = symbolic::common::Name::from(&q.value.name) - .demangle(symbolic::demangle::DemangleOptions::complete()); + let offset = pc - q.value.base + q.value.rva; + let symbol_demangled = try_demangle(&q.value.name) + .map(|d| d.to_string()) + .ok() + .or_else(|| { + Symbol::new(&q.value.name) + .ok() + .and_then(|s| s.demangle(&DemangleOptions::new()).ok()) + }); ExecutionTraceSymbol { symbol: q.value.name.clone(), symbol_demangled, From d0207a485ca5495ae41c95638eac20496645de5f Mon Sep 17 00:00:00 2001 From: novafacing Date: Wed, 10 Jul 2024 14:59:02 -0700 Subject: [PATCH 04/14] Add internal dockerfile major version switch and option to disable symbolic system-wide coverage for user-provided only --- ...kerfile-internal => Dockerfile-internal-6} | 4 +- .github/builder/Dockerfile-internal-7 | 65 +++++++++++++++++++ .github/builder/common.sh | 5 +- Cargo.toml | 2 +- scripts/build-internal.sh | 61 +++++++++++------ src/haps/mod.rs | 16 ++++- src/lib.rs | 8 +++ src/os/mod.rs | 8 +++ src/os/windows/debug_info.rs | 10 +-- src/os/windows/kernel.rs | 23 ++++--- src/os/windows/mod.rs | 15 +++-- src/os/windows/structs.rs | 9 ++- 12 files changed, 176 insertions(+), 50 deletions(-) rename .github/builder/{Dockerfile-internal => Dockerfile-internal-6} (89%) create mode 100644 .github/builder/Dockerfile-internal-7 diff --git a/.github/builder/Dockerfile-internal b/.github/builder/Dockerfile-internal-6 similarity index 89% rename from .github/builder/Dockerfile-internal rename to .github/builder/Dockerfile-internal-6 index db606595..ec4f0d0d 100644 --- a/.github/builder/Dockerfile-internal +++ b/.github/builder/Dockerfile-internal-6 @@ -12,7 +12,7 @@ COPY .github/builder/rsrc/lld-5.0.2.src.tar.xz /install/lld-5.0.2.src.tar.xz COPY .github/builder/rsrc/cfe-5.0.2.src.tar.xz /install/cfe-5.0.2.src.tar.xz COPY .github/builder/rsrc/llvm-5.0.2.src.tar.xz /install/llvm-5.0.2.src.tar.xz COPY .github/builder/rsrc/rpms /install/rpms -COPY .github/builder/rsrc/simics /simics +COPY .github/builder/rsrc/simics-6 /simics COPY .github/builder/rsrc/ispm /simics/ispm RUN yum -y install /install/rpms/*.rpm && yum clean all @@ -58,7 +58,7 @@ RUN ispm settings install-dir /simics && \ RUN RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=$(which ld.lld)" && \ export RUSTFLAGS && \ cargo install cargo-simics-build && \ - SIMICS_PACKAGE_VERSION="$(ispm packages --list | grep 1000 | awk '{print $3}' | cut -d '.' -f1).$(grep -E '^version = ' < Cargo.toml | sed -n '$p' | grep -oE '\"[^\"]+\"' | tr -d '\"' | cut -d'.' -f2-)" && \ + SIMICS_PACKAGE_VERSION="$(ispm packages --list-installed | grep 1000 | awk '{print $3}' | cut -d '.' -f1).$(grep -E '^version = ' < Cargo.toml | sed -n '$p' | grep -oE '\"[^\"]+\"' | tr -d '\"' | cut -d'.' -f2-)" && \ export SIMICS_PACKAGE_VERSION && \ cargo simics-build -r && \ mkdir -p /packages-internal && \ diff --git a/.github/builder/Dockerfile-internal-7 b/.github/builder/Dockerfile-internal-7 new file mode 100644 index 00000000..f4f716fe --- /dev/null +++ b/.github/builder/Dockerfile-internal-7 @@ -0,0 +1,65 @@ +# hadolint global ignore=DL3033,SC3044 +FROM fedora:20 + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +ENV PATH="${PATH}:/root/.cargo/bin/" + +COPY .github/builder/rsrc/rust-nightly-x86_64-unknown-linux-gnu.tar.xz /install/rust-nightly-x86_64-unknown-linux-gnu.tar.xz +COPY .github/builder/rsrc/make-4.4.1.tar.gz /install/make-4.4.1.tar.gz +COPY .github/builder/rsrc/cmake-3.29.3-linux-x86_64.tar.gz /install/cmake-3.29.3-linux-x86_64.tar.gz +COPY .github/builder/rsrc/lld-5.0.2.src.tar.xz /install/lld-5.0.2.src.tar.xz +COPY .github/builder/rsrc/cfe-5.0.2.src.tar.xz /install/cfe-5.0.2.src.tar.xz +COPY .github/builder/rsrc/llvm-5.0.2.src.tar.xz /install/llvm-5.0.2.src.tar.xz +COPY .github/builder/rsrc/rpms /install/rpms +COPY .github/builder/rsrc/simics-7 /simics +COPY .github/builder/rsrc/ispm /simics/ispm + +RUN yum -y install /install/rpms/*.rpm && yum clean all + +RUN tar -C /install -xvf /install/rust-nightly-x86_64-unknown-linux-gnu.tar.xz && \ + /install/rust-nightly-x86_64-unknown-linux-gnu/install.sh && \ + mkdir -p /make && \ + tar -C /make --strip-components=1 -xf /install/make-4.4.1.tar.gz && \ + pushd /make && \ + ./configure && \ + make && \ + make install && \ + make clean && \ + popd && \ + tar -C /usr/local/ --strip-components=1 -xf /install/cmake-3.29.3-linux-x86_64.tar.gz && \ + mkdir -p /llvm/tools/clang && \ + mkdir -p /llvm/tools/lld && \ + tar -C /llvm --strip-components=1 -xf /install/llvm-5.0.2.src.tar.xz && \ + tar -C /llvm/tools/clang --strip-components=1 -xf /install/cfe-5.0.2.src.tar.xz && \ + tar -C /llvm/tools/lld --strip-components=1 -xf /install/lld-5.0.2.src.tar.xz && \ + mkdir -p /llvm/build && \ + pushd /llvm/build && \ + cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE="MinSizeRel" -DLLVM_TARGETS_TO_BUILD="X86" .. && \ + make -j "$(nproc)" && \ + make install && \ + make clean && \ + rm -rf /llvm/build/ && \ + popd && \ + rm -rf /make /llvm + +WORKDIR / + +ENV PATH="${PATH}:/simics/ispm/" +ENV PATH="${PATH}:/root/.cargo/bin/" + +COPY . /tsffs/ + +WORKDIR /tsffs/ + +RUN ispm settings install-dir /simics && \ + ispm packages --list + +RUN RUSTFLAGS="-C linker=clang -C link-arg=-fuse-ld=$(which ld.lld)" && \ + export RUSTFLAGS && \ + cargo install cargo-simics-build && \ + SIMICS_PACKAGE_VERSION="$(ispm packages --list-installed | grep 1000 | awk '{print $3}' | cut -d '.' -f1).$(grep -E '^version = ' < Cargo.toml | sed -n '$p' | grep -oE '\"[^\"]+\"' | tr -d '\"' | cut -d'.' -f2-)" && \ + export SIMICS_PACKAGE_VERSION && \ + cargo simics-build -r && \ + mkdir -p /packages-internal && \ + cp target/release/*.ispm /packages-internal diff --git a/.github/builder/common.sh b/.github/builder/common.sh index b096683e..ef954800 100755 --- a/.github/builder/common.sh +++ b/.github/builder/common.sh @@ -1,6 +1,7 @@ #!/bin/bash -# NOTE: Do not just copy-paste scripts/build.sh! +# NOTE: The `use-keyboxd` option in ~/.gnupg/common.conf should be disabled for this script's GPG routines to +# work correctly! set -e @@ -233,4 +234,4 @@ download_and_verify_builder_deps() { download_and_verify_cmake download_and_verify_simics download_and_verify_builder_rpms -} \ No newline at end of file +} diff --git a/Cargo.toml b/Cargo.toml index 4d8152fb..d9df67b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ version = "0.2.2" [package.metadata.simics] package-number = 31337 -version = "6.1.4" +version = "6.1.5" [lib] crate-type = ["cdylib", "rlib"] diff --git a/scripts/build-internal.sh b/scripts/build-internal.sh index f864be9b..83ef5a6b 100755 --- a/scripts/build-internal.sh +++ b/scripts/build-internal.sh @@ -11,6 +11,12 @@ set -e +MAJOR_VERSION="${1}" + +if [ -z "${MAJOR_VERSION}" ]; then + MAJOR_VERSION="7" +fi + SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) ROOT_DIR="${SCRIPT_DIR}/../" BUILDER_DIR="${ROOT_DIR}/.github/builder/" @@ -31,32 +37,47 @@ if [ ! -d "${BUILDER_DIR}/rsrc/ispm" ]; then cp -a "${ISPM_DIR}" "${BUILDER_DIR}/rsrc/ispm" fi -if [ ! -d "${BUILDER_DIR}/rsrc/simics" ]; then - echo "Simics packages not found. Installing..." - mkdir -p "${BUILDER_DIR}/rsrc/simics" - ispm packages --install-dir "${BUILDER_DIR}/rsrc/simics" -i \ - 1000-latest \ - 1020-latest \ - 1030-latest \ - 1031-latest \ - 2050-latest \ - 2053-latest \ - 2096-latest \ - 4094-latest \ - 6010-latest \ - 7801-latest \ - 8112-latest \ - 8126-latest \ - 8144-latest \ +install_major_version() { + WANTED_MAJOR_VERSION="${1}" + ispm packages --install-dir "${BUILDER_DIR}/rsrc/simics-${WANTED_MAJOR_VERSION}" -i \ + "1000-${WANTED_MAJOR_VERSION}.latest" \ + "1020-${WANTED_MAJOR_VERSION}.latest" \ + "1030-${WANTED_MAJOR_VERSION}.latest" \ + "1031-${WANTED_MAJOR_VERSION}.latest" \ + "2050-${WANTED_MAJOR_VERSION}.latest" \ + "2053-${WANTED_MAJOR_VERSION}.latest" \ + "2096-${WANTED_MAJOR_VERSION}.latest" \ + "4094-${WANTED_MAJOR_VERSION}.latest" \ + "6010-${WANTED_MAJOR_VERSION}.latest" \ + "7801-${WANTED_MAJOR_VERSION}.latest" \ + "8112-${WANTED_MAJOR_VERSION}.latest" \ + "8126-${WANTED_MAJOR_VERSION}.latest" \ + "8144-${WANTED_MAJOR_VERSION}.latest" \ --non-interactive -fi +} download_and_verify_builder_deps +if [ "${MAJOR_VERSION}" -eq "7" ]; then + DOCKERFILE="${BUILDER_DIR}/Dockerfile-internal-7" + if [ ! -d "${BUILDER_DIR}/rsrc/simics-7" ]; then + echo "Simics 7 packages not found. Installing..." + install_major_version 7 + fi +fi + +if [ "${MAJOR_VERSION}" -eq "6" ]; then + DOCKERFILE="${BUILDER_DIR}/Dockerfile-internal-6" + if [ ! -d "${BUILDER_DIR}/rsrc/simics-6" ]; then + echo "Simics 6 packages not found. Installing..." + install_major_version 6 + fi +fi + unset SIMICS_BASE docker build \ - -t "${IMAGE_NAME}" -f "${BUILDER_DIR}/Dockerfile-internal" "${ROOT_DIR}" + -t "${IMAGE_NAME}" -f "${DOCKERFILE}" "${ROOT_DIR}" docker create --name "${CONTAINER_NAME}" "${IMAGE_NAME}" bash mkdir -p "${ROOT_DIR}/packages-internal" docker cp "${CONTAINER_NAME}:/packages-internal" "${ROOT_DIR}/" -docker rm -f "${CONTAINER_NAME}" +docker rm -f "${CONTAINER_NAME}" \ No newline at end of file diff --git a/src/haps/mod.rs b/src/haps/mod.rs index 7cb924da..73be744a 100644 --- a/src/haps/mod.rs +++ b/src/haps/mod.rs @@ -8,6 +8,7 @@ use std::time::SystemTime; use crate::{ arch::ArchitectureOperations, magic::MagicNumber, + os::DebugInfoConfig, state::{SolutionKind, StopReason}, ManualStartInfo, Tsffs, }; @@ -62,7 +63,10 @@ impl Tsffs { start_processor_raw, &self.debuginfo_download_directory, self.guess_pdb_function_size, - &self.debug_info, + DebugInfoConfig { + system: self.symbolic_coverage_system, + user_debug_info: &self.debug_info, + }, )?; } self.get_and_write_testcase()?; @@ -222,7 +226,10 @@ impl Tsffs { processor, &self.debuginfo_download_directory, self.guess_pdb_function_size, - &self.debug_info, + DebugInfoConfig { + system: self.symbolic_coverage_system, + user_debug_info: &self.debug_info, + }, )?; } @@ -265,7 +272,10 @@ impl Tsffs { processor, &self.debuginfo_download_directory, self.guess_pdb_function_size, - &self.debug_info, + DebugInfoConfig { + system: self.symbolic_coverage_system, + user_debug_info: &self.debug_info, + }, )?; } diff --git a/src/lib.rs b/src/lib.rs index fcf11248..a2a7725f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -415,6 +415,14 @@ pub(crate) struct Tsffs { /// to a tuple of (exe path, debuginfo path) where debuginfo is either a PDB or DWARF /// file pub debug_info: HashMap>, + #[class(attribute(optional, default = lookup_file("%simics%")?.join("debuginfo-source")))] + /// Directory in which source files are located. Source files do not need to be arranged in + /// the same directory structure as the compiled source, and are looked up by hash. + pub debuginfo_source_directory: PathBuf, + #[class(attribute(optional, default = false))] + /// Whether symbolic coverage should be collected for system components by downloading + /// executable and debug info files where possible. + pub symbolic_coverage_system: bool, /// Handle for the core simulation stopped hap stop_hap_handle: HapHandle, diff --git a/src/os/mod.rs b/src/os/mod.rs index 063ffbf2..2240edad 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -1,3 +1,11 @@ #![allow(unused)] +use std::{collections::HashMap, path::PathBuf}; + pub mod windows; + +#[derive(Debug, Clone)] +pub struct DebugInfoConfig<'a> { + pub system: bool, + pub user_debug_info: &'a HashMap>, +} diff --git a/src/os/windows/debug_info.rs b/src/os/windows/debug_info.rs index 6cb41bc2..dc1647ac 100644 --- a/src/os/windows/debug_info.rs +++ b/src/os/windows/debug_info.rs @@ -20,6 +20,8 @@ use windows::Win32::System::{ SystemServices::IMAGE_DOS_HEADER, }; +use crate::os::DebugInfoConfig; + use super::{ pdb::{CvInfoPdb70, Export}, util::{read_virtual, read_virtual_dtb}, @@ -40,12 +42,12 @@ impl<'a> DebugInfo<'a> { base: u64, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: &HashMap>, + user_debug_info: DebugInfoConfig, ) -> Result where P: AsRef, { - if let Some(info) = user_debug_info.get(name) { + if let Some(info) = user_debug_info.user_debug_info.get(name) { debug!( get_object("tsffs")?, "Have user-provided debug info for {name}" @@ -177,12 +179,12 @@ impl<'a> DebugInfo<'a> { download_directory: P, directory_table_base: u64, not_found_full_name_cache: &mut HashSet, - user_debug_info: &HashMap>, + user_debug_info: DebugInfoConfig, ) -> Result where P: AsRef, { - if let Some(info) = user_debug_info.get(name) { + if let Some(info) = user_debug_info.user_debug_info.get(name) { debug!( get_object("tsffs")?, "Have user-provided debug info for {name}" diff --git a/src/os/windows/kernel.rs b/src/os/windows/kernel.rs index 88735b13..b91745e2 100644 --- a/src/os/windows/kernel.rs +++ b/src/os/windows/kernel.rs @@ -15,10 +15,13 @@ use windows::Win32::System::{ }, }; -use crate::os::windows::{ - debug_info::DebugInfo, - idt::IdtEntry64, - util::{read_nul_terminated_string, read_unicode_string, read_virtual}, +use crate::os::{ + windows::{ + debug_info::DebugInfo, + idt::IdtEntry64, + util::{read_nul_terminated_string, read_unicode_string, read_virtual}, + }, + DebugInfoConfig, }; use super::{ @@ -181,7 +184,7 @@ impl KernelInfo { base: u64, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: &HashMap>, + user_debug_info: DebugInfoConfig, ) -> Result where P: AsRef, @@ -258,7 +261,7 @@ impl KernelInfo { processor: *mut ConfObject, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: &HashMap>, + user_debug_info: DebugInfoConfig, ) -> Result> where P: AsRef, @@ -313,7 +316,7 @@ impl KernelInfo { base, download_directory.as_ref(), not_found_full_name_cache, - user_debug_info, + user_debug_info.clone(), ) }) .ok(); @@ -382,7 +385,7 @@ impl KernelInfo { processor: *mut ConfObject, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: &HashMap>, + user_debug_info: DebugInfoConfig, ) -> Result where P: AsRef, @@ -427,7 +430,7 @@ impl KernelInfo { processor: *mut ConfObject, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: &HashMap>, + user_debug_info: DebugInfoConfig, ) -> Result> where P: AsRef, @@ -477,7 +480,7 @@ impl KernelInfo { self.build, download_directory.as_ref(), not_found_full_name_cache, - user_debug_info, + user_debug_info.clone(), ) .unwrap_or_default(), }); diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 9edc3941..3c471d7d 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -20,6 +20,8 @@ use vergilius::bindings::*; use crate::Tsffs; +use super::DebugInfoConfig; + pub mod debug_info; pub mod idt; pub mod kernel; @@ -68,7 +70,7 @@ impl WindowsOsInfo { processor: *mut ConfObject, download_directory: P, guess_pdb_function_size: bool, - user_debug_info: &HashMap>, + user_debug_info: DebugInfoConfig, ) -> Result<()> where P: AsRef, @@ -110,7 +112,7 @@ impl WindowsOsInfo { kernel_base, download_directory.as_ref(), &mut self.not_found_full_name_cache, - user_debug_info, + user_debug_info.clone(), )?); } @@ -125,7 +127,7 @@ impl WindowsOsInfo { processor, download_directory.as_ref(), &mut self.not_found_full_name_cache, - user_debug_info, + user_debug_info.clone(), )?, ); @@ -140,7 +142,7 @@ impl WindowsOsInfo { processor, download_directory.as_ref(), &mut self.not_found_full_name_cache, - user_debug_info, + user_debug_info.clone(), )?, ); @@ -216,7 +218,10 @@ impl Tsffs { trigger_obj, &self.debuginfo_download_directory, self.guess_pdb_function_size, - &self.debug_info, + DebugInfoConfig { + system: self.symbolic_coverage_system, + user_debug_info: &self.debug_info, + }, )?; self.cr3_cache.insert(processor_nr, value); diff --git a/src/os/windows/structs.rs b/src/os/windows/structs.rs index 6eca1a66..ce64131e 100644 --- a/src/os/windows/structs.rs +++ b/src/os/windows/structs.rs @@ -10,7 +10,10 @@ use simics::{debug, get_attribute, get_interface, get_object, ConfObject, IntReg use vergilius::bindings::*; use windows::Win32::{Foundation::UNICODE_STRING, System::Kernel::LIST_ENTRY}; -use crate::os::windows::{debug_info::DebugInfo, util::read_virtual}; +use crate::os::{ + windows::{debug_info::DebugInfo, util::read_virtual}, + DebugInfoConfig, +}; use super::{ debug_info::ProcessModule, @@ -2718,7 +2721,7 @@ impl WindowsEProcess { build: u32, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: &HashMap>, + user_debug_info: DebugInfoConfig, ) -> Result> where P: AsRef, @@ -2849,7 +2852,7 @@ impl WindowsEProcess { base, download_directory.as_ref(), not_found_full_name_cache, - user_debug_info, + user_debug_info.clone(), ) }) .ok(); From 9223660fcf218d39447713b432326e89787d0d47 Mon Sep 17 00:00:00 2001 From: novafacing Date: Tue, 16 Jul 2024 17:41:45 -0700 Subject: [PATCH 05/14] Add HTML coverage viewer and LCOV output format --- Cargo.toml | 7 + src/haps/mod.rs | 6 +- src/lib.rs | 9 +- src/os/windows/debug_info.rs | 380 +++++---- src/os/windows/kernel.rs | 13 +- src/os/windows/mod.rs | 10 +- src/os/windows/structs.rs | 3 +- src/source_cov/html.rs | 448 ++++++++++ src/source_cov/lcov.rs | 773 ++++++++++++++++++ src/source_cov/mod.rs | 137 ++++ tests/rsrc/test-lcov/html/home/index.html | 1 + .../test-lcov/html/home/rhart/hub/index.html | 1 + .../html/home/rhart/hub/tsffs/index.html | 1 + .../home/rhart/hub/tsffs/tests/index.html | 1 + .../rhart/hub/tsffs/tests/rsrc/index.html | 1 + .../hub/tsffs/tests/rsrc/test-lcov/index.html | 1 + .../tests/rsrc/test-lcov/subdir1/index.html | 1 + .../tests/rsrc/test-lcov/subdir1/test.c.html | 1 + .../tests/rsrc/test-lcov/subdir2/index.html | 1 + .../test-lcov/subdir2/test-subdir2.c.html | 1 + .../tsffs/tests/rsrc/test-lcov/test.c.html | 1 + .../tsffs/tests/rsrc/test-lcov/test2.c.html | 1 + .../rsrc/test-lcov/html/home/rhart/index.html | 1 + tests/rsrc/test-lcov/html/index.html | 1 + tests/rsrc/test-lcov/subdir1/test.c | 16 + tests/rsrc/test-lcov/subdir2/test-subdir2.c | 16 + tests/rsrc/test-lcov/test.c | 16 + tests/rsrc/test-lcov/test2.c | 3 + 28 files changed, 1692 insertions(+), 159 deletions(-) create mode 100644 src/source_cov/html.rs create mode 100644 src/source_cov/lcov.rs create mode 100644 src/source_cov/mod.rs create mode 100644 tests/rsrc/test-lcov/html/home/index.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/index.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/index.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/index.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/index.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/index.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir1/index.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir1/test.c.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir2/index.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir2/test-subdir2.c.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/test.c.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/test2.c.html create mode 100644 tests/rsrc/test-lcov/html/home/rhart/index.html create mode 100644 tests/rsrc/test-lcov/html/index.html create mode 100644 tests/rsrc/test-lcov/subdir1/test.c create mode 100644 tests/rsrc/test-lcov/subdir2/test-subdir2.c create mode 100644 tests/rsrc/test-lcov/test.c create mode 100644 tests/rsrc/test-lcov/test2.c diff --git a/Cargo.toml b/Cargo.toml index d9df67b4..f0b7ecd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,13 @@ intervaltree = "0.2.7" lending-iterator = "0.1.7" rustc-demangle = "0.1.24" cpp_demangle = "0.4.3" +walkdir = "2.5.0" +md5 = "0.7.0" +sha1 = "0.10.6" +sha2 = "0.10.8" +typed-path = "0.9.0" +markup = "0.15.0" +petgraph = "0.6.5" [dev-dependencies] simics-test = "0.1.0" diff --git a/src/haps/mod.rs b/src/haps/mod.rs index 73be744a..6b92cce2 100644 --- a/src/haps/mod.rs +++ b/src/haps/mod.rs @@ -62,11 +62,11 @@ impl Tsffs { self.windows_os_info.collect( start_processor_raw, &self.debuginfo_download_directory, - self.guess_pdb_function_size, DebugInfoConfig { system: self.symbolic_coverage_system, user_debug_info: &self.debug_info, }, + &self.source_file_cache, )?; } self.get_and_write_testcase()?; @@ -225,11 +225,11 @@ impl Tsffs { self.windows_os_info.collect( processor, &self.debuginfo_download_directory, - self.guess_pdb_function_size, DebugInfoConfig { system: self.symbolic_coverage_system, user_debug_info: &self.debug_info, }, + &self.source_file_cache, )?; } @@ -271,11 +271,11 @@ impl Tsffs { self.windows_os_info.collect( processor, &self.debuginfo_download_directory, - self.guess_pdb_function_size, DebugInfoConfig { system: self.symbolic_coverage_system, user_debug_info: &self.debug_info, }, + &self.source_file_cache, )?; } diff --git a/src/lib.rs b/src/lib.rs index a2a7725f..7280b38e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,6 +63,7 @@ use simics::{ // which is necessary because this module is compatible with base versions which cross the // deprecation boundary use simics::{restore_snapshot, save_snapshot}; +use source_cov::{Coverage, SourceCache}; use state::StopReason; use std::{ alloc::{alloc_zeroed, Layout}, @@ -91,6 +92,7 @@ pub(crate) mod interfaces; pub(crate) mod log; pub(crate) mod magic; pub(crate) mod os; +pub(crate) mod source_cov; pub(crate) mod state; pub(crate) mod tracer; pub(crate) mod traits; @@ -407,9 +409,6 @@ pub(crate) struct Tsffs { #[class(attribute(optional, default = lookup_file("%simics%")?.join("debuginfo-cache")))] /// Directory in which to download PDB and EXE files from symbol servers on Windows pub debuginfo_download_directory: PathBuf, - #[class(attribute(optional, default = true))] - /// Whether to guess the size of each function provided by a PDB file - pub guess_pdb_function_size: bool, #[class(attribute(optional))] /// Mapping of file name (name and extension e.g. fuzzer-app.exe or target.sys) /// to a tuple of (exe path, debuginfo path) where debuginfo is either a PDB or DWARF @@ -477,6 +476,9 @@ pub(crate) struct Tsffs { edges_seen_since_last: HashMap, /// The set of PCs comprising the current execution trace. This is cleared every execution. execution_trace: ExecutionTrace, + /// The current line coverage state comprising the total execution. This is not + /// cleared and is persistent across the full campaign until the fuzzer stops. + coverage: Coverage, /// The name of the fuzz snapshot, if saved snapshot_name: OnceCell, @@ -524,6 +526,7 @@ pub(crate) struct Tsffs { windows_os_info: WindowsOsInfo, cr3_cache: HashMap, + source_file_cache: SourceCache, } impl ClassObjectsFinalize for Tsffs { diff --git a/src/os/windows/debug_info.rs b/src/os/windows/debug_info.rs index dc1647ac..851dd4cb 100644 --- a/src/os/windows/debug_info.rs +++ b/src/os/windows/debug_info.rs @@ -20,7 +20,7 @@ use windows::Win32::System::{ SystemServices::IMAGE_DOS_HEADER, }; -use crate::os::DebugInfoConfig; +use crate::{os::DebugInfoConfig, source_cov::SourceCache}; use super::{ pdb::{CvInfoPdb70, Export}, @@ -43,7 +43,7 @@ impl<'a> DebugInfo<'a> { download_directory: P, not_found_full_name_cache: &mut HashSet, user_debug_info: DebugInfoConfig, - ) -> Result + ) -> Result> where P: AsRef, { @@ -61,13 +61,13 @@ impl<'a> DebugInfo<'a> { let pdb = PDB::open(pdb_file)?; - Ok(Self { + Ok(Some(Self { exe_path, pdb_path, exe_file_contents, pdb, - }) - } else { + })) + } else if user_debug_info.system { let dos_header = read_virtual::(processor, base)?; let nt_header = read_virtual::(processor, base + dos_header.e_lfanew as u64)?; @@ -163,12 +163,15 @@ impl<'a> DebugInfo<'a> { let pdb = PDB::open(pdb_file)?; - Ok(Self { + Ok(Some(Self { exe_path, pdb_path, exe_file_contents, pdb, - }) + })) + } else { + // bail!("No debug info provided for {name}"); + Ok(None) } } @@ -180,7 +183,7 @@ impl<'a> DebugInfo<'a> { directory_table_base: u64, not_found_full_name_cache: &mut HashSet, user_debug_info: DebugInfoConfig, - ) -> Result + ) -> Result> where P: AsRef, { @@ -198,13 +201,13 @@ impl<'a> DebugInfo<'a> { let pdb = PDB::open(pdb_file)?; - Ok(Self { + Ok(Some(Self { exe_path, pdb_path, exe_file_contents, pdb, - }) - } else { + })) + } else if user_debug_info.system { let dos_header = read_virtual_dtb::(processor, directory_table_base, base)?; let nt_header = read_virtual_dtb::( @@ -311,12 +314,14 @@ impl<'a> DebugInfo<'a> { let pdb = PDB::open(pdb_file)?; - Ok(Self { + Ok(Some(Self { exe_path, pdb_path, exe_file_contents, pdb, - }) + })) + } else { + Ok(None) } } @@ -342,73 +347,104 @@ pub struct ProcessModule { impl ProcessModule { pub fn intervals( &mut self, - guess_pdb_function_size: bool, + source_cache: &SourceCache, ) -> Result>> { - let mut syms = Vec::new(); - - if let Some(debug_info) = self.debug_info.as_mut() { - let symbol_table = debug_info.pdb.global_symbols()?; - let address_map = debug_info.pdb.address_map()?; - // let debug_information = debug_info.pdb.debug_information()?; - let mut symbols = symbol_table.iter(); - while let Some(symbol) = symbols.next()? { - match symbol.parse() { - Ok(sd) => { - match sd { - SymbolData::Public(p) => { - if p.function { - // NOTE: Public symbols don't have sizes, the address is just - // the RVA of their entry point, so we just do an entry of size 1 - if let Some(rva) = p.offset.to_rva(&address_map) { - let info = SymbolInfo::new( - rva.0 as u64, - self.base, - 0, - p.name.to_string().to_string(), - self.full_name.clone(), - ); - syms.push(info); - } - } - } - SymbolData::Procedure(p) => { - if let Some(rva) = p.offset.to_rva(&address_map) { - let info = SymbolInfo::new( - rva.0 as u64, - self.base, - p.len as u64, - p.name.to_string().to_string(), - self.full_name.clone(), - ); - syms.push(info); - } - } - SymbolData::ProcedureReference(_p) => { - // TODO - } - SymbolData::Trampoline(_t) => { - // TODO - } - _ => {} - } - } - Err(e) => { - let _ = e; - } - } - } - } - - if guess_pdb_function_size { - syms.sort_by(|a, b| a.rva.cmp(&b.rva)); - windows_mut(&mut syms).for_each(|w: &mut [SymbolInfo; 2]| { - if w[0].size == 0 { - w[0].size = w[1].rva - w[0].rva; - } - }); - } + let Some(debug_info) = self.debug_info.as_mut() else { + bail!("No debug info for module {}", self.full_name); + }; + + let string_table = debug_info.pdb.string_table()?; + let address_map = debug_info.pdb.address_map()?; + let symbols = debug_info + .pdb + .debug_information()? + .modules()? + .iterator() + .filter_map(|module| module.ok()) + .filter_map(|module| { + debug_info + .pdb + .module_info(&module) + .ok() + .flatten() + .map(|module_info| (module, module_info)) + }) + .flat_map(|(_module, module_info)| { + let Ok(line_program) = module_info.line_program() else { + return Vec::new(); + }; + + let Ok(symbols) = module_info.symbols() else { + return Vec::new(); + }; + + symbols + .iterator() + .filter_map(|symbol| symbol.ok()) + .filter_map(|symbol| { + symbol.parse().ok().map(|symbol_data| (symbol, symbol_data)) + }) + .filter_map(|(_symbol, symbol_data)| { + let SymbolData::Procedure(procedure_symbol) = symbol_data else { + return None; + }; + let symbol_name = symbol_data.name()?; + let procedure_rva = procedure_symbol.offset.to_rva(&address_map)?; + + let lines = line_program + .lines_for_symbol(procedure_symbol.offset) + .iterator() + .filter_map(|line| line.ok()) + .filter_map(|line_info| { + line_program + .get_file_info(line_info.file_index) + .ok() + .and_then(|line_file_info| { + string_table + .get(line_file_info.name) + .map(|line_file_name| (line_file_info, line_file_name)) + .ok() + }) + .and_then(|(line_file_info, line_file_name)| { + line_info.offset.to_rva(&address_map).map(|line_rva| { + (line_file_info, line_file_name, line_rva, line_info) + }) + }) + .and_then( + |(line_file_info, line_file_name, line_rva, line_info)| { + source_cache + .lookup_pdb( + &line_file_info, + &line_file_name.to_string(), + ) + .ok() + .flatten() + .map(|p| p.to_path_buf()) + .map(|file_path| LineInfo { + rva: line_rva.0 as u64, + size: line_info.length.unwrap_or(1), + file_path, + start_line: line_info.line_start, + end_line: line_info.line_end, + }) + }, + ) + }) + .collect::>(); + Some(SymbolInfo::new( + procedure_rva.0 as u64, + self.base, + procedure_symbol.len as u64, + symbol_name.to_string().to_string(), + self.full_name.clone(), + lines, + )) + }) + .collect::>() + }) + .collect::>(); - Ok(syms + Ok(symbols .into_iter() .map(|s| (self.base + s.rva..self.base + s.rva + s.size, s).into()) .collect()) @@ -423,23 +459,47 @@ pub struct Process { pub modules: Vec, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct LineInfo { + /// The relative virtual address in the executable image + pub rva: u64, + /// The size in bytes of the code this line represents + pub size: u32, + /// The file path of the source file on the *local* filesystem. This path is found by + /// looking up the pdb source path in the source cache on a best-effort approach + pub file_path: PathBuf, + /// The line number in the source file that this line starts at + pub start_line: u32, + /// The line number in the source file that this line ends at + pub end_line: u32, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct SymbolInfo { pub rva: u64, pub base: u64, pub size: u64, pub name: String, pub module: String, + pub lines: Vec, } impl SymbolInfo { - pub fn new(rva: u64, base: u64, size: u64, name: String, module: String) -> Self { + pub fn new( + rva: u64, + base: u64, + size: u64, + name: String, + module: String, + lines: Vec, + ) -> Self { Self { rva, base, size, name, module, + lines, } } } @@ -457,72 +517,104 @@ pub struct Module { impl Module { pub fn intervals( &mut self, - guess_pdb_function_size: bool, + source_cache: &SourceCache, ) -> Result>> { - let mut syms = Vec::new(); - - if let Some(debug_info) = self.debug_info.as_mut() { - let symbol_table = debug_info.pdb.global_symbols()?; - let address_map = debug_info.pdb.address_map()?; - let mut symbols = symbol_table.iter(); - while let Some(symbol) = symbols.next()? { - match symbol.parse() { - Ok(sd) => { - match sd { - SymbolData::Public(p) => { - if p.function { - // NOTE: Public symbols don't have sizes, the address is just - // the RVA of their entry point, so we just do an entry of size 1 - if let Some(rva) = p.offset.to_rva(&address_map) { - let info = SymbolInfo::new( - rva.0 as u64, - self.base, - 1, - p.name.to_string().to_string(), - self.full_name.clone(), - ); - syms.push(info); - } - } - } - SymbolData::Procedure(p) => { - if let Some(rva) = p.offset.to_rva(&address_map) { - let info = SymbolInfo::new( - rva.0 as u64, - self.base, - p.len as u64, - p.name.to_string().to_string(), - self.full_name.clone(), - ); - syms.push(info); - } - } - SymbolData::ProcedureReference(_p) => { - // TODO - } - SymbolData::Trampoline(_t) => { - // TODO - } - _ => {} - } - } - Err(e) => { - let _ = e; - } - } - } - } - - if guess_pdb_function_size { - syms.sort_by(|a, b| a.rva.cmp(&b.rva)); - windows_mut(&mut syms).for_each(|w: &mut [SymbolInfo; 2]| { - if w[0].size == 0 { - w[0].size = w[1].rva - w[0].rva; - } - }); - } + let Some(debug_info) = self.debug_info.as_mut() else { + bail!("No debug info for module {}", self.full_name); + }; + + let string_table = debug_info.pdb.string_table()?; + let address_map = debug_info.pdb.address_map()?; + let symbols = debug_info + .pdb + .debug_information()? + .modules()? + .iterator() + .filter_map(|module| module.ok()) + .filter_map(|module| { + debug_info + .pdb + .module_info(&module) + .ok() + .flatten() + .map(|module_info| (module, module_info)) + }) + .flat_map(|(_module, module_info)| { + let Ok(line_program) = module_info.line_program() else { + return Vec::new(); + }; + + let Ok(symbols) = module_info.symbols() else { + return Vec::new(); + }; + + symbols + .iterator() + .filter_map(|symbol| symbol.ok()) + .filter_map(|symbol| { + symbol.parse().ok().map(|symbol_data| (symbol, symbol_data)) + }) + .filter_map(|(_symbol, symbol_data)| { + let SymbolData::Procedure(procedure_symbol) = symbol_data else { + return None; + }; + let symbol_name = symbol_data.name()?; + let procedure_rva = procedure_symbol.offset.to_rva(&address_map)?; + + let lines = line_program + .lines_for_symbol(procedure_symbol.offset) + .iterator() + .filter_map(|line| line.ok()) + .filter_map(|line_info| { + line_program + .get_file_info(line_info.file_index) + .ok() + .and_then(|line_file_info| { + string_table + .get(line_file_info.name) + .map(|line_file_name| (line_file_info, line_file_name)) + .ok() + }) + .and_then(|(line_file_info, line_file_name)| { + line_info.offset.to_rva(&address_map).map(|line_rva| { + (line_file_info, line_file_name, line_rva, line_info) + }) + }) + .and_then( + |(line_file_info, line_file_name, line_rva, line_info)| { + source_cache + .lookup_pdb( + &line_file_info, + &line_file_name.to_string(), + ) + .ok() + .flatten() + .map(|p| p.to_path_buf()) + .map(|file_path| LineInfo { + rva: line_rva.0 as u64, + size: line_info.length.unwrap_or(1), + file_path, + start_line: line_info.line_start, + end_line: line_info.line_end, + }) + }, + ) + }) + .collect::>(); + Some(SymbolInfo::new( + procedure_rva.0 as u64, + self.base, + procedure_symbol.len as u64, + symbol_name.to_string().to_string(), + self.full_name.clone(), + lines, + )) + }) + .collect::>() + }) + .collect::>(); - Ok(syms + Ok(symbols .into_iter() .map(|s| (self.base + s.rva..self.base + s.rva + s.size, s).into()) .collect()) diff --git a/src/os/windows/kernel.rs b/src/os/windows/kernel.rs index b91745e2..3993d74e 100644 --- a/src/os/windows/kernel.rs +++ b/src/os/windows/kernel.rs @@ -195,8 +195,14 @@ impl KernelInfo { base, download_directory, not_found_full_name_cache, - user_debug_info, - )?; + // NOTE: We override that system must be true for the kernel because we must + // download it + DebugInfoConfig { + system: true, + user_debug_info: user_debug_info.user_debug_info, + }, + )? + .ok_or_else(|| anyhow!("Failed to get debug info for kernel"))?; let kuser_shared_data = read_virtual::( processor, @@ -319,7 +325,8 @@ impl KernelInfo { user_debug_info.clone(), ) }) - .ok(); + .ok() + .flatten(); modules.push(Module { base, diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 3c471d7d..48932edc 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -18,7 +18,7 @@ use util::read_virtual; use vergilius::bindings::*; -use crate::Tsffs; +use crate::{source_cov::SourceCache, Tsffs}; use super::DebugInfoConfig; @@ -69,8 +69,8 @@ impl WindowsOsInfo { &mut self, processor: *mut ConfObject, download_directory: P, - guess_pdb_function_size: bool, user_debug_info: DebugInfoConfig, + source_cache: &SourceCache, ) -> Result<()> where P: AsRef, @@ -151,7 +151,7 @@ impl WindowsOsInfo { .get_mut(&processor_nr) .ok_or_else(|| anyhow!("No modules for processor {processor_nr}"))? .iter_mut() - .map(|m| m.intervals(guess_pdb_function_size)) + .map(|m| m.intervals(source_cache)) .collect::>>()? .into_iter() .chain( @@ -166,7 +166,7 @@ impl WindowsOsInfo { )? .modules .iter_mut() - .map(|m| m.intervals(guess_pdb_function_size)) + .map(|m| m.intervals(source_cache)) .collect::>>()?, ) .flatten() @@ -217,11 +217,11 @@ impl Tsffs { self.windows_os_info.collect( trigger_obj, &self.debuginfo_download_directory, - self.guess_pdb_function_size, DebugInfoConfig { system: self.symbolic_coverage_system, user_debug_info: &self.debug_info, }, + &self.source_file_cache, )?; self.cr3_cache.insert(processor_nr, value); diff --git a/src/os/windows/structs.rs b/src/os/windows/structs.rs index ce64131e..ddb885db 100644 --- a/src/os/windows/structs.rs +++ b/src/os/windows/structs.rs @@ -2855,7 +2855,8 @@ impl WindowsEProcess { user_debug_info.clone(), ) }) - .ok(); + .ok() + .flatten(); modules.push(ProcessModule { base, diff --git a/src/source_cov/html.rs b/src/source_cov/html.rs new file mode 100644 index 00000000..ac493a05 --- /dev/null +++ b/src/source_cov/html.rs @@ -0,0 +1,448 @@ +use std::path::PathBuf; + +use markup::{define, Render}; + +#[derive(Debug, Clone)] +pub(crate) struct HtmlLineInfo { + pub(crate) hit_count: Option, + pub(crate) leading_spaces: usize, + pub(crate) line: String, +} + +#[derive(Debug, Clone)] +pub(crate) struct HtmlFunctionInfo { + pub(crate) hit_count: Option, + pub(crate) name: String, +} + +#[derive(Debug, Clone)] +pub(crate) struct HtmlSummaryInfo { + pub(crate) is_dir: bool, + // e.g. `../../../../../index.html` + pub(crate) top_level: PathBuf, + // e.g. `index.html` for files, `../index.html` for directories + pub(crate) parent: Option, + // e.g. `test.c` for files, `dir-name` for directories + pub(crate) filename: Option, + pub(crate) total_lines: usize, + pub(crate) hit_lines: usize, + pub(crate) total_functions: usize, + pub(crate) hit_functions: usize, +} + +define! { + Head { + } + Style { + + } + + CurrentView(summary: HtmlSummaryInfo) { + table { + tr { + th { + "Current view:" + } + td { + a[href = summary.top_level.to_string_lossy().to_string()] { + "Top Level" + } + @if let Some(parent) = &summary.parent { + @if let Some(parent_parent) = parent.parent() { + @markup::raw(" ") + "-" + @markup::raw(" ") + a[href = &parent.to_string_lossy().to_string()] { + @parent_parent.file_name().map(|s| s.to_string_lossy().to_string()).unwrap_or("".to_string()) + } + } + } + @markup::raw(" ") + "-" + @markup::raw(" ") + @summary.filename + } + } + tr { + th { + "Generated On" + } + td { + @chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string() + } + } + } + + } + + Summary( + summary: HtmlSummaryInfo, + ) { + table { + tr { + th {} + th[style = "text-align: right;"] { + "Coverage" + } + th[style = "text-align: right;"] { + "Total" + } + th[style = "text-align: right;"] { + "Hit" + } + } + tr { + th[style = "text-align: right;"] { + "Lines" + } + @if (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 >= 90.0 { + td[style = "text-align: right; background-color: #a7fc9d;"] { + @format!( + "{:.02}%", + (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 + ) + } + } else if (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 >= 30.0 { + td[style = "text-align: right; background-color: #ffea20;"] { + @format!( + "{:.02}%", + (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 + ) + } + } else { + td[style = "text-align: right; background-color: #ff6230;"] { + @format!( + "{:.02}%", + (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 + ) + } + } + + td[style = "text-align: right; background-color: #cad7fe;"] { + @summary.total_lines + } + td[style = "text-align: right; background-color: #cad7fe;"] { + @summary.hit_lines + } + } + tr { + th[style = "text-align: right;"] { + "Functions" + } + @if (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 >= 90.0 { + td[style = "text-align: right; background-color: #a7fc9d;"] { + @format!( + "{:.02}%", + (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 + ) + } + } else if (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 >= 30.0 { + td[style = "text-align: right; background-color: #ffea20;"] { + @format!( + "{:.02}%", + (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 + ) + } + + } else { + td[style = "text-align: right; background-color: #ff6230;"] { + @format!( + "{:.02}%", + (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 + ) + } + } + td[style = "text-align: right; background-color: #cad7fe;"] { + @summary.total_functions + } + td[style = "text-align: right; background-color: #cad7fe;"] { + @summary.hit_functions + } + } + } + } + + Listing( + lines: Vec, + ) { + table[ + cellpadding = 0, + cellspacing = 0, + border = 0, + style = "font-family: monospace, monospace; border-collapse: separate; border-spacing: 1em 0;" + ] { + tbody { + tr { + th[style = "text-align: right;"] { + "Line" + } + th[style = "text-align: right;"] { + "Hits" + } + th[style = "text-align: left;"] { + "Source Code" + } + } + @for (i, line_info) in lines.iter().enumerate() { + tr { + td[style = "text-align: right;"] { + @{i + 1} + } + td[style = "text-align: right;"] { + @if let Some(hit_count) = line_info.hit_count { + @hit_count + } else { + "-" + } + } + @if let Some(hit_count) = line_info.hit_count { + @if hit_count == 0 { + td[style = "text-align: left; background-color: #ff6230;"] { + @for _ in 0..line_info.leading_spaces { + @markup::raw(" ") + } + @line_info.line + } + } else { + td[style = "text-align: left; background-color: #cad7fe;"] { + @for _ in 0..line_info.leading_spaces { + @markup::raw(" ") + } + @line_info.line + } + } + } else { + td[style = "text-align: left;"] { + @for _ in 0..line_info.leading_spaces { + @markup::raw(" ") + } + @line_info.line + } + } + } + } + } + } + } + + FunctionListing( + functions: Vec, + ) { + table[ + cellpadding = 0, + cellspacing = 0, + border = 0, + style = "font-family: monospace, monospace; border-collapse: separate; border-spacing: 1em 0;" + ] { + tbody { + tr { + th[style = "text-align: right;"] { + "Function" + } + th[style = "text-align: right;"] { + "Hits" + } + } + @for function_info in functions.iter() { + tr { + @if let Some(hit_count) = function_info.hit_count { + @if hit_count == 0 { + td[style = "text-align: left; background-color: #ff6230;"] { + @function_info.name + } + } else { + td[style = "text-align: left; background-color: #cad7fe;"] { + @function_info.name + } + } + } else { + td[style = "text-align: left;"] { + @function_info.name + } + } + td[style = "text-align: right;"] { + @if let Some(hit_count) = function_info.hit_count { + @hit_count + } else { + "-" + } + } + } + } + } + } + } + + FilePage(listing: L, function_listing: FL) where L: Render, FL: Render { + tr[style = "border-bottom: 1px solid black;"] { + td { + @listing + } + } + tr[style = "border-bottom: 1px solid black;"] { + td { + @function_listing + } + } + } + + DirectoryPage(summaries: Vec) { + tr[style = "border-bottom: 1px solid black;"] { + table[width = "100%", style = "border-collapse: collapse;"] { + tr[style = "border-bottom: 1px solid black;"] { + th { + "File/Directory" + } + th { + "Line Coverage" + } + th { + "Total Lines" + } + th { + "Hit Lines" + } + th { + "Function Coverage" + } + th { + "Total Functions" + } + th { + "Hit Functions" + } + } + @for summary in summaries { + tr[style = "border-bottom: 1px solid black;"] { + td { + @if summary.is_dir { + @if let Some(filename) = &summary.filename { + a[href = PathBuf::from(filename).join("index.html").to_string_lossy().to_string()] { + @summary.filename + } + } else { + "" + } + + } else if let Some(filename) = &summary.filename { + a[href = format!("{}.html", filename)] { + @summary.filename + } + } else { + "" + } + } + @if (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 >= 90.0 { + td[style = "text-align: right; background-color: #a7fc9d;"] { + @format!( + "{:.02}%", + (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 + ) + } + } else if (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 >= 30.0 { + td[style = "text-align: right; background-color: #ffea20;"] { + @format!( + "{:.02}%", + (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 + ) + } + } else { + td[style = "text-align: right; background-color: #ff6230;"] { + @format!( + "{:.02}%", + (summary.hit_lines as f32 / summary.total_lines as f32) + * 100.0 + ) + } + } + td[style = "text-align: right; background-color: #cad7fe;"] { + @summary.total_lines + } + td[style = "text-align: right; background-color: #cad7fe;"] { + @summary.hit_lines + } + @if (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 >= 90.0 { + td[style = "text-align: right; background-color: #a7fc9d;"] { + @format!( + "{:.02}%", + (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 + ) + } + } else if (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 >= 30.0 { + td[style = "text-align: right; background-color: #ffea20;"] { + @format!( + "{:.02}%", + (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 + ) + } + } else { + td[style = "text-align: right; background-color: #ff6230;"] { + @format!( + "{:.02}%", + (summary.hit_functions as f32 / summary.total_functions as f32) + * 100.0 + ) + } + } + td[style = "text-align: right; background-color: #cad7fe;"] { + @summary.total_functions + } + td[style = "text-align: right; background-color: #cad7fe;"] { + @summary.hit_functions + } + } + } + } + } + } + + + Page(head: H, current_view: CV, summary: S, main: M) where H: Render, CV: Render, S: Render, M: Render { + @markup::doctype() + html { + head { + @head + @Style {} + meta[charse = "utf-8"]; + title { "TSFFS Coverage Report" } + } + body { + table[width = "100%", style = "border-collapse: collapse;"] { + tr[style = "text-align: center; border-bottom: 1px solid black;"] { + td { + "TSFFS Code Coverage Report" + } + } + tr[style = "border-bottom: 1px solid black;"] { + td { + @current_view + } + td { + @summary + } + } + @main + } + } + } + } + +} diff --git a/src/source_cov/lcov.rs b/src/source_cov/lcov.rs new file mode 100644 index 00000000..20e63952 --- /dev/null +++ b/src/source_cov/lcov.rs @@ -0,0 +1,773 @@ +use anyhow::{anyhow, Result}; +use markup::Render; +use petgraph::{ + dot::Dot, + graph::{DiGraph, NodeIndex}, + visit::{DfsPostOrder, IntoNeighborsDirected}, + Direction, Graph, +}; +use std::{ + collections::{btree_map::Entry, BTreeMap, HashMap, HashSet}, + fs::{create_dir_all, read_to_string, write}, + iter::repeat, + path::{Component, Path, PathBuf}, +}; + +use super::html::{ + CurrentView, DirectoryPage, FilePage, FunctionListing, Head, HtmlFunctionInfo, HtmlLineInfo, + HtmlSummaryInfo, Listing, Page, Summary, +}; + +#[derive(Debug, Clone, Default)] +pub struct TestNameRecordEntry(String); + +impl std::fmt::Display for TestNameRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "TN:{}", self.0) + } +} + +#[derive(Debug, Clone)] +pub struct SourceFileRecordEntry(PathBuf); + +impl std::fmt::Display for SourceFileRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "SF:{}", self.0.to_string_lossy()) + } +} + +impl Default for SourceFileRecordEntry { + fn default() -> Self { + Self(PathBuf::new()) + } +} + +#[derive(Debug, Clone)] +pub struct VersionRecordEntry(usize); + +impl Default for VersionRecordEntry { + fn default() -> Self { + Self(1) + } +} + +impl std::fmt::Display for VersionRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "VER:{}", self.0) + } +} + +#[derive(Debug, Clone)] +pub struct FunctionRecordEntry { + start_line: usize, + end_line: Option, + name: String, +} + +impl std::fmt::Display for FunctionRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.end_line { + Some(end_line) => write!(f, "FN:{},{},{}", self.start_line, end_line, self.name), + None => write!(f, "FN:{},{}", self.start_line, self.name), + } + } +} + +impl std::cmp::PartialEq for FunctionRecordEntry { + fn eq(&self, other: &Self) -> bool { + self.start_line == other.start_line && self.name == other.name + } +} + +impl std::cmp::PartialOrd for FunctionRecordEntry { + fn partial_cmp(&self, other: &Self) -> Option { + // Entries are ordered by start line + self.start_line.partial_cmp(&other.start_line) + } +} + +#[derive(Debug, Clone)] +pub struct FunctionDataRecordEntry { + hits: usize, + name: String, +} + +impl std::fmt::Display for FunctionDataRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "FNDA:{},{}", self.hits, self.name) + } +} + +impl std::cmp::PartialEq for FunctionDataRecordEntry { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + +#[derive(Debug, Clone, Default)] +pub struct FunctionsFoundRecordEntry(usize); + +impl std::fmt::Display for FunctionsFoundRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "FNF:{}", self.0) + } +} + +#[derive(Debug, Clone, Default)] +pub struct FunctionsHitRecordEntry(usize); + +impl std::fmt::Display for FunctionsHitRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "FNH:{}", self.0) + } +} + +// NOTE: We don't bother to implement branch data since we have the line data anyway + +// BRDA, BRF, BRH empty + +#[derive(Debug, Clone)] +pub struct LineRecordEntry { + line_number: usize, + hit_count: usize, + /// MD5 hash of line saved as base64, typically not used + checksum: Option, +} + +impl std::fmt::Display for LineRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.checksum { + Some(checksum) => { + write!(f, "DA:{},{},{}", self.line_number, self.hit_count, checksum) + } + None => write!(f, "DA:{},{}", self.line_number, self.hit_count), + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct LinesFoundRecordEntry(usize); + +impl std::fmt::Display for LinesFoundRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "LF:{}", self.0) + } +} + +#[derive(Debug, Clone, Default)] +pub struct LinesHitRecordEntry(usize); + +impl std::fmt::Display for LinesHitRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "LH:{}", self.0) + } +} + +#[derive(Debug, Clone, Default)] +pub struct EndOfRecordEntry; + +impl std::fmt::Display for EndOfRecordEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "end_of_record") + } +} + +#[derive(Debug, Clone, Default)] +pub struct Record { + test_name: TestNameRecordEntry, + source_file: SourceFileRecordEntry, + version: VersionRecordEntry, + // Functions ordered by start line + functions: BTreeMap, + // Function datas are unique + function_data: HashMap, + functions_found: FunctionsFoundRecordEntry, + functions_hit: FunctionsHitRecordEntry, + // Lines are ordered by line and unique + lines: BTreeMap, + lines_found: LinesFoundRecordEntry, + lines_hit: LinesHitRecordEntry, + end_of_record: EndOfRecordEntry, +} + +impl Record { + pub fn new

(path: P) -> Self + where + P: AsRef, + { + Self { + source_file: SourceFileRecordEntry(path.as_ref().to_path_buf()), + functions: BTreeMap::new(), + function_data: HashMap::new(), + lines: BTreeMap::new(), + ..Default::default() + } + } + + pub fn add_function_if_not_exists( + &mut self, + start_line: usize, + end_line: Option, + name: S, + ) -> bool + where + S: AsRef, + { + match self.functions.entry(start_line) { + Entry::Occupied(_) => false, + Entry::Vacant(entry) => { + entry.insert(FunctionRecordEntry { + start_line, + end_line, + name: name.as_ref().to_string(), + }); + self.functions_found.0 += 1; + self.function_data + .entry(name.as_ref().to_string()) + .or_insert_with(|| FunctionDataRecordEntry { + hits: 0, + name: name.as_ref().to_string(), + }); + true + } + } + } + + pub fn increment_function_data(&mut self, name: S) + where + S: AsRef, + { + let entry = self + .function_data + .entry(name.as_ref().to_string()) + .or_insert_with(|| FunctionDataRecordEntry { + hits: 0, + name: name.as_ref().to_string(), + }); + + if entry.hits == 0 { + self.functions_hit.0 += 1; + } + + entry.hits += 1; + } + + pub fn add_line_if_not_exists(&mut self, line_number: usize) -> bool { + match self.lines.entry(line_number) { + Entry::Occupied(_) => false, + Entry::Vacant(entry) => { + entry.insert(LineRecordEntry { + line_number, + hit_count: 0, + checksum: None, + }); + self.lines_found.0 += 1; + true + } + } + } + + pub fn increment_line(&mut self, line_number: usize) { + let entry = self + .lines + .entry(line_number) + .or_insert_with(|| LineRecordEntry { + line_number, + hit_count: 0, + checksum: None, + }); + + if entry.hit_count == 0 { + self.lines_hit.0 += 1; + } + + entry.hit_count += 1; + } +} + +impl std::fmt::Display for Record { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{}", self.test_name)?; + writeln!(f, "{}", self.source_file)?; + writeln!(f, "{}", self.version)?; + for function in self.functions.values() { + writeln!(f, "{}", function)?; + } + for function_data in self.function_data.values() { + writeln!(f, "{}", function_data)?; + } + writeln!(f, "{}", self.functions_found)?; + writeln!(f, "{}", self.functions_hit)?; + for line in self.lines.values() { + writeln!(f, "{}", line)?; + } + writeln!(f, "{}", self.lines_found)?; + writeln!(f, "{}", self.lines_hit)?; + writeln!(f, "{}", self.end_of_record)?; + Ok(()) + } +} + +impl Record { + pub fn source_filename(&self) -> String { + self.source_file + .0 + .file_name() + .map(|fname| fname.to_string_lossy().to_string()) + .unwrap_or("".to_string()) + } + + pub fn summary(&self, top_level: PathBuf, parent: Option) -> HtmlSummaryInfo { + HtmlSummaryInfo { + is_dir: false, + top_level, + parent, + filename: Some(self.source_filename()), + total_lines: self.lines_found.0, + hit_lines: self.lines_hit.0, + total_functions: self.functions_found.0, + hit_functions: self.functions_hit.0, + } + } + + pub fn lines(&self) -> Result> { + let contents = read_to_string(self.source_file.0.as_path())?; + let lines = contents + .lines() + .enumerate() + .map(|(i, line)| { + let hit_count = self.lines.get(&(i + 1)).map(|l| l.hit_count); + let leading_spaces = line.chars().take_while(|c| c.is_whitespace()).count(); + let trimmed = line.trim().to_string(); + HtmlLineInfo { + hit_count, + leading_spaces, + line: trimmed, + } + }) + .collect::>(); + Ok(lines) + } + + pub fn functions(&self) -> Vec { + let mut functions = self + .functions + .values() + .map(|f| HtmlFunctionInfo { + hit_count: self.function_data.get(&f.name).map(|d| d.hits), + name: f.name.as_str().to_string(), + }) + .collect::>(); + functions.sort_by(|a, b| a.name.cmp(&b.name)); + functions + } + + pub fn to_html(&self) -> Result { + let contents = read_to_string(self.source_file.0.as_path())?; + Ok(Default::default()) + } +} + +#[derive(Debug, Clone, Default)] +pub struct Records(HashMap); + +impl Records { + pub fn get_or_insert_mut

(&mut self, path: P) -> &mut Record + where + P: AsRef, + { + self.0 + .entry(path.as_ref().to_path_buf()) + .or_insert_with(|| Record::new(path)) + } + + pub fn get(&self, path: &Path) -> Option<&Record> { + self.0.get(path) + } +} + +impl std::fmt::Display for Records { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for record in self.0.values() { + write!(f, "{}", record)?; + } + Ok(()) + } +} + +impl Records { + /// Output LCOV records to HTML format, like a mini genhtml + pub fn to_html

(&self, output_directory: P) -> Result<()> + where + P: AsRef, + { + // Build a tree out of the output paths + let mut graph = DiGraph::<(PathBuf, Option), ()>::new(); + let mut node_ids = HashMap::::new(); + + let entries = self + .0 + .values() + .map(|record| { + let absolute_source_path = record.source_file.0.canonicalize()?; + let mut output_path = output_directory + .as_ref() + .components() + .chain( + absolute_source_path + .components() + .filter(|c| matches!(c, Component::Normal(_))), + ) + .collect::(); + output_path.set_file_name( + output_path + .file_name() + .map(|fname| fname.to_string_lossy().to_string()) + .unwrap_or_default() + + ".html", + ); + if let std::collections::hash_map::Entry::Vacant(entry) = + node_ids.entry(output_path.clone()) + { + entry.insert(graph.add_node((output_path.clone(), None))); + } + + let mut path = output_path.as_path(); + while let Some(parent) = path.parent() { + if let std::collections::hash_map::Entry::Vacant(entry) = + node_ids.entry(parent.to_path_buf()) + { + entry.insert(graph.add_node((parent.to_path_buf(), None))); + } + + if graph + .find_edge( + *node_ids + .get(parent) + .ok_or_else(|| anyhow!("parent not found"))?, + *node_ids + .get(path) + .ok_or_else(|| anyhow!("output path not found"))?, + ) + .is_none() + { + graph.add_edge( + *node_ids + .get(parent) + .ok_or_else(|| anyhow!("parent not found"))?, + *node_ids + .get(path) + .ok_or_else(|| anyhow!("output path not found"))?, + (), + ); + } + + path = parent; + + if !path.is_dir() { + create_dir_all(path)?; + } + + if path == output_directory.as_ref() { + break; + } + } + + Ok((output_path, record)) + }) + .collect::>>()? + .into_iter() + .collect::>(); + + let root = node_ids + .get(output_directory.as_ref()) + .ok_or_else(|| anyhow!("root not found"))?; + + let mut traversal = DfsPostOrder::new(&graph, *root); + + while let Some(node) = traversal.next(&graph) { + let path = graph + .node_weight(node) + .ok_or_else(|| anyhow!("No weight for node"))? + .0 + .clone(); + // Calculate the depth of this path from the output directory + if let Some(record) = entries.get(path.as_path()) { + let depth = path + .components() + .count() + .saturating_sub(output_directory.as_ref().components().count()) + .saturating_sub(1); + // This is a file node + let summary = record.summary( + repeat("..") + .take(depth) + .collect::() + .join("index.html"), + path.parent().map(|p| p.join("index.html")), + ); + graph + .node_weight_mut(node) + .ok_or_else(|| anyhow!("No weight for node"))? + .1 = Some(summary.clone()); + let lines = record.lines()?; + let functions = record.functions(); + let page = Page { + head: Head {}, + current_view: CurrentView { + summary: summary.clone(), + }, + summary: Summary { summary }, + main: FilePage { + listing: Listing { lines }, + function_listing: FunctionListing { functions }, + }, + }; + write(&path, page.to_string())?; + } else { + let depth = path + .components() + .count() + .saturating_sub(output_directory.as_ref().components().count()); + let (top_level, parent) = if path == output_directory.as_ref() { + // This is the root node + (PathBuf::from("index.html"), None) + } else { + // This is a directory node + ( + repeat("..") + .take(depth) + .collect::() + .join("index.html"), + path.parent().map(|p| p.join("index.html")), + ) + }; + let (total_lines, hit_lines, total_functions, hit_functions) = graph + .neighbors_directed(node, Direction::Outgoing) + .try_fold( + (0, 0, 0, 0), + |(total_lines, hit_lines, total_functions, hit_functions), neighbor| { + let summary = graph + .node_weight(neighbor) + .ok_or_else(|| anyhow!("No weight for node"))? + .1 + .as_ref() + .ok_or_else(|| anyhow!("No summary for node"))?; + println!("Adding neighbor {:?}", summary); + Ok::<(usize, usize, usize, usize), anyhow::Error>(( + total_lines + summary.total_lines, + hit_lines + summary.hit_lines, + total_functions + summary.total_functions, + hit_functions + summary.hit_functions, + )) + }, + )?; + + let summary = HtmlSummaryInfo { + is_dir: true, + top_level, + parent, + filename: path + .file_name() + .map(|fname| fname.to_string_lossy().to_string()), + total_lines, + hit_lines, + total_functions, + hit_functions, + }; + + let page = Page { + head: Head {}, + current_view: CurrentView { + summary: summary.clone(), + }, + summary: Summary { + summary: summary.clone(), + }, + main: DirectoryPage { + summaries: graph + .neighbors_directed(node, Direction::Outgoing) + .filter_map(|neighbor| { + graph + .node_weight(neighbor) + .ok_or_else(|| anyhow!("No weight for node")) + .ok() + .and_then(|weight| weight.1.as_ref().cloned()) + }) + .collect(), + }, + }; + write(path.join("index.html"), page.to_string())?; + + graph + .node_weight_mut(node) + .ok_or_else(|| anyhow!("No weight for node"))? + .1 = Some(summary); + } + } + + // NOTE: Left for easy debugging of the directory graph + // let dot = Dot::new(&graph); + // write( + // output_directory.as_ref().join("graph.dot"), + // format!("{:?}", dot), + // )?; + + Ok(()) + } +} + +#[allow(clippy::unwrap_used)] +#[cfg(test)] +mod test { + use std::path::PathBuf; + + use super::Records; + + #[test] + fn test_records() { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let mut records = Records::default(); + let record_test = records.get_or_insert_mut( + manifest_dir + .join("tests") + .join("rsrc") + .join("test-lcov") + .join("test.c"), + ); + record_test.add_function_if_not_exists(4, Some(16), "main"); + record_test.increment_function_data("main"); + record_test.add_line_if_not_exists(4); + record_test.add_line_if_not_exists(5); + record_test.add_line_if_not_exists(7); + record_test.add_line_if_not_exists(9); + record_test.add_line_if_not_exists(11); + record_test.add_line_if_not_exists(12); + record_test.add_line_if_not_exists(14); + record_test.increment_line(4); + record_test.increment_line(5); + record_test.increment_line(7); + record_test.increment_line(9); + record_test.increment_line(11); + record_test.increment_line(14); + let record_test2 = records.get_or_insert_mut( + manifest_dir + .join("tests") + .join("rsrc") + .join("test-lcov") + .join("test2.c"), + ); + record_test2.add_function_if_not_exists(1, Some(3), "x"); + record_test2.increment_function_data("x"); + record_test2.add_line_if_not_exists(1); + record_test2.add_line_if_not_exists(2); + record_test2.add_line_if_not_exists(3); + record_test2.increment_line(1); + record_test2.increment_line(2); + record_test2.increment_line(3); + println!("{}", records); + } + + #[test] + fn test_records_to_html() { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let mut records = Records::default(); + + let record_test = records.get_or_insert_mut( + manifest_dir + .join("tests") + .join("rsrc") + .join("test-lcov") + .join("test.c"), + ); + record_test.add_function_if_not_exists(4, Some(16), "main"); + record_test.increment_function_data("main"); + record_test.add_line_if_not_exists(4); + record_test.add_line_if_not_exists(5); + record_test.add_line_if_not_exists(7); + record_test.add_line_if_not_exists(9); + record_test.add_line_if_not_exists(11); + record_test.add_line_if_not_exists(12); + record_test.add_line_if_not_exists(14); + record_test.increment_line(4); + record_test.increment_line(5); + record_test.increment_line(7); + record_test.increment_line(9); + record_test.increment_line(11); + record_test.increment_line(14); + + let record_test = records.get_or_insert_mut( + manifest_dir + .join("tests") + .join("rsrc") + .join("test-lcov") + .join("subdir1") + .join("test.c"), + ); + record_test.add_function_if_not_exists(4, Some(16), "main"); + record_test.increment_function_data("main"); + record_test.add_line_if_not_exists(4); + record_test.add_line_if_not_exists(5); + record_test.add_line_if_not_exists(7); + record_test.add_line_if_not_exists(9); + record_test.add_line_if_not_exists(11); + record_test.add_line_if_not_exists(12); + record_test.add_line_if_not_exists(14); + record_test.increment_line(4); + record_test.increment_line(5); + record_test.increment_line(7); + record_test.increment_line(9); + record_test.increment_line(11); + record_test.increment_line(14); + + let record_test = records.get_or_insert_mut( + manifest_dir + .join("tests") + .join("rsrc") + .join("test-lcov") + .join("subdir2") + .join("test-subdir2.c"), + ); + record_test.add_function_if_not_exists(4, Some(16), "main"); + record_test.increment_function_data("main"); + record_test.add_line_if_not_exists(4); + record_test.add_line_if_not_exists(5); + record_test.add_line_if_not_exists(7); + record_test.add_line_if_not_exists(9); + record_test.add_line_if_not_exists(11); + record_test.add_line_if_not_exists(12); + record_test.add_line_if_not_exists(14); + record_test.increment_line(4); + record_test.increment_line(5); + record_test.increment_line(7); + record_test.increment_line(9); + record_test.increment_line(11); + record_test.increment_line(14); + + let record_test2 = records.get_or_insert_mut( + manifest_dir + .join("tests") + .join("rsrc") + .join("test-lcov") + .join("test2.c"), + ); + record_test2.add_function_if_not_exists(1, Some(3), "x"); + record_test2.increment_function_data("x"); + record_test2.add_line_if_not_exists(1); + record_test2.add_line_if_not_exists(2); + record_test2.add_line_if_not_exists(3); + record_test2.increment_line(1); + record_test2.increment_line(2); + record_test2.increment_line(3); + + records + .to_html( + manifest_dir + .join("tests") + .join("rsrc") + .join("test-lcov") + .join("html"), + ) + .unwrap(); + } +} diff --git a/src/source_cov/mod.rs b/src/source_cov/mod.rs new file mode 100644 index 00000000..1f87f4d0 --- /dev/null +++ b/src/source_cov/mod.rs @@ -0,0 +1,137 @@ +use std::{ + collections::HashMap, + fs::read, + path::{Path, PathBuf}, +}; + +use anyhow::Result; +use md5::compute; +use pdb::{FileChecksum, FileInfo}; +use sha1::{Digest, Sha1}; +use sha2::Sha256; +use typed_path::{TypedComponent, TypedPath, UnixComponent, WindowsComponent}; +use walkdir::WalkDir; + +pub(crate) mod html; +pub(crate) mod lcov; + +#[derive(Debug, Clone, Default)] +pub struct SourceCache { + file_paths: Vec, + prefix_lookup: HashMap, PathBuf>, + md5_lookup: HashMap, PathBuf>, + sha1_lookup: HashMap, PathBuf>, + sha256_lookup: HashMap, PathBuf>, +} + +impl SourceCache { + pub fn new

(src_dir: P) -> Result + where + P: AsRef, + { + let mut prefix_lookup = HashMap::new(); + let mut md5_lookup = HashMap::new(); + let mut sha1_lookup = HashMap::new(); + let mut sha256_lookup = HashMap::new(); + + let file_paths = WalkDir::new(src_dir) + .into_iter() + .filter_map(|entry| entry.ok()) + .filter(|entry| entry.file_type().is_file()) + .map(|entry| entry.path().to_path_buf()) + .collect::>(); + + for path in &file_paths { + let contents = read(path)?; + let md5 = compute(&contents).0.to_vec(); + let sha1 = Sha1::digest(&contents).to_vec(); + let sha256 = Sha256::digest(&contents).to_vec(); + md5_lookup.insert(md5, path.clone()); + sha1_lookup.insert(sha1, path.clone()); + sha256_lookup.insert(sha256, path.clone()); + let mut components = path + .components() + .filter_map(|c| { + if let std::path::Component::Normal(c) = c { + Some(c.to_string_lossy().to_string()) + } else { + None + } + }) + .collect::>(); + + // Create a list of component lists starting from the full path, then the full path + // minus the first component, then the full path minus the first two components, etc. + // This is used to create a lookup table for the source files. + while !components.is_empty() { + prefix_lookup.insert(components.clone(), path.clone()); + components.remove(0); + } + } + + Ok(Self { + file_paths, + prefix_lookup, + md5_lookup, + sha1_lookup, + sha256_lookup, + }) + } + + pub fn lookup_file_name_components(&self, file_name: &str) -> Option<&Path> { + let mut file_name_components = TypedPath::derive(&file_name.to_string().to_string()) + .components() + .filter_map(|c| match c { + TypedComponent::Unix(u) => { + if let UnixComponent::Normal(c) = u { + String::from_utf8(c.to_vec()).ok() + } else { + None + } + } + TypedComponent::Windows(w) => { + if let WindowsComponent::Normal(c) = w { + String::from_utf8(c.to_vec()).ok() + } else { + None + } + } + }) + .collect::>(); + + while !file_name_components.is_empty() { + println!("Looking up {:?}", file_name_components); + if let Some(file_path) = self.prefix_lookup.get(&file_name_components) { + return Some(file_path); + } + + file_name_components.remove(0); + } + + None + } + + pub fn lookup_pdb(&self, file_info: &FileInfo, file_name: &str) -> Result> { + Ok(match file_info.checksum { + FileChecksum::None => self.lookup_file_name_components(file_name), + FileChecksum::Md5(m) => self + .md5_lookup + .get(m) + .map(|p| p.as_path()) + .or_else(|| self.lookup_file_name_components(file_name)), + FileChecksum::Sha1(s1) => self + .sha1_lookup + .get(s1) + .map(|p| p.as_path()) + .or_else(|| self.lookup_file_name_components(file_name)), + FileChecksum::Sha256(s256) => self + .sha256_lookup + .get(s256) + .map(|p| p.as_path()) + .or_else(|| self.lookup_file_name_components(file_name)), + }) + } +} + +#[derive(Default)] +pub struct Coverage {} diff --git a/tests/rsrc/test-lcov/html/home/index.html b/tests/rsrc/test-lcov/html/home/index.html new file mode 100644 index 00000000..413545a9 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - html - home
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines87.50%2421
Functions100.00%44
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
rhart87.50%2421100.00%44
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/index.html b/tests/rsrc/test-lcov/html/home/rhart/hub/index.html new file mode 100644 index 00000000..6d9fbba5 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - rhart - hub
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines87.50%2421
Functions100.00%44
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
tsffs87.50%2421100.00%44
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/index.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/index.html new file mode 100644 index 00000000..95441d45 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - hub - tsffs
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines87.50%2421
Functions100.00%44
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
tests87.50%2421100.00%44
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/index.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/index.html new file mode 100644 index 00000000..03c96589 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - tsffs - tests
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines87.50%2421
Functions100.00%44
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
rsrc87.50%2421100.00%44
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/index.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/index.html new file mode 100644 index 00000000..9e774b97 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - tests - rsrc
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines87.50%2421
Functions100.00%44
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
test-lcov87.50%2421100.00%44
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/index.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/index.html new file mode 100644 index 00000000..0b050eb0 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - rsrc - test-lcov
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines87.50%2421
Functions100.00%44
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
subdir285.71%76100.00%11
subdir185.71%76100.00%11
test.c85.71%76100.00%11
test2.c100.00%33100.00%11
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir1/index.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir1/index.html new file mode 100644 index 00000000..da06410a --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir1/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - test-lcov - subdir1
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines85.71%76
Functions100.00%11
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
test.c85.71%76100.00%11
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir1/test.c.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir1/test.c.html new file mode 100644 index 00000000..8f899fa4 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir1/test.c.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - subdir1 - test.c
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines85.71%76
Functions100.00%11
LineHitsSource Code
1-#include <stdio.h>
2-extern int x(int);
3-
41int main() {
51  int a = 0;
6-
71  a += 1;
8-
91  a = x(a);
10-
111  if (a == 15) {
120    printf("%s\n", "hello");
13-  } else {
141    printf("no\n");
15-  }
16-}
FunctionHits
main1
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir2/index.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir2/index.html new file mode 100644 index 00000000..a7930b14 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir2/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - test-lcov - subdir2
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines85.71%76
Functions100.00%11
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
test-subdir2.c85.71%76100.00%11
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir2/test-subdir2.c.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir2/test-subdir2.c.html new file mode 100644 index 00000000..d698687f --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/subdir2/test-subdir2.c.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - subdir2 - test-subdir2.c
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines85.71%76
Functions100.00%11
LineHitsSource Code
1-#include <stdio.h>
2-extern int x(int);
3-
41int main() {
51  int a = 0;
6-
71  a += 1;
8-
91  a = x(a);
10-
111  if (a == 15) {
120    printf("%s\n", "hello");
13-  } else {
141    printf("no\n");
15-  }
16-}
FunctionHits
main1
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/test.c.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/test.c.html new file mode 100644 index 00000000..998c846c --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/test.c.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - test-lcov - test.c
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines85.71%76
Functions100.00%11
LineHitsSource Code
1-#include <stdio.h>
2-extern int x(int);
3-
41int main() {
51    int a = 0;
6-
71    a += 1;
8-
91    a = x(a);
10-
111    if (a == 15) {
120        printf("%s\n", "hello");
13-    } else {
141        printf("no\n");
15-    }
16-}
FunctionHits
main1
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/test2.c.html b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/test2.c.html new file mode 100644 index 00000000..58a0e4e9 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/hub/tsffs/tests/rsrc/test-lcov/test2.c.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - test-lcov - test2.c
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines100.00%33
Functions100.00%11
LineHitsSource Code
11int x(int a) {
21    return a;
31}
FunctionHits
x1
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/home/rhart/index.html b/tests/rsrc/test-lcov/html/home/rhart/index.html new file mode 100644 index 00000000..d2a302c8 --- /dev/null +++ b/tests/rsrc/test-lcov/html/home/rhart/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - home - rhart
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines87.50%2421
Functions100.00%44
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
hub87.50%2421100.00%44
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/html/index.html b/tests/rsrc/test-lcov/html/index.html new file mode 100644 index 00000000..f4394731 --- /dev/null +++ b/tests/rsrc/test-lcov/html/index.html @@ -0,0 +1 @@ +TSFFS Coverage Report
TSFFS Code Coverage Report
Current view:Top Level - html
Generated On2024-07-16 17:40:25
CoverageTotalHit
Lines87.50%2421
Functions100.00%44
File/DirectoryLine CoverageTotal LinesHit LinesFunction CoverageTotal FunctionsHit Functions
home87.50%2421100.00%44
\ No newline at end of file diff --git a/tests/rsrc/test-lcov/subdir1/test.c b/tests/rsrc/test-lcov/subdir1/test.c new file mode 100644 index 00000000..1ff5b4fb --- /dev/null +++ b/tests/rsrc/test-lcov/subdir1/test.c @@ -0,0 +1,16 @@ +#include +extern int x(int); + +int main() { + int a = 0; + + a += 1; + + a = x(a); + + if (a == 15) { + printf("%s\n", "hello"); + } else { + printf("no\n"); + } +} diff --git a/tests/rsrc/test-lcov/subdir2/test-subdir2.c b/tests/rsrc/test-lcov/subdir2/test-subdir2.c new file mode 100644 index 00000000..1ff5b4fb --- /dev/null +++ b/tests/rsrc/test-lcov/subdir2/test-subdir2.c @@ -0,0 +1,16 @@ +#include +extern int x(int); + +int main() { + int a = 0; + + a += 1; + + a = x(a); + + if (a == 15) { + printf("%s\n", "hello"); + } else { + printf("no\n"); + } +} diff --git a/tests/rsrc/test-lcov/test.c b/tests/rsrc/test-lcov/test.c new file mode 100644 index 00000000..a1c3a8d0 --- /dev/null +++ b/tests/rsrc/test-lcov/test.c @@ -0,0 +1,16 @@ +#include +extern int x(int); + +int main() { + int a = 0; + + a += 1; + + a = x(a); + + if (a == 15) { + printf("%s\n", "hello"); + } else { + printf("no\n"); + } +} diff --git a/tests/rsrc/test-lcov/test2.c b/tests/rsrc/test-lcov/test2.c new file mode 100644 index 00000000..e8492f41 --- /dev/null +++ b/tests/rsrc/test-lcov/test2.c @@ -0,0 +1,3 @@ +int x(int a) { + return a; +} From 5d42a3b055ca998cd728950705af46b0e589be3e Mon Sep 17 00:00:00 2001 From: novafacing Date: Thu, 18 Jul 2024 15:05:13 -0700 Subject: [PATCH 06/14] Add some debug logging and fixes --- Cargo.toml | 3 +- src/haps/mod.rs | 9 +++-- src/lib.rs | 55 +++++++++++++++++++--------- src/os/mod.rs | 5 ++- src/os/windows/debug_info.rs | 40 ++++++++++++++++++++- src/os/windows/idt.rs | 2 +- src/os/windows/kernel.rs | 67 ++++++++++++++++++++++------------ src/os/windows/mod.rs | 70 ++++++++++++++++++++++++++++++------ src/os/windows/pdb.rs | 5 +++ src/os/windows/structs.rs | 16 +++++++-- src/os/windows/util.rs | 17 +++++++++ src/source_cov/html.rs | 1 - src/source_cov/lcov.rs | 13 ++----- src/source_cov/mod.rs | 6 +--- src/tracer/mod.rs | 48 ++++++++++++++++++++++--- 15 files changed, 278 insertions(+), 79 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f0b7ecd6..ce550592 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ version = "0.2.2" [package.metadata.simics] package-number = 31337 -version = "6.1.5" +version = "6.1.6" [lib] crate-type = ["cdylib", "rlib"] @@ -104,6 +104,7 @@ sha2 = "0.10.8" typed-path = "0.9.0" markup = "0.15.0" petgraph = "0.6.5" +thiserror = "1.0.63" [dev-dependencies] simics-test = "0.1.0" diff --git a/src/haps/mod.rs b/src/haps/mod.rs index 6b92cce2..a6579bd0 100644 --- a/src/haps/mod.rs +++ b/src/haps/mod.rs @@ -62,9 +62,10 @@ impl Tsffs { self.windows_os_info.collect( start_processor_raw, &self.debuginfo_download_directory, - DebugInfoConfig { + &mut DebugInfoConfig { system: self.symbolic_coverage_system, user_debug_info: &self.debug_info, + coverage: &mut self.coverage, }, &self.source_file_cache, )?; @@ -225,9 +226,10 @@ impl Tsffs { self.windows_os_info.collect( processor, &self.debuginfo_download_directory, - DebugInfoConfig { + &mut DebugInfoConfig { system: self.symbolic_coverage_system, user_debug_info: &self.debug_info, + coverage: &mut self.coverage, }, &self.source_file_cache, )?; @@ -271,9 +273,10 @@ impl Tsffs { self.windows_os_info.collect( processor, &self.debuginfo_download_directory, - DebugInfoConfig { + &mut DebugInfoConfig { system: self.symbolic_coverage_system, user_debug_info: &self.debug_info, + coverage: &mut self.coverage, }, &self.source_file_cache, )?; diff --git a/src/lib.rs b/src/lib.rs index 7280b38e..0872948f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ use simics::{ // which is necessary because this module is compatible with base versions which cross the // deprecation boundary use simics::{restore_snapshot, save_snapshot}; -use source_cov::{Coverage, SourceCache}; +use source_cov::{lcov::Records, SourceCache}; use state::StopReason; use std::{ alloc::{alloc_zeroed, Layout}, @@ -75,7 +75,7 @@ use std::{ ptr::null_mut, str::FromStr, sync::mpsc::{Receiver, Sender}, - thread::JoinHandle, + thread::{spawn, JoinHandle}, time::SystemTime, }; use tracer::{ @@ -422,6 +422,10 @@ pub(crate) struct Tsffs { /// Whether symbolic coverage should be collected for system components by downloading /// executable and debug info files where possible. pub symbolic_coverage_system: bool, + #[class(attribute(optional, default = lookup_file("%simics%")?.join("symbolic-coverage")))] + /// Directory in which source files are located. Source files do not need to be arranged in + /// the same directory structure as the compiled source, and are looked up by hash. + pub symbolic_coverage_directory: PathBuf, /// Handle for the core simulation stopped hap stop_hap_handle: HapHandle, @@ -478,7 +482,7 @@ pub(crate) struct Tsffs { execution_trace: ExecutionTrace, /// The current line coverage state comprising the total execution. This is not /// cleared and is persistent across the full campaign until the fuzzer stops. - coverage: Coverage, + coverage: Records, /// The name of the fuzz snapshot, if saved snapshot_name: OnceCell, @@ -724,6 +728,10 @@ impl Tsffs { warn!(self.as_conf_object(), "Failed to disable VMP: {}", e); } + // Initialize the source cache for source/line lookups + info!(self.as_conf_object(), "Initializing source cache"); + self.source_file_cache = SourceCache::new(&self.debuginfo_source_directory)?; + self.log(LogMessage::startup())?; #[cfg(simics_version_7)] @@ -929,23 +937,38 @@ impl Tsffs { /// Save the current execution trace to a file pub fn save_execution_trace(&mut self) -> Result<()> { - let mut hasher = DefaultHasher::new(); - self.execution_trace.hash(&mut hasher); - let hash = hasher.finish(); + let execution_trace = self.execution_trace.clone(); + let execution_trace_dir = self.execution_trace_directory.clone(); + let coverage = self.coverage.clone(); + let symbolic_coverage_directory = self.symbolic_coverage_directory.clone(); + // We just fire and forget this thread -- we won't know if it fails but it's basically + // guaranteed not to. This stops us from having to wait every exec to write a huge file. + spawn(move || { + let mut hasher = DefaultHasher::new(); + execution_trace.hash(&mut hasher); + let hash = hasher.finish(); + + if !execution_trace_dir.is_dir() { + create_dir_all(&execution_trace_dir)?; + } - if !self.execution_trace_directory.is_dir() { - create_dir_all(&self.execution_trace_directory)?; - } + let trace_path = execution_trace_dir.join(format!("{:x}.json", hash)); - let trace_path = self - .execution_trace_directory - .join(format!("{:x}.json", hash)); + if !trace_path.exists() { + let trace_file = File::create(&trace_path)?; + println!("Saving execution trace to {}", trace_path.display()); + to_writer(trace_file, &execution_trace)?; + } + + if !symbolic_coverage_directory.is_dir() { + create_dir_all(&symbolic_coverage_directory)?; + } - if !trace_path.exists() { - let trace_file = File::create(trace_path)?; + println!("Saving coverage information to symbolic coverage directory {symbolic_coverage_directory:?}"); + coverage.to_html(&symbolic_coverage_directory)?; - to_writer(trace_file, &self.execution_trace)?; - } + Ok::<(), anyhow::Error>(()) + }); Ok(()) } diff --git a/src/os/mod.rs b/src/os/mod.rs index 2240edad..e4ba013b 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -2,10 +2,13 @@ use std::{collections::HashMap, path::PathBuf}; +use crate::source_cov::lcov::Records; + pub mod windows; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct DebugInfoConfig<'a> { pub system: bool, pub user_debug_info: &'a HashMap>, + pub coverage: &'a mut Records, } diff --git a/src/os/windows/debug_info.rs b/src/os/windows/debug_info.rs index 851dd4cb..ff78bcfe 100644 --- a/src/os/windows/debug_info.rs +++ b/src/os/windows/debug_info.rs @@ -28,21 +28,27 @@ use super::{ }; #[derive(Debug)] +/// Debug info for an executable (which may be a .exe, .sys, etc) pub struct DebugInfo<'a> { + /// The path to the executable file on the local system pub exe_path: PathBuf, + /// The path to the PDB file corresponding to the executable on the local system pub pdb_path: PathBuf, + /// The contents of the executable file pub exe_file_contents: Vec, + /// The loaded PDB info pub pdb: PDB<'a, File>, } impl<'a> DebugInfo<'a> { + /// Instantiate a new debug info for an object pub fn new

( processor: *mut ConfObject, name: &str, base: u64, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: DebugInfoConfig, + user_debug_info: &DebugInfoConfig, ) -> Result> where P: AsRef, @@ -175,6 +181,7 @@ impl<'a> DebugInfo<'a> { } } + /// Instantiate a new debug info for an object with a specific directory table base pub fn new_dtb

( processor: *mut ConfObject, name: &str, @@ -325,26 +332,36 @@ impl<'a> DebugInfo<'a> { } } + /// Return the parsed PE file pub fn exe(&self) -> Result> { PE::parse(&self.exe_file_contents) .map_err(move |e| anyhow!("Failed to parse PE file: {}", e)) } + /// Get a list of exports from the PE file pub fn exports(&self) -> Result> { Ok(self.exe()?.exports.iter().map(Export::from).collect()) } } #[derive(Debug)] +/// A module (or object) loaded in a specific process pub struct ProcessModule { + /// The base of the object pub base: u64, + /// The size of the object pub size: u64, + /// The full name (typically a path) of the object on disk pub full_name: String, + /// The base name of the object pub base_name: String, + /// Loaded debug info for the object pub debug_info: Option>, } impl ProcessModule { + /// Return lookup intervals for symbols in the process module which can be used to build + /// an interval tree pub fn intervals( &mut self, source_cache: &SourceCache, @@ -452,14 +469,20 @@ impl ProcessModule { } #[derive(Debug)] +/// A process pub struct Process { + /// The unique PID of the process pub pid: u64, + /// The file name of the process's main object pub file_name: String, + /// The base address of the process's main object pub base_address: u64, + /// The list of modules/objects loaded into the process's address space pub modules: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// Information about a line in a source file from a PDB pub struct LineInfo { /// The relative virtual address in the executable image pub rva: u64, @@ -475,12 +498,19 @@ pub struct LineInfo { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// Information about a symbol in a PDB, including the member lines of the symbol, if any. pub struct SymbolInfo { + /// The relative virtual address in the executable image pub rva: u64, + /// The base address of the executable image pub base: u64, + /// The size of the symbol (e.g. function size) pub size: u64, + /// The (possibly mangled) name of the symbol pub name: String, + /// The name of the module the symbol is in pub module: String, + /// The source lines of code for the symbol pub lines: Vec, } @@ -505,16 +535,24 @@ impl SymbolInfo { } #[derive(Debug)] +/// A kernel module/driver pub struct Module { + /// The base address of the module pub base: u64, + /// The entrypoint of the module pub entry: u64, + /// The size of the module pub size: u64, + /// The full name of the module pub full_name: String, + /// The base name of the module pub base_name: String, + /// The loaded debug info for the module pub debug_info: Option>, } impl Module { + /// Return lookup intervals for symbols in the module which can be used to build an interval tree pub fn intervals( &mut self, source_cache: &SourceCache, diff --git a/src/os/windows/idt.rs b/src/os/windows/idt.rs index cd811c39..0901bfda 100644 --- a/src/os/windows/idt.rs +++ b/src/os/windows/idt.rs @@ -1,6 +1,6 @@ #[repr(C)] // NOTE: Without repr(C) alignment causes corruption #[derive(Debug, Clone)] -// NOTE: The vergilius generated struct is incorrectly sized +// NOTE: The vergilius generated struct is incorrectly sized so we use this one pub struct IdtEntry64 { offset_low: u16, selector: u16, diff --git a/src/os/windows/kernel.rs b/src/os/windows/kernel.rs index 3993d74e..e13c2d58 100644 --- a/src/os/windows/kernel.rs +++ b/src/os/windows/kernel.rs @@ -5,7 +5,7 @@ use std::{ use anyhow::{anyhow, bail, Result}; use pdb::{FallibleIterator, SymbolData}; -use simics::{debug, get_attribute, get_object, ConfObject}; +use simics::{debug, get_attribute, get_object, info, ConfObject}; use vergilius::bindings::*; use windows::Win32::System::{ Diagnostics::Debug::{IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_NT_HEADERS64}, @@ -31,6 +31,7 @@ use super::{ const KUSER_SHARED_DATA_ADDRESS_X86_64: u64 = 0xFFFFF78000000000; +/// Returns whether the given page is the ntos kernel pub fn page_is_kernel(processor: *mut ConfObject, address: u64) -> Result { const OPTIONAL_HEADER_SIGNATURE_PE32: u16 = 0x10b; const OPTIONAL_HEADER_SIGNATURE_PE32_PLUS: u16 = 0x20b; @@ -120,6 +121,7 @@ pub fn page_is_kernel(processor: *mut ConfObject, address: u64) -> Result Ok(false) } +/// Search for the ntos kernel within a given address range pub fn find_kernel(processor: *mut ConfObject, start: u64, step: u64) -> Result { let mut scan_address = start & !(step - 1); let stop_address = start & !(0x1000000000000 - 1); @@ -140,6 +142,7 @@ pub fn find_kernel(processor: *mut ConfObject, start: u64, step: u64) -> Result< bail!("Kernel not found"); } +/// Search for the ntos kernel by using the IDT to determine scan ranges, given a build number pub fn find_kernel_with_idt(processor: *mut ConfObject, build: u32) -> Result { let sim_idtr_base: u64 = get_attribute(processor, "idtr_base")?.try_into()?; @@ -169,22 +172,29 @@ pub fn find_kernel_with_idt(processor: *mut ConfObject, build: u32) -> Result, } impl KernelInfo { + /// Create a new kernel info struct pub fn new

( processor: *mut ConfObject, name: &str, base: u64, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: DebugInfoConfig, + user_debug_info: &mut DebugInfoConfig, ) -> Result where P: AsRef, @@ -197,9 +207,10 @@ impl KernelInfo { not_found_full_name_cache, // NOTE: We override that system must be true for the kernel because we must // download it - DebugInfoConfig { + &DebugInfoConfig { system: true, user_debug_info: user_debug_info.user_debug_info, + coverage: user_debug_info.coverage, }, )? .ok_or_else(|| anyhow!("Failed to get debug info for kernel"))?; @@ -262,12 +273,13 @@ impl KernelInfo { } } + /// Return the list of currently loaded modules pub fn loaded_module_list

( &mut self, processor: *mut ConfObject, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: DebugInfoConfig, + user_debug_info: &DebugInfoConfig, ) -> Result> where P: AsRef, @@ -322,7 +334,7 @@ impl KernelInfo { base, download_directory.as_ref(), not_found_full_name_cache, - user_debug_info.clone(), + user_debug_info, ) }) .ok() @@ -387,12 +399,13 @@ impl KernelInfo { } } + /// Return the currently running process pub fn current_process

( &mut self, processor: *mut ConfObject, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: DebugInfoConfig, + user_debug_info: &DebugInfoConfig, ) -> Result where P: AsRef, @@ -432,12 +445,13 @@ impl KernelInfo { }) } + /// Return the list of loaded processes pub fn process_list

( &mut self, processor: *mut ConfObject, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: DebugInfoConfig, + user_debug_info: &DebugInfoConfig, ) -> Result> where P: AsRef, @@ -473,23 +487,32 @@ impl KernelInfo { self.build, list_entry.Flink as u64, )?; + let pid = eprocess.pid(); + let file_name = eprocess.file_name(processor)?; + let base_address = + eprocess.base_address(processor, self.major, self.minor, self.build)?; + debug!( + get_object("tsffs")?, + "Found process {} at {:#x}", file_name, base_address + ); + let modules = eprocess + .modules( + processor, + self.major, + self.minor, + self.build, + download_directory.as_ref(), + not_found_full_name_cache, + user_debug_info, + ) + .unwrap_or_default(); + debug!(get_object("tsffs")?, "Found {} modules", modules.len()); processes.push(Process { - pid: eprocess.pid(), - file_name: eprocess.file_name(processor)?, - base_address: eprocess - .base_address(processor, self.major, self.minor, self.build)?, - modules: eprocess - .modules( - processor, - self.major, - self.minor, - self.build, - download_directory.as_ref(), - not_found_full_name_cache, - user_debug_info.clone(), - ) - .unwrap_or_default(), + pid, + file_name, + base_address, + modules, }); list_entry = eprocess.active_process_links(); diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 48932edc..6dd7f987 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -5,7 +5,7 @@ use intervaltree::IntervalTree; use kernel::{find_kernel_with_idt, KernelInfo}; use raw_cstr::AsRawCstr; use simics::{ - get_interface, get_object, get_processor_number, info, sys::cpu_cb_handle_t, ConfObject, + get_interface, get_object, get_processor_number, info, sys::cpu_cb_handle_t, warn, ConfObject, CpuInstrumentationSubscribeInterface, IntRegisterInterface, ProcessorInfoV2Interface, }; use std::{ @@ -48,6 +48,7 @@ impl From for *mut cpu_cb_handle_t { } #[derive(Debug, Default)] +/// Container for various types of information about a running Windows OS pub struct WindowsOsInfo { /// Kernel info pub kernel_info: Option, @@ -65,11 +66,13 @@ pub struct WindowsOsInfo { } impl WindowsOsInfo { + /// Collect or refresh OS info. Typically run on new CR3 writes to refresh for + /// possibly-changed address space mappings. pub fn collect

( &mut self, processor: *mut ConfObject, download_directory: P, - user_debug_info: DebugInfoConfig, + user_debug_info: &mut DebugInfoConfig, source_cache: &SourceCache, ) -> Result<()> where @@ -112,7 +115,7 @@ impl WindowsOsInfo { kernel_base, download_directory.as_ref(), &mut self.not_found_full_name_cache, - user_debug_info.clone(), + user_debug_info, )?); } @@ -127,7 +130,7 @@ impl WindowsOsInfo { processor, download_directory.as_ref(), &mut self.not_found_full_name_cache, - user_debug_info.clone(), + user_debug_info, )?, ); @@ -142,7 +145,7 @@ impl WindowsOsInfo { processor, download_directory.as_ref(), &mut self.not_found_full_name_cache, - user_debug_info.clone(), + user_debug_info, )?, ); @@ -151,8 +154,20 @@ impl WindowsOsInfo { .get_mut(&processor_nr) .ok_or_else(|| anyhow!("No modules for processor {processor_nr}"))? .iter_mut() - .map(|m| m.intervals(source_cache)) - .collect::>>()? + .filter_map(|m| { + m.intervals(source_cache).ok().or_else(|| { + get_object("tsffs") + .and_then(|obj| { + warn!(obj, "Failed to get intervals for module {}", &m.full_name); + Err( + anyhow!("Failed to get intervals for module {}", &m.full_name) + .into(), + ) + }) + .ok() + }) + }) + .collect::>() .into_iter() .chain( self.kernel_info @@ -166,8 +181,24 @@ impl WindowsOsInfo { )? .modules .iter_mut() - .map(|m| m.intervals(source_cache)) - .collect::>>()?, + .filter_map(|m| { + m.intervals(source_cache).ok().or_else(|| { + get_object("tsffs") + .and_then(|obj| { + warn!( + obj, + "Failed to get intervals for module {}", &m.full_name + ); + Err(anyhow!( + "Failed to get intervals for module {}", + &m.full_name + ) + .into()) + }) + .ok() + }) + }) + .collect::>(), ) .flatten() .collect::>(); @@ -180,6 +211,23 @@ impl WindowsOsInfo { .filter(|e| filtered_elements.insert(e.range.clone())) .collect::>(); + // Populate elements into the coverage record set + elements.iter().map(|e| &e.value).for_each(|si| { + if let Some(first) = si.lines.first() { + let record = user_debug_info.coverage.get_or_insert_mut(&first.file_path); + record.add_function_if_not_exists( + first.start_line as usize, + si.lines.last().map(|l| l.end_line as usize), + &si.name, + ); + si.lines.iter().for_each(|l| { + (l.start_line..l.end_line).for_each(|line| { + record.add_line_if_not_exists(line as usize); + }); + }); + } + }); + self.symbol_lookup_trees.insert( processor_nr, elements.iter().cloned().collect::>(), @@ -190,6 +238,7 @@ impl WindowsOsInfo { } impl Tsffs { + /// Triggered on control register write to refresh windows OS information if necessary pub fn on_control_register_write_windows_symcov( &mut self, trigger_obj: *mut ConfObject, @@ -217,9 +266,10 @@ impl Tsffs { self.windows_os_info.collect( trigger_obj, &self.debuginfo_download_directory, - DebugInfoConfig { + &mut DebugInfoConfig { system: self.symbolic_coverage_system, user_debug_info: &self.debug_info, + coverage: &mut self.coverage, }, &self.source_file_cache, )?; diff --git a/src/os/windows/pdb.rs b/src/os/windows/pdb.rs index 2b6df113..9d6f2563 100644 --- a/src/os/windows/pdb.rs +++ b/src/os/windows/pdb.rs @@ -7,6 +7,7 @@ use crate::os::windows::util::{ #[derive(Debug, Clone)] #[repr(C)] +/// The GUID structure in a CV 7.0 header pub struct Guid { pub data0: u32, pub data1: u16, @@ -15,6 +16,7 @@ pub struct Guid { } #[derive(Debug, Clone)] +/// The header of a CV (coreview) 7.0 file pub struct CvInfoPdb70 { pub cv_signature: u32, pub signature: Guid, @@ -23,6 +25,7 @@ pub struct CvInfoPdb70 { } impl CvInfoPdb70 { + /// Parse a CV 7.0 header from a given address pub fn new(processor: *mut ConfObject, address: u64) -> Result { let cv_signature = read_virtual::(processor, address)?; let signature = @@ -49,6 +52,7 @@ impl CvInfoPdb70 { }) } + /// Parse a CV 7.0 header from a given address with a DTB pub fn new_dtb( processor: *mut ConfObject, directory_table_base: u64, @@ -100,6 +104,7 @@ impl CvInfoPdb70 { } #[derive(Debug, Clone)] +/// An export from a PE file pub struct Export { pub name: Option, pub offset: Option, diff --git a/src/os/windows/structs.rs b/src/os/windows/structs.rs index ddb885db..ce613a46 100644 --- a/src/os/windows/structs.rs +++ b/src/os/windows/structs.rs @@ -6,7 +6,9 @@ use std::{ use anyhow::{anyhow, bail, ensure, Result}; use raw_cstr::AsRawCstr; -use simics::{debug, get_attribute, get_interface, get_object, ConfObject, IntRegisterInterface}; +use simics::{ + debug, get_attribute, get_interface, get_object, info, ConfObject, IntRegisterInterface, +}; use vergilius::bindings::*; use windows::Win32::{Foundation::UNICODE_STRING, System::Kernel::LIST_ENTRY}; @@ -2677,8 +2679,14 @@ impl WindowsEProcess { eprocess.SeAuditProcessCreationInfo.ImageFileName as u64 } }; + + if object_name_information_addr == 0 { + return Ok("".to_string()); + } + let object_name_information = read_virtual::(processor, object_name_information_addr)?; + read_unicode_string( processor, object_name_information.Length as usize, @@ -2721,7 +2729,7 @@ impl WindowsEProcess { build: u32, download_directory: P, not_found_full_name_cache: &mut HashSet, - user_debug_info: DebugInfoConfig, + user_debug_info: &DebugInfoConfig, ) -> Result> where P: AsRef, @@ -2852,12 +2860,14 @@ impl WindowsEProcess { base, download_directory.as_ref(), not_found_full_name_cache, - user_debug_info.clone(), + user_debug_info, ) }) .ok() .flatten(); + debug!(get_object("tsffs")?, "Found module: {}", full_name); + modules.push(ProcessModule { base, size, diff --git a/src/os/windows/util.rs b/src/os/windows/util.rs index 8b7ab41f..83cf3a92 100644 --- a/src/os/windows/util.rs +++ b/src/os/windows/util.rs @@ -8,6 +8,7 @@ use super::paging::{ PTE, VIRTUAL_ADDRESS, }; +/// Read from a virtual address pub fn read_virtual(processor: *mut ConfObject, virtual_address: u64) -> Result where T: Sized, @@ -35,6 +36,7 @@ where } } +/// Read from a physical address pub fn read_physical(processor: *mut ConfObject, physical_address: u64) -> Result { let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(processor)?; @@ -57,6 +59,7 @@ pub fn read_physical(processor: *mut ConfObject, physical_address: u64) -> Re } } +/// Read from a virtual address with a specific directory table base pub fn read_virtual_dtb( processor: *mut ConfObject, directory_table_base: u64, @@ -66,6 +69,7 @@ pub fn read_virtual_dtb( read_physical(processor, physical_address) } +/// Translate a virtual to physical address using a directory table base pub fn virtual_to_physical( processor: *mut ConfObject, directory_table_base: u64, @@ -147,6 +151,10 @@ pub fn read_unicode_string( length: usize, buffer: *const u16, ) -> Result { + if length == 0 || buffer.is_null() { + return Ok(String::new()); + } + let mut string = Vec::new(); let mut address = buffer as u64; @@ -170,6 +178,9 @@ pub fn read_unicode_string_dtb( buffer: *const u16, directory_table_base: u64, ) -> Result { + if length == 0 || buffer.is_null() { + return Ok(String::new()); + } let mut string = Vec::new(); let mut address = buffer as u64; @@ -188,6 +199,9 @@ pub fn read_unicode_string_dtb( } pub fn read_nul_terminated_string(processor: *mut ConfObject, address: u64) -> Result { + if address == 0 { + return Ok(String::new()); + } let mut string = String::new(); let mut address = address; @@ -210,6 +224,9 @@ pub fn read_nul_terminated_string_dtb( address: u64, directory_table_base: u64, ) -> Result { + if address == 0 { + return Ok(String::new()); + } let mut string = String::new(); let mut address = address; diff --git a/src/source_cov/html.rs b/src/source_cov/html.rs index ac493a05..8937a2c4 100644 --- a/src/source_cov/html.rs +++ b/src/source_cov/html.rs @@ -36,7 +36,6 @@ define! { Style { } - CurrentView(summary: HtmlSummaryInfo) { table { tr { diff --git a/src/source_cov/lcov.rs b/src/source_cov/lcov.rs index 20e63952..823dd551 100644 --- a/src/source_cov/lcov.rs +++ b/src/source_cov/lcov.rs @@ -1,13 +1,11 @@ use anyhow::{anyhow, Result}; -use markup::Render; use petgraph::{ - dot::Dot, graph::{DiGraph, NodeIndex}, - visit::{DfsPostOrder, IntoNeighborsDirected}, - Direction, Graph, + visit::DfsPostOrder, + Direction, }; use std::{ - collections::{btree_map::Entry, BTreeMap, HashMap, HashSet}, + collections::{btree_map::Entry, BTreeMap, HashMap}, fs::{create_dir_all, read_to_string, write}, iter::repeat, path::{Component, Path, PathBuf}, @@ -361,11 +359,6 @@ impl Record { functions.sort_by(|a, b| a.name.cmp(&b.name)); functions } - - pub fn to_html(&self) -> Result { - let contents = read_to_string(self.source_file.0.as_path())?; - Ok(Default::default()) - } } #[derive(Debug, Clone, Default)] diff --git a/src/source_cov/mod.rs b/src/source_cov/mod.rs index 1f87f4d0..f465d160 100644 --- a/src/source_cov/mod.rs +++ b/src/source_cov/mod.rs @@ -5,6 +5,7 @@ use std::{ }; use anyhow::Result; +use lcov::Records; use md5::compute; use pdb::{FileChecksum, FileInfo}; use sha1::{Digest, Sha1}; @@ -17,7 +18,6 @@ pub(crate) mod lcov; #[derive(Debug, Clone, Default)] pub struct SourceCache { - file_paths: Vec, prefix_lookup: HashMap, PathBuf>, md5_lookup: HashMap, PathBuf>, sha1_lookup: HashMap, PathBuf>, @@ -70,7 +70,6 @@ impl SourceCache { } Ok(Self { - file_paths, prefix_lookup, md5_lookup, sha1_lookup, @@ -132,6 +131,3 @@ impl SourceCache { }) } } - -#[derive(Default)] -pub struct Coverage {} diff --git a/src/tracer/mod.rs b/src/tracer/mod.rs index 978bc72f..7feaf8d3 100644 --- a/src/tracer/mod.rs +++ b/src/tracer/mod.rs @@ -17,14 +17,14 @@ use simics::{ get_interface, trace, ProcessorInfoV2Interface, }; use std::{ - collections::HashMap, ffi::c_void, fmt::Display, hash::Hash, num::Wrapping, + collections::HashMap, ffi::c_void, fmt::Display, hash::Hash, num::Wrapping, path::PathBuf, slice::from_raw_parts, str::FromStr, }; use typed_builder::TypedBuilder; use crate::{arch::ArchitectureOperations, Tsffs}; -#[derive(Deserialize, Serialize, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Deserialize, Serialize, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(crate) struct ExecutionTraceSymbol { /// The symbol name pub symbol: String, @@ -32,11 +32,11 @@ pub(crate) struct ExecutionTraceSymbol { pub symbol_demangled: Option, /// The offset into the symbol pub offset: u64, - /// The containing module's name (usually the path to the executable) + /// The containing module's name (usually the path to the executable or dll) pub module: String, } -#[derive(Deserialize, Serialize, Debug, Default)] +#[derive(Clone, Deserialize, Serialize, Debug, Default)] pub(crate) struct ExecutionTrace(pub HashMap>); impl Hash for ExecutionTrace { @@ -50,7 +50,9 @@ impl Hash for ExecutionTrace { } } -#[derive(TypedBuilder, Deserialize, Serialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive( + Clone, TypedBuilder, Deserialize, Serialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, +)] pub(crate) struct ExecutionTraceEntry { pc: u64, #[builder(default, setter(into, strip_option))] @@ -407,6 +409,42 @@ impl Tsffs { .ok() .and_then(|s| s.demangle(&DemangleOptions::new()).ok()) }); + + let function_end_line = q.value.lines.last().map(|f| f.end_line); + // Update coverage record + if let Some(function_start_line) = q.value.lines.first() { + let record = self + .coverage + .get_or_insert_mut(&function_start_line.file_path); + record.add_function_if_not_exists( + function_start_line.start_line as usize, + function_end_line.map(|l| l as usize), + &q.value.name, + ); + q.value.lines.iter().for_each(|l| { + (l.start_line..l.end_line).for_each(|line| { + record.add_line_if_not_exists(line as usize); + }); + }); + let pc_line = q + .value + .lines + .iter() + .find(|l| { + l.rva <= pc - q.value.base + && l.rva + l.size as u64 >= pc - q.value.base + }) + .map(|l| l.start_line as usize); + if pc_line + .is_some_and(|pcl| function_start_line.start_line as usize == pcl) + { + // Increment function hit counter if we just hit the fn + record.increment_function_data(&q.value.name); + } + if let Some(pc_line) = pc_line { + record.increment_line(pc_line); + } + } ExecutionTraceSymbol { symbol: q.value.name.clone(), symbol_demangled, From 369ace31c6d090c4c93ac672844e7cf136b497c2 Mon Sep 17 00:00:00 2001 From: novafacing Date: Thu, 18 Jul 2024 16:04:54 -0700 Subject: [PATCH 07/14] Switch to only keep current process --- src/os/windows/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 6dd7f987..0faa8b26 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -52,8 +52,8 @@ impl From for *mut cpu_cb_handle_t { pub struct WindowsOsInfo { /// Kernel info pub kernel_info: Option, - /// Per-CPU process list, there may be overlap between them. - pub processes: HashMap>, + /// Per-CPU current process, there may be overlap between them. + pub processes: HashMap, /// Per-CPU kernel module list, there may be overlap between them. pub modules: HashMap>, /// Per-CPU Symbol lookup trees @@ -126,7 +126,7 @@ impl WindowsOsInfo { self.kernel_info .as_mut() .expect("Kernel Info must be set at this point") - .process_list( + .current_process( processor, download_directory.as_ref(), &mut self.not_found_full_name_cache, From 2f300d286d3d894b7a6a696e7f396379311d68b0 Mon Sep 17 00:00:00 2001 From: novafacing Date: Fri, 19 Jul 2024 16:41:14 -0700 Subject: [PATCH 08/14] Enable turning on symbolic cov separately from execution trace capture --- src/haps/mod.rs | 12 +++++ src/lib.rs | 63 ++++++++++++---------- src/log/mod.rs | 12 +++++ src/os/windows/debug_info.rs | 100 +++++++++++++++++++++-------------- src/os/windows/mod.rs | 15 ++++-- src/source_cov/html.rs | 10 ++++ src/source_cov/lcov.rs | 75 ++++++++++++++++++-------- src/source_cov/mod.rs | 7 ++- src/tracer/mod.rs | 65 ++++++++++++++++++++++- 9 files changed, 260 insertions(+), 99 deletions(-) diff --git a/src/haps/mod.rs b/src/haps/mod.rs index a6579bd0..77370a36 100644 --- a/src/haps/mod.rs +++ b/src/haps/mod.rs @@ -173,6 +173,10 @@ impl Tsffs { self.save_execution_trace()?; } + if self.symbolic_coverage { + self.save_symbolic_coverage()?; + } + debug!(self.as_conf_object(), "Resuming simulation"); run_alone(|| { @@ -380,6 +384,10 @@ impl Tsffs { self.save_execution_trace()?; } + if self.symbolic_coverage { + self.save_symbolic_coverage()?; + } + debug!(self.as_conf_object(), "Resuming simulation"); run_alone(|| { @@ -481,6 +489,10 @@ impl Tsffs { self.save_execution_trace()?; } + if self.symbolic_coverage { + self.save_symbolic_coverage()?; + } + debug!(self.as_conf_object(), "Resuming simulation"); run_alone(|| { diff --git a/src/lib.rs b/src/lib.rs index 0872948f..089af780 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -935,41 +935,46 @@ impl Tsffs { Ok(()) } - /// Save the current execution trace to a file - pub fn save_execution_trace(&mut self) -> Result<()> { - let execution_trace = self.execution_trace.clone(); - let execution_trace_dir = self.execution_trace_directory.clone(); - let coverage = self.coverage.clone(); - let symbolic_coverage_directory = self.symbolic_coverage_directory.clone(); - // We just fire and forget this thread -- we won't know if it fails but it's basically - // guaranteed not to. This stops us from having to wait every exec to write a huge file. - spawn(move || { - let mut hasher = DefaultHasher::new(); - execution_trace.hash(&mut hasher); - let hash = hasher.finish(); - - if !execution_trace_dir.is_dir() { - create_dir_all(&execution_trace_dir)?; - } + pub fn save_symbolic_coverage(&mut self) -> Result<()> { + if self.symbolic_coverage_directory.is_dir() { + create_dir_all(&self.symbolic_coverage_directory)?; + } - let trace_path = execution_trace_dir.join(format!("{:x}.json", hash)); + debug!( + self.as_conf_object(), + "Saving symbolic coverage to {}", + self.symbolic_coverage_directory.display() + ); - if !trace_path.exists() { - let trace_file = File::create(&trace_path)?; - println!("Saving execution trace to {}", trace_path.display()); - to_writer(trace_file, &execution_trace)?; - } + self.coverage.to_html(&self.symbolic_coverage_directory)?; - if !symbolic_coverage_directory.is_dir() { - create_dir_all(&symbolic_coverage_directory)?; - } + debug!( + self.as_conf_object(), + "Symbolic coverage saved to {}", + self.symbolic_coverage_directory.display() + ); + + Ok(()) + } - println!("Saving coverage information to symbolic coverage directory {symbolic_coverage_directory:?}"); - coverage.to_html(&symbolic_coverage_directory)?; + /// Save the current execution trace to a file + pub fn save_execution_trace(&mut self) -> Result<()> { + let mut hasher = DefaultHasher::new(); + self.execution_trace.hash(&mut hasher); + let hash = hasher.finish(); - Ok::<(), anyhow::Error>(()) - }); + if !self.execution_trace_directory.is_dir() { + create_dir_all(&self.execution_trace_directory)?; + } + let trace_path = self + .execution_trace_directory + .join(format!("{:x}.json", hash)); + + if !trace_path.exists() { + let trace_file = File::create(&trace_path)?; + to_writer(trace_file, &self.execution_trace)?; + } Ok(()) } } diff --git a/src/log/mod.rs b/src/log/mod.rs index 991d4cc9..245963ce 100644 --- a/src/log/mod.rs +++ b/src/log/mod.rs @@ -152,6 +152,10 @@ impl Tsffs { if self.save_interesting_execution_traces { self.save_execution_trace()?; } + + if self.symbolic_coverage { + self.save_symbolic_coverage()?; + } } FuzzerMessage::Crash { indices, input } => { info!( @@ -184,6 +188,10 @@ impl Tsffs { if self.save_solution_execution_traces { self.save_execution_trace()?; } + + if self.symbolic_coverage { + self.save_symbolic_coverage()?; + } } FuzzerMessage::Timeout { indices, input } => { info!( @@ -216,6 +224,10 @@ impl Tsffs { if self.save_timeout_execution_traces { self.save_execution_trace()?; } + + if self.symbolic_coverage { + self.save_symbolic_coverage()?; + } } } diff --git a/src/os/windows/debug_info.rs b/src/os/windows/debug_info.rs index ff78bcfe..3a80b6ef 100644 --- a/src/os/windows/debug_info.rs +++ b/src/os/windows/debug_info.rs @@ -11,13 +11,13 @@ use std::{ }; use lending_iterator::{windows_mut, LendingIterator}; -use simics::{debug, get_object, info, ConfObject}; +use simics::{debug, get_object, info, warn, ConfObject}; use windows::Win32::System::{ Diagnostics::Debug::{ IMAGE_DEBUG_DIRECTORY, IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DIRECTORY_ENTRY_DEBUG, IMAGE_NT_HEADERS64, }, - SystemServices::IMAGE_DOS_HEADER, + SystemServices::{FILE_NOTIFY_FULL_INFORMATION, IMAGE_DOS_HEADER}, }; use crate::{os::DebugInfoConfig, source_cov::SourceCache}; @@ -448,14 +448,18 @@ impl ProcessModule { ) }) .collect::>(); - Some(SymbolInfo::new( + let info = SymbolInfo::new( procedure_rva.0 as u64, self.base, procedure_symbol.len as u64, symbol_name.to_string().to_string(), self.full_name.clone(), lines, - )) + ); + if let Ok(o) = get_object("tsffs") { + debug!(o, "Got symbol: {:?}", info); + } + Some(info) }) .collect::>() }) @@ -604,49 +608,67 @@ impl Module { .iterator() .filter_map(|line| line.ok()) .filter_map(|line_info| { - line_program - .get_file_info(line_info.file_index) - .ok() - .and_then(|line_file_info| { - string_table - .get(line_file_info.name) - .map(|line_file_name| (line_file_info, line_file_name)) - .ok() - }) - .and_then(|(line_file_info, line_file_name)| { - line_info.offset.to_rva(&address_map).map(|line_rva| { - (line_file_info, line_file_name, line_rva, line_info) - }) - }) - .and_then( - |(line_file_info, line_file_name, line_rva, line_info)| { - source_cache - .lookup_pdb( - &line_file_info, - &line_file_name.to_string(), - ) - .ok() - .flatten() - .map(|p| p.to_path_buf()) - .map(|file_path| LineInfo { - rva: line_rva.0 as u64, - size: line_info.length.unwrap_or(1), - file_path, - start_line: line_info.line_start, - end_line: line_info.line_end, - }) - }, - ) + let Ok(line_file_info) = + line_program.get_file_info(line_info.file_index) + else { + if let Ok(o) = get_object("tsffs") { + debug!(o, "No file info for line {:?}", line_info); + } + return None; + }; + + let Ok(line_file_name) = string_table.get(line_file_info.name) + else { + if let Ok(o) = get_object("tsffs") { + debug!(o, "No file name for line {:?}", line_file_info); + } + return None; + }; + + let Some(line_rva) = line_info.offset.to_rva(&address_map) else { + if let Ok(o) = get_object("tsffs") { + debug!(o, "No RVA for line {:?}", line_info); + } + return None; + }; + + let Ok(Some(source_file)) = source_cache + .lookup_pdb(&line_file_info, &line_file_name.to_string()) + else { + if let Ok(o) = get_object("tsffs") { + debug!(o, "No source file path for line {:?}", line_info); + } + return None; + }; + + let info = LineInfo { + rva: line_rva.0 as u64, + size: line_info.length.unwrap_or(1), + file_path: source_file.to_path_buf(), + start_line: line_info.line_start, + end_line: line_info.line_end, + }; + if let Ok(o) = get_object("tsffs") { + debug!(o, "Got line info {:?}", line_info); + } + + Some(info) }) .collect::>(); - Some(SymbolInfo::new( + + let info = SymbolInfo::new( procedure_rva.0 as u64, self.base, procedure_symbol.len as u64, symbol_name.to_string().to_string(), self.full_name.clone(), lines, - )) + ); + + if let Ok(o) = get_object("tsffs") { + debug!(o, "Got symbol: {:?}", info); + } + Some(info) }) .collect::>() }) diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 0faa8b26..484f5cba 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -5,8 +5,9 @@ use intervaltree::IntervalTree; use kernel::{find_kernel_with_idt, KernelInfo}; use raw_cstr::AsRawCstr; use simics::{ - get_interface, get_object, get_processor_number, info, sys::cpu_cb_handle_t, warn, ConfObject, - CpuInstrumentationSubscribeInterface, IntRegisterInterface, ProcessorInfoV2Interface, + debug, get_interface, get_object, get_processor_number, info, sys::cpu_cb_handle_t, warn, + ConfObject, CpuInstrumentationSubscribeInterface, IntRegisterInterface, + ProcessorInfoV2Interface, }; use std::{ collections::{hash_map::Entry, HashMap, HashSet}, @@ -158,7 +159,10 @@ impl WindowsOsInfo { m.intervals(source_cache).ok().or_else(|| { get_object("tsffs") .and_then(|obj| { - warn!(obj, "Failed to get intervals for module {}", &m.full_name); + debug!( + obj, + "Failed (or skipped) getting intervals for module {}", &m.full_name + ); Err( anyhow!("Failed to get intervals for module {}", &m.full_name) .into(), @@ -185,9 +189,10 @@ impl WindowsOsInfo { m.intervals(source_cache).ok().or_else(|| { get_object("tsffs") .and_then(|obj| { - warn!( + debug!( obj, - "Failed to get intervals for module {}", &m.full_name + "Failed (or skipped) getting intervals for module {}", + &m.full_name ); Err(anyhow!( "Failed to get intervals for module {}", diff --git a/src/source_cov/html.rs b/src/source_cov/html.rs index 8937a2c4..32d4ff13 100644 --- a/src/source_cov/html.rs +++ b/src/source_cov/html.rs @@ -30,6 +30,16 @@ pub(crate) struct HtmlSummaryInfo { pub(crate) hit_functions: usize, } +impl std::fmt::Display for HtmlSummaryInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "HtmlSummaryInfo {{ is_dir: {}, top_level: {:?}, parent: {:?}, filename: {:?}, total_lines: {}, hit_lines: {}, total_functions: {}, hit_functions: {} }}", + self.is_dir, self.top_level, self.parent, self.filename, self.total_lines, self.hit_lines, self.total_functions, self.hit_functions + ) + } +} + define! { Head { } diff --git a/src/source_cov/lcov.rs b/src/source_cov/lcov.rs index 823dd551..dd15429a 100644 --- a/src/source_cov/lcov.rs +++ b/src/source_cov/lcov.rs @@ -6,7 +6,7 @@ use petgraph::{ }; use std::{ collections::{btree_map::Entry, BTreeMap, HashMap}, - fs::{create_dir_all, read_to_string, write}, + fs::{create_dir_all, read, write}, iter::repeat, path::{Component, Path, PathBuf}, }; @@ -238,9 +238,12 @@ impl Record { let entry = self .function_data .entry(name.as_ref().to_string()) - .or_insert_with(|| FunctionDataRecordEntry { - hits: 0, - name: name.as_ref().to_string(), + .or_insert_with(|| { + self.functions_found.0 += 1; + FunctionDataRecordEntry { + hits: 0, + name: name.as_ref().to_string(), + } }); if entry.hits == 0 { @@ -266,14 +269,14 @@ impl Record { } pub fn increment_line(&mut self, line_number: usize) { - let entry = self - .lines - .entry(line_number) - .or_insert_with(|| LineRecordEntry { + let entry = self.lines.entry(line_number).or_insert_with(|| { + self.lines_found.0 += 1; + LineRecordEntry { line_number, hit_count: 0, checksum: None, - }); + } + }); if entry.hit_count == 0 { self.lines_hit.0 += 1; @@ -329,7 +332,8 @@ impl Record { } pub fn lines(&self) -> Result> { - let contents = read_to_string(self.source_file.0.as_path())?; + let contents_raw = read(self.source_file.0.as_path())?; + let contents = String::from_utf8_lossy(&contents_raw); let lines = contents .lines() .enumerate() @@ -388,6 +392,31 @@ impl std::fmt::Display for Records { } } +struct GraphNode { + pub path: PathBuf, + pub summary: Option, +} + +impl GraphNode { + pub fn new(path: PathBuf, summary: Option) -> Self { + Self { path, summary } + } +} + +impl std::fmt::Display for GraphNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.path.to_string_lossy()) + } +} + +struct GraphEdge {} + +impl std::fmt::Display for GraphEdge { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "") + } +} + impl Records { /// Output LCOV records to HTML format, like a mini genhtml pub fn to_html

(&self, output_directory: P) -> Result<()> @@ -395,7 +424,7 @@ impl Records { P: AsRef, { // Build a tree out of the output paths - let mut graph = DiGraph::<(PathBuf, Option), ()>::new(); + let mut graph = DiGraph::::new(); let mut node_ids = HashMap::::new(); let entries = self @@ -422,7 +451,7 @@ impl Records { if let std::collections::hash_map::Entry::Vacant(entry) = node_ids.entry(output_path.clone()) { - entry.insert(graph.add_node((output_path.clone(), None))); + entry.insert(graph.add_node(GraphNode::new(output_path.clone(), None))); } let mut path = output_path.as_path(); @@ -430,7 +459,7 @@ impl Records { if let std::collections::hash_map::Entry::Vacant(entry) = node_ids.entry(parent.to_path_buf()) { - entry.insert(graph.add_node((parent.to_path_buf(), None))); + entry.insert(graph.add_node(GraphNode::new(parent.to_path_buf(), None))); } if graph @@ -451,7 +480,7 @@ impl Records { *node_ids .get(path) .ok_or_else(|| anyhow!("output path not found"))?, - (), + GraphEdge {}, ); } @@ -479,10 +508,13 @@ impl Records { let mut traversal = DfsPostOrder::new(&graph, *root); while let Some(node) = traversal.next(&graph) { + let weight = graph + .node_weight(node) + .ok_or_else(|| anyhow!("No weight for node"))?; let path = graph .node_weight(node) .ok_or_else(|| anyhow!("No weight for node"))? - .0 + .path .clone(); // Calculate the depth of this path from the output directory if let Some(record) = entries.get(path.as_path()) { @@ -502,7 +534,7 @@ impl Records { graph .node_weight_mut(node) .ok_or_else(|| anyhow!("No weight for node"))? - .1 = Some(summary.clone()); + .summary = Some(summary.clone()); let lines = record.lines()?; let functions = record.functions(); let page = Page { @@ -543,10 +575,9 @@ impl Records { let summary = graph .node_weight(neighbor) .ok_or_else(|| anyhow!("No weight for node"))? - .1 + .summary .as_ref() .ok_or_else(|| anyhow!("No summary for node"))?; - println!("Adding neighbor {:?}", summary); Ok::<(usize, usize, usize, usize), anyhow::Error>(( total_lines + summary.total_lines, hit_lines + summary.hit_lines, @@ -585,7 +616,7 @@ impl Records { .node_weight(neighbor) .ok_or_else(|| anyhow!("No weight for node")) .ok() - .and_then(|weight| weight.1.as_ref().cloned()) + .and_then(|weight| weight.summary.as_ref().cloned()) }) .collect(), }, @@ -595,15 +626,15 @@ impl Records { graph .node_weight_mut(node) .ok_or_else(|| anyhow!("No weight for node"))? - .1 = Some(summary); + .summary = Some(summary); } } // NOTE: Left for easy debugging of the directory graph - // let dot = Dot::new(&graph); + // let dot = petgraph::dot::Dot::new(&graph); // write( // output_directory.as_ref().join("graph.dot"), - // format!("{:?}", dot), + // format!("{}", dot), // )?; Ok(()) diff --git a/src/source_cov/mod.rs b/src/source_cov/mod.rs index f465d160..1ee4d58d 100644 --- a/src/source_cov/mod.rs +++ b/src/source_cov/mod.rs @@ -5,11 +5,11 @@ use std::{ }; use anyhow::Result; -use lcov::Records; use md5::compute; use pdb::{FileChecksum, FileInfo}; use sha1::{Digest, Sha1}; use sha2::Sha256; +use simics::{debug, get_object}; use typed_path::{TypedComponent, TypedPath, UnixComponent, WindowsComponent}; use walkdir::WalkDir; @@ -69,6 +69,10 @@ impl SourceCache { } } + if let Ok(o) = get_object("tsffs") { + debug!(o, "Cached {} source files", file_paths.len()); + } + Ok(Self { prefix_lookup, md5_lookup, @@ -99,7 +103,6 @@ impl SourceCache { .collect::>(); while !file_name_components.is_empty() { - println!("Looking up {:?}", file_name_components); if let Some(file_path) = self.prefix_lookup.get(&file_name_components) { return Some(file_path); } diff --git a/src/tracer/mod.rs b/src/tracer/mod.rs index 7feaf8d3..164345cf 100644 --- a/src/tracer/mod.rs +++ b/src/tracer/mod.rs @@ -17,7 +17,7 @@ use simics::{ get_interface, trace, ProcessorInfoV2Interface, }; use std::{ - collections::HashMap, ffi::c_void, fmt::Display, hash::Hash, num::Wrapping, path::PathBuf, + collections::HashMap, ffi::c_void, fmt::Display, hash::Hash, num::Wrapping, slice::from_raw_parts, str::FromStr, }; use typed_builder::TypedBuilder; @@ -391,7 +391,13 @@ impl Tsffs { } } - let symcov = if self.coverage_enabled && self.windows && self.symbolic_coverage { + let symcov = if self.coverage_enabled + && self.windows + && self.symbolic_coverage + && (self.save_all_execution_traces + || self.save_interesting_execution_traces + || self.save_solution_execution_traces) + { // Get the current instruction address let mut processor_information_v2 = get_interface::(cpu)?; let pc = processor_information_v2.get_program_counter()?; @@ -453,6 +459,61 @@ impl Tsffs { } }) }) + } else if self.coverage_enabled && self.symbolic_coverage { + let mut processor_information_v2 = get_interface::(cpu)?; + let pc = processor_information_v2.get_program_counter()?; + if let Some(lookup_tree) = self + .windows_os_info + .symbol_lookup_trees + .get(&processor_number) + { + if let Some(q) = lookup_tree.query(pc..pc + 1).next() { + let symbol_demangled = try_demangle(&q.value.name) + .map(|d| d.to_string()) + .ok() + .or_else(|| { + Symbol::new(&q.value.name) + .ok() + .and_then(|s| s.demangle(&DemangleOptions::new()).ok()) + }); + + let function_end_line = q.value.lines.last().map(|f| f.end_line); + // Update coverage record + if let Some(function_start_line) = q.value.lines.first() { + let record = self + .coverage + .get_or_insert_mut(&function_start_line.file_path); + record.add_function_if_not_exists( + function_start_line.start_line as usize, + function_end_line.map(|l| l as usize), + symbol_demangled.as_ref().unwrap_or(&q.value.name), + ); + q.value.lines.iter().for_each(|l| { + (l.start_line..l.end_line).for_each(|line| { + record.add_line_if_not_exists(line as usize); + }); + }); + let pc_line = q + .value + .lines + .iter() + .find(|l| { + l.rva <= pc - q.value.base + && l.rva + l.size as u64 >= pc - q.value.base + }) + .map(|l| l.start_line as usize); + if pc_line.is_some_and(|pcl| function_start_line.start_line as usize == pcl) + { + // Increment function hit counter if we just hit the fn + record.increment_function_data(&q.value.name); + } + if let Some(pc_line) = pc_line { + record.increment_line(pc_line); + } + } + } + } + None } else { None }; From 1170c1773515e5fb5726b925568ced45ec591466 Mon Sep 17 00:00:00 2001 From: novafacing Date: Mon, 22 Jul 2024 11:31:02 -0700 Subject: [PATCH 09/14] Update function hit and line mapping calculations --- src/lib.rs | 2 +- src/os/windows/debug_info.rs | 7 +- src/os/windows/mod.rs | 2 +- src/source_cov/html.rs | 123 ++++++++++++------------------ src/source_cov/lcov.rs | 3 - src/tracer/mod.rs | 143 +++++++++++++++-------------------- 6 files changed, 110 insertions(+), 170 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 089af780..6b676d38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,7 +75,7 @@ use std::{ ptr::null_mut, str::FromStr, sync::mpsc::{Receiver, Sender}, - thread::{spawn, JoinHandle}, + thread::JoinHandle, time::SystemTime, }; use tracer::{ diff --git a/src/os/windows/debug_info.rs b/src/os/windows/debug_info.rs index 3a80b6ef..a73d4ad6 100644 --- a/src/os/windows/debug_info.rs +++ b/src/os/windows/debug_info.rs @@ -456,9 +456,7 @@ impl ProcessModule { self.full_name.clone(), lines, ); - if let Ok(o) = get_object("tsffs") { - debug!(o, "Got symbol: {:?}", info); - } + Some(info) }) .collect::>() @@ -665,9 +663,6 @@ impl Module { lines, ); - if let Ok(o) = get_object("tsffs") { - debug!(o, "Got symbol: {:?}", info); - } Some(info) }) .collect::>() diff --git a/src/os/windows/mod.rs b/src/os/windows/mod.rs index 484f5cba..2aaecbac 100644 --- a/src/os/windows/mod.rs +++ b/src/os/windows/mod.rs @@ -226,7 +226,7 @@ impl WindowsOsInfo { &si.name, ); si.lines.iter().for_each(|l| { - (l.start_line..l.end_line).for_each(|line| { + (l.start_line..=l.end_line).for_each(|line| { record.add_line_if_not_exists(line as usize); }); }); diff --git a/src/source_cov/html.rs b/src/source_cov/html.rs index 32d4ff13..7be5172e 100644 --- a/src/source_cov/html.rs +++ b/src/source_cov/html.rs @@ -104,31 +104,22 @@ define! { th[style = "text-align: right;"] { "Lines" } - @if (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 >= 90.0 { + @let ratio = if summary.total_lines == 0 { + 0.0 + } else { + summary.hit_lines as f32 / summary.total_lines as f32 * 100.0 + }; + @if ratio >= 90.0 { td[style = "text-align: right; background-color: #a7fc9d;"] { - @format!( - "{:.02}%", - (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 - ) + @format!("{ratio:.02}%") } - } else if (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 >= 30.0 { + } else if ratio >= 30.0 { td[style = "text-align: right; background-color: #ffea20;"] { - @format!( - "{:.02}%", - (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 - ) + @format!("{ratio:.02}%") } } else { td[style = "text-align: right; background-color: #ff6230;"] { - @format!( - "{:.02}%", - (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 - ) + @format!("{ratio:.02}%") } } @@ -143,32 +134,23 @@ define! { th[style = "text-align: right;"] { "Functions" } - @if (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 >= 90.0 { + @let ratio = if summary.total_functions == 0 { + 0.0 + } else { + summary.hit_functions as f32 / summary.total_functions as f32 * 100.0 + }; + @if ratio >= 90.0 { td[style = "text-align: right; background-color: #a7fc9d;"] { - @format!( - "{:.02}%", - (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 - ) + @format!("{ratio:.02}%") } - } else if (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 >= 30.0 { + } else if ratio >= 30.0 { td[style = "text-align: right; background-color: #ffea20;"] { - @format!( - "{:.02}%", - (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 - ) + @format!("{ratio:.02}%") } } else { td[style = "text-align: right; background-color: #ff6230;"] { - @format!( - "{:.02}%", - (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 - ) + @format!("{ratio:.02}%") } } td[style = "text-align: right; background-color: #cad7fe;"] { @@ -351,69 +333,58 @@ define! { "" } } - @if (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 >= 90.0 { + @let line_ratio = if summary.total_lines == 0 { + 0.0 + } else { + summary.hit_lines as f32 / summary.total_lines as f32 * 100.0 + }; + + @let function_ratio = if summary.total_functions == 0 { + 0.0 + } else { + summary.hit_functions as f32 / summary.total_functions as f32 * 100.0 + }; + + @if line_ratio >= 90.0 { td[style = "text-align: right; background-color: #a7fc9d;"] { - @format!( - "{:.02}%", - (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 - ) + @format!("{line_ratio:.02}%") } - } else if (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 >= 30.0 { + } else if line_ratio >= 30.0 { td[style = "text-align: right; background-color: #ffea20;"] { - @format!( - "{:.02}%", - (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 - ) + @format!("{line_ratio:.02}%") } } else { td[style = "text-align: right; background-color: #ff6230;"] { - @format!( - "{:.02}%", - (summary.hit_lines as f32 / summary.total_lines as f32) - * 100.0 - ) + @format!("{line_ratio:.02}%") } } + td[style = "text-align: right; background-color: #cad7fe;"] { @summary.total_lines } + td[style = "text-align: right; background-color: #cad7fe;"] { @summary.hit_lines } - @if (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 >= 90.0 { + + @if function_ratio >= 90.0 { td[style = "text-align: right; background-color: #a7fc9d;"] { - @format!( - "{:.02}%", - (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 - ) + @format!("{function_ratio:.02}%") } - } else if (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 >= 30.0 { + } else if function_ratio >= 30.0 { td[style = "text-align: right; background-color: #ffea20;"] { - @format!( - "{:.02}%", - (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 - ) + @format!("{function_ratio:.02}%") } } else { td[style = "text-align: right; background-color: #ff6230;"] { - @format!( - "{:.02}%", - (summary.hit_functions as f32 / summary.total_functions as f32) - * 100.0 - ) + @format!("{function_ratio:.02}%") } } + td[style = "text-align: right; background-color: #cad7fe;"] { @summary.total_functions } + td[style = "text-align: right; background-color: #cad7fe;"] { @summary.hit_functions } diff --git a/src/source_cov/lcov.rs b/src/source_cov/lcov.rs index dd15429a..7e9f4126 100644 --- a/src/source_cov/lcov.rs +++ b/src/source_cov/lcov.rs @@ -508,9 +508,6 @@ impl Records { let mut traversal = DfsPostOrder::new(&graph, *root); while let Some(node) = traversal.next(&graph) { - let weight = graph - .node_weight(node) - .ok_or_else(|| anyhow!("No weight for node"))?; let path = graph .node_weight(node) .ok_or_else(|| anyhow!("No weight for node"))? diff --git a/src/tracer/mod.rs b/src/tracer/mod.rs index 164345cf..9f0f16e3 100644 --- a/src/tracer/mod.rs +++ b/src/tracer/mod.rs @@ -405,59 +405,54 @@ impl Tsffs { .symbol_lookup_trees .get(&processor_number) .and_then(|lookup_tree| { - lookup_tree.query(pc..pc + 1).next().map(|q| { - let offset = pc - q.value.base + q.value.rva; - let symbol_demangled = try_demangle(&q.value.name) - .map(|d| d.to_string()) - .ok() - .or_else(|| { - Symbol::new(&q.value.name) - .ok() - .and_then(|s| s.demangle(&DemangleOptions::new()).ok()) - }); - - let function_end_line = q.value.lines.last().map(|f| f.end_line); - // Update coverage record - if let Some(function_start_line) = q.value.lines.first() { - let record = self - .coverage - .get_or_insert_mut(&function_start_line.file_path); - record.add_function_if_not_exists( - function_start_line.start_line as usize, - function_end_line.map(|l| l as usize), - &q.value.name, - ); - q.value.lines.iter().for_each(|l| { - (l.start_line..l.end_line).for_each(|line| { - record.add_line_if_not_exists(line as usize); + lookup_tree + .query(pc..pc + 1) + .next() + .map(|symbol_for_query| { + let offset = + pc - symbol_for_query.value.base + symbol_for_query.value.rva; + let symbol_demangled = try_demangle(&symbol_for_query.value.name) + .map(|d| d.to_string()) + .ok() + .or_else(|| { + Symbol::new(&symbol_for_query.value.name) + .ok() + .and_then(|s| s.demangle(&DemangleOptions::new()).ok()) }); - }); - let pc_line = q - .value - .lines - .iter() - .find(|l| { - l.rva <= pc - q.value.base - && l.rva + l.size as u64 >= pc - q.value.base - }) - .map(|l| l.start_line as usize); - if pc_line - .is_some_and(|pcl| function_start_line.start_line as usize == pcl) + + if let Some(function_start_line) = symbol_for_query.value.lines.first() { - // Increment function hit counter if we just hit the fn - record.increment_function_data(&q.value.name); + let record = self + .coverage + .get_or_insert_mut(&function_start_line.file_path); + let pc_lines = symbol_for_query + .value + .lines + .iter() + .filter(|line_info| { + pc - symbol_for_query.value.base >= line_info.rva + && pc - symbol_for_query.value.base + < line_info.rva + line_info.size as u64 + }) + .flat_map(|l| (l.start_line..=l.end_line).map(|i| i as usize)) + .collect::>(); + + if pc_lines.contains(&(function_start_line.start_line as usize)) { + // Increment function hit counter if we just hit the fn + record.increment_function_data(&symbol_for_query.value.name); + } + + pc_lines.iter().for_each(|line| { + record.increment_line(*line); + }); } - if let Some(pc_line) = pc_line { - record.increment_line(pc_line); + ExecutionTraceSymbol { + symbol: symbol_for_query.value.name.clone(), + symbol_demangled, + offset, + module: symbol_for_query.value.module.clone(), } - } - ExecutionTraceSymbol { - symbol: q.value.name.clone(), - symbol_demangled, - offset, - module: q.value.module.clone(), - } - }) + }) }) } else if self.coverage_enabled && self.symbolic_coverage { let mut processor_information_v2 = get_interface::(cpu)?; @@ -467,49 +462,31 @@ impl Tsffs { .symbol_lookup_trees .get(&processor_number) { - if let Some(q) = lookup_tree.query(pc..pc + 1).next() { - let symbol_demangled = try_demangle(&q.value.name) - .map(|d| d.to_string()) - .ok() - .or_else(|| { - Symbol::new(&q.value.name) - .ok() - .and_then(|s| s.demangle(&DemangleOptions::new()).ok()) - }); - - let function_end_line = q.value.lines.last().map(|f| f.end_line); - // Update coverage record - if let Some(function_start_line) = q.value.lines.first() { + if let Some(symbol_for_query) = lookup_tree.query(pc..pc + 1).next() { + if let Some(function_start_line) = symbol_for_query.value.lines.first() { let record = self .coverage .get_or_insert_mut(&function_start_line.file_path); - record.add_function_if_not_exists( - function_start_line.start_line as usize, - function_end_line.map(|l| l as usize), - symbol_demangled.as_ref().unwrap_or(&q.value.name), - ); - q.value.lines.iter().for_each(|l| { - (l.start_line..l.end_line).for_each(|line| { - record.add_line_if_not_exists(line as usize); - }); - }); - let pc_line = q + let pc_lines = symbol_for_query .value .lines .iter() - .find(|l| { - l.rva <= pc - q.value.base - && l.rva + l.size as u64 >= pc - q.value.base + .filter(|line_info| { + pc - symbol_for_query.value.base >= line_info.rva + && pc - symbol_for_query.value.base + < line_info.rva + line_info.size as u64 }) - .map(|l| l.start_line as usize); - if pc_line.is_some_and(|pcl| function_start_line.start_line as usize == pcl) - { + .flat_map(|l| (l.start_line..=l.end_line).map(|i| i as usize)) + .collect::>(); + + if pc_lines.contains(&(function_start_line.start_line as usize)) { // Increment function hit counter if we just hit the fn - record.increment_function_data(&q.value.name); - } - if let Some(pc_line) = pc_line { - record.increment_line(pc_line); + record.increment_function_data(&symbol_for_query.value.name); } + + pc_lines.iter().for_each(|line| { + record.increment_line(*line); + }); } } } From 650fff1cb4a070af62695a990b995a37a2b5cf45 Mon Sep 17 00:00:00 2001 From: novafacing Date: Thu, 8 Aug 2024 14:52:31 -0700 Subject: [PATCH 10/14] Update yaxpeax-x86 and yaxpeax-x86_64 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ce550592..99859491 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ yaxpeax-x86 = { version = "2.0.0", default-features = false, features = [ typed-builder = "0.20.0" raw-cstr = "0.1.4" goblin = "0.8.2" -yaxpeax-riscv = { git = "https://github.com/DrChat/yaxpeax-riscv", version = "0.1.0", features = [ +yaxpeax-riscv = { git = "https://github.com/novafacing/yaxpeax-riscv", version = "0.1.0", features = [ "serde", ], rev = "5973ff8" } crc32fast = "1.4.2" From 8e441bb2c9bff106da8898bedea4a2f815713d77 Mon Sep 17 00:00:00 2001 From: novafacing Date: Thu, 8 Aug 2024 14:54:00 -0700 Subject: [PATCH 11/14] Move back to upstream yaxpeax-riscv --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 99859491..ce550592 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ yaxpeax-x86 = { version = "2.0.0", default-features = false, features = [ typed-builder = "0.20.0" raw-cstr = "0.1.4" goblin = "0.8.2" -yaxpeax-riscv = { git = "https://github.com/novafacing/yaxpeax-riscv", version = "0.1.0", features = [ +yaxpeax-riscv = { git = "https://github.com/DrChat/yaxpeax-riscv", version = "0.1.0", features = [ "serde", ], rev = "5973ff8" } crc32fast = "1.4.2" From 89288593e66f74361714a1fe3823819ea47dca91 Mon Sep 17 00:00:00 2001 From: novafacing Date: Fri, 9 Aug 2024 13:30:41 -0700 Subject: [PATCH 12/14] Switch to windows-sys crate --- Cargo.toml | 7 +-- src/os/windows/debug_info.rs | 10 ++--- src/os/windows/kernel.rs | 10 ++--- src/os/windows/structs.rs | 82 ++++++++++++++++++------------------ 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ce550592..559c798f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,15 +77,16 @@ tracing = { version = "0.1.40", features = ["log"] } yaxpeax-arm = "0.3.0" chrono = "0.4.38" vergilius = "0.1.2" -windows = { version = "0.57.0", features = [ - "Win32_Foundation", +windows-sys = { features = [ + "Win32", "Win32_System", "Win32_System_SystemServices", "Win32_System_Diagnostics_Debug", "Win32_System_Diagnostics", "Win32_System_SystemInformation", "Win32_System_Kernel", -] } + "Win32_Foundation", +], version = "0.59.0" } reqwest = { version = "0.12.5", features = [ "blocking", # NOTE: rustls is used because native-tls does not build with the diff --git a/src/os/windows/debug_info.rs b/src/os/windows/debug_info.rs index a73d4ad6..ea619e0b 100644 --- a/src/os/windows/debug_info.rs +++ b/src/os/windows/debug_info.rs @@ -12,7 +12,7 @@ use std::{ use lending_iterator::{windows_mut, LendingIterator}; use simics::{debug, get_object, info, warn, ConfObject}; -use windows::Win32::System::{ +use windows_sys::Win32::System::{ Diagnostics::Debug::{ IMAGE_DEBUG_DIRECTORY, IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DIRECTORY_ENTRY_DEBUG, IMAGE_NT_HEADERS64, @@ -78,10 +78,10 @@ impl<'a> DebugInfo<'a> { let nt_header = read_virtual::(processor, base + dos_header.e_lfanew as u64)?; let debug_data_directory_offset = nt_header.OptionalHeader.DataDirectory - [IMAGE_DIRECTORY_ENTRY_DEBUG.0 as usize] + [IMAGE_DIRECTORY_ENTRY_DEBUG as usize] .VirtualAddress; let debug_data_directory_size = - nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG.0 as usize].Size; + nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG as usize].Size; let debug_directory = (base + debug_data_directory_offset as u64 ..base + debug_data_directory_offset as u64 + debug_data_directory_size as u64) .step_by(std::mem::size_of::()) @@ -223,10 +223,10 @@ impl<'a> DebugInfo<'a> { base + dos_header.e_lfanew as u64, )?; let debug_data_directory_offset = nt_header.OptionalHeader.DataDirectory - [IMAGE_DIRECTORY_ENTRY_DEBUG.0 as usize] + [IMAGE_DIRECTORY_ENTRY_DEBUG as usize] .VirtualAddress; let debug_data_directory_size = - nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG.0 as usize].Size; + nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG as usize].Size; let debug_directory = (base + debug_data_directory_offset as u64 ..base + debug_data_directory_offset as u64 + debug_data_directory_size as u64) .step_by(std::mem::size_of::()) diff --git a/src/os/windows/kernel.rs b/src/os/windows/kernel.rs index e13c2d58..6598a254 100644 --- a/src/os/windows/kernel.rs +++ b/src/os/windows/kernel.rs @@ -7,7 +7,7 @@ use anyhow::{anyhow, bail, Result}; use pdb::{FallibleIterator, SymbolData}; use simics::{debug, get_attribute, get_object, info, ConfObject}; use vergilius::bindings::*; -use windows::Win32::System::{ +use windows_sys::Win32::System::{ Diagnostics::Debug::{IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_NT_HEADERS64}, Kernel::LIST_ENTRY, SystemServices::{ @@ -67,11 +67,11 @@ pub fn page_is_kernel(processor: *mut ConfObject, address: u64) -> Result OPTIONAL_HEADER_SIGNATURE_PE32, OPTIONAL_HEADER_SIGNATURE_PE32_PLUS, ] - .contains(&nt_header.OptionalHeader.Magic.0) + .contains(&nt_header.OptionalHeader.Magic) { debug!( "Optional header magic {:#x} unrecognized", - nt_header.OptionalHeader.Magic.0 + nt_header.OptionalHeader.Magic ); return Ok(false); } @@ -81,10 +81,10 @@ pub fn page_is_kernel(processor: *mut ConfObject, address: u64) -> Result debug!(get_object("tsffs")?, "Image size is {:#x}", image_size); let export_header_offset = nt_header.OptionalHeader.DataDirectory - [IMAGE_DIRECTORY_ENTRY_EXPORT.0 as usize] + [IMAGE_DIRECTORY_ENTRY_EXPORT as usize] .VirtualAddress as u64; let export_header_size = - nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT.0 as usize].Size as u64; + nt_header.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT as usize].Size as u64; if export_header_offset == 0 || export_header_offset >= image_size { debug!( diff --git a/src/os/windows/structs.rs b/src/os/windows/structs.rs index ce613a46..31dfcaa1 100644 --- a/src/os/windows/structs.rs +++ b/src/os/windows/structs.rs @@ -10,7 +10,7 @@ use simics::{ debug, get_attribute, get_interface, get_object, info, ConfObject, IntRegisterInterface, }; use vergilius::bindings::*; -use windows::Win32::{Foundation::UNICODE_STRING, System::Kernel::LIST_ENTRY}; +use windows_sys::Win32::{Foundation::UNICODE_STRING, System::Kernel::LIST_ENTRY}; use crate::os::{ windows::{debug_info::DebugInfo, util::read_virtual}, @@ -1453,7 +1453,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_10240_16384_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_10586_0 { @@ -1461,7 +1461,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_10586_0_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_14393_0 { @@ -1469,7 +1469,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_14393_0_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_15063_0 { @@ -1477,7 +1477,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_15063_0_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_16299_15 { @@ -1485,7 +1485,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_16299_15_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_17134_1 { @@ -1493,7 +1493,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_17134_1_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_17763_107 { @@ -1501,7 +1501,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_17763_107_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_18362_418 { @@ -1509,7 +1509,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_18362_418_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_19041_1288 { @@ -1517,7 +1517,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_19041_1288_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_19045_2965 { @@ -1525,7 +1525,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_19045_2965_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_22000_194 { @@ -1533,7 +1533,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_22000_194_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_22621_382 { @@ -1541,7 +1541,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_22621_382_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, WindowsLdrDataTableEntry::Windows10_0_22631_2428 { @@ -1549,7 +1549,7 @@ impl WindowsLdrDataTableEntry { } => unsafe { std::mem::transmute::< vergilius::windows_10_0_22631_2428_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data_table_entry.InLoadOrderLinks) }, } @@ -1842,79 +1842,79 @@ impl WindowsPebLdrData { WindowsPebLdrData::Windows10_0_10240_16384 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_10240_16384_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_10586_0 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_10586_0_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_14393_0 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_14393_0_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_15063_0 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_15063_0_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_16299_15 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_16299_15_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_17134_1 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_17134_1_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_17763_107 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_17763_107_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_18362_418 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_18362_418_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_19041_1288 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_19041_1288_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_19045_2965 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_19045_2965_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_22000_194 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_22000_194_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_22621_382 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_22621_382_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, WindowsPebLdrData::Windows10_0_22631_2428 { ldr_data } => unsafe { std::mem::transmute::< vergilius::windows_10_0_22631_2428_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(ldr_data.InLoadOrderModuleList) }, } @@ -2538,79 +2538,79 @@ impl WindowsEProcess { WindowsEProcess::Windows10_0_10240_16384 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_10240_16384_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_10586_0 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_10586_0_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_14393_0 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_14393_0_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_15063_0 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_15063_0_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_16299_15 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_16299_15_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_17134_1 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_17134_1_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_17763_107 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_17763_107_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_18362_418 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_18362_418_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_19041_1288 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_19041_1288_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_19045_2965 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_19045_2965_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_22000_194 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_22000_194_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_22621_382 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_22621_382_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, WindowsEProcess::Windows10_0_22631_2428 { eprocess } => unsafe { std::mem::transmute::< vergilius::windows_10_0_22631_2428_x64::_LIST_ENTRY, - windows::Win32::System::Kernel::LIST_ENTRY, + windows_sys::Win32::System::Kernel::LIST_ENTRY, >(eprocess.ActiveProcessLinks) }, } @@ -2690,7 +2690,7 @@ impl WindowsEProcess { read_unicode_string( processor, object_name_information.Length as usize, - object_name_information.Buffer.0, + object_name_information.Buffer, ) } From 81347d2b328ea994ee947c77f5588b6f4e61970a Mon Sep 17 00:00:00 2001 From: novafacing Date: Fri, 9 Aug 2024 14:37:35 -0700 Subject: [PATCH 13/14] Move from lcov to dependency implementation --- Cargo.toml | 3 +- src/lib.rs | 3 +- src/os/mod.rs | 2 +- src/source_cov/html.rs | 428 ---------------------- src/source_cov/lcov.rs | 794 ----------------------------------------- src/source_cov/mod.rs | 3 - 6 files changed, 4 insertions(+), 1229 deletions(-) delete mode 100644 src/source_cov/html.rs delete mode 100644 src/source_cov/lcov.rs diff --git a/Cargo.toml b/Cargo.toml index 559c798f..a128a7c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -103,9 +103,8 @@ md5 = "0.7.0" sha1 = "0.10.6" sha2 = "0.10.8" typed-path = "0.9.0" -markup = "0.15.0" -petgraph = "0.6.5" thiserror = "1.0.63" +lcov2 = "0.1.0" [dev-dependencies] simics-test = "0.1.0" diff --git a/src/lib.rs b/src/lib.rs index 6b676d38..1b27e0a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ use anyhow::{anyhow, Result}; use arch::{Architecture, ArchitectureHint, ArchitectureOperations}; use fuzzer::{messages::FuzzerMessage, ShutdownMessage, Testcase}; use indoc::indoc; +use lcov2::Records; use libafl::{inputs::HasBytesVec, prelude::ExitKind}; use libafl_bolts::prelude::OwnedMutSlice; use libafl_targets::AFLppCmpLogMap; @@ -63,7 +64,7 @@ use simics::{ // which is necessary because this module is compatible with base versions which cross the // deprecation boundary use simics::{restore_snapshot, save_snapshot}; -use source_cov::{lcov::Records, SourceCache}; +use source_cov::SourceCache; use state::StopReason; use std::{ alloc::{alloc_zeroed, Layout}, diff --git a/src/os/mod.rs b/src/os/mod.rs index e4ba013b..577203d5 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, path::PathBuf}; -use crate::source_cov::lcov::Records; +use lcov2::Records; pub mod windows; diff --git a/src/source_cov/html.rs b/src/source_cov/html.rs deleted file mode 100644 index 7be5172e..00000000 --- a/src/source_cov/html.rs +++ /dev/null @@ -1,428 +0,0 @@ -use std::path::PathBuf; - -use markup::{define, Render}; - -#[derive(Debug, Clone)] -pub(crate) struct HtmlLineInfo { - pub(crate) hit_count: Option, - pub(crate) leading_spaces: usize, - pub(crate) line: String, -} - -#[derive(Debug, Clone)] -pub(crate) struct HtmlFunctionInfo { - pub(crate) hit_count: Option, - pub(crate) name: String, -} - -#[derive(Debug, Clone)] -pub(crate) struct HtmlSummaryInfo { - pub(crate) is_dir: bool, - // e.g. `../../../../../index.html` - pub(crate) top_level: PathBuf, - // e.g. `index.html` for files, `../index.html` for directories - pub(crate) parent: Option, - // e.g. `test.c` for files, `dir-name` for directories - pub(crate) filename: Option, - pub(crate) total_lines: usize, - pub(crate) hit_lines: usize, - pub(crate) total_functions: usize, - pub(crate) hit_functions: usize, -} - -impl std::fmt::Display for HtmlSummaryInfo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "HtmlSummaryInfo {{ is_dir: {}, top_level: {:?}, parent: {:?}, filename: {:?}, total_lines: {}, hit_lines: {}, total_functions: {}, hit_functions: {} }}", - self.is_dir, self.top_level, self.parent, self.filename, self.total_lines, self.hit_lines, self.total_functions, self.hit_functions - ) - } -} - -define! { - Head { - } - Style { - - } - CurrentView(summary: HtmlSummaryInfo) { - table { - tr { - th { - "Current view:" - } - td { - a[href = summary.top_level.to_string_lossy().to_string()] { - "Top Level" - } - @if let Some(parent) = &summary.parent { - @if let Some(parent_parent) = parent.parent() { - @markup::raw(" ") - "-" - @markup::raw(" ") - a[href = &parent.to_string_lossy().to_string()] { - @parent_parent.file_name().map(|s| s.to_string_lossy().to_string()).unwrap_or("".to_string()) - } - } - } - @markup::raw(" ") - "-" - @markup::raw(" ") - @summary.filename - } - } - tr { - th { - "Generated On" - } - td { - @chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string() - } - } - } - - } - - Summary( - summary: HtmlSummaryInfo, - ) { - table { - tr { - th {} - th[style = "text-align: right;"] { - "Coverage" - } - th[style = "text-align: right;"] { - "Total" - } - th[style = "text-align: right;"] { - "Hit" - } - } - tr { - th[style = "text-align: right;"] { - "Lines" - } - @let ratio = if summary.total_lines == 0 { - 0.0 - } else { - summary.hit_lines as f32 / summary.total_lines as f32 * 100.0 - }; - @if ratio >= 90.0 { - td[style = "text-align: right; background-color: #a7fc9d;"] { - @format!("{ratio:.02}%") - } - } else if ratio >= 30.0 { - td[style = "text-align: right; background-color: #ffea20;"] { - @format!("{ratio:.02}%") - } - } else { - td[style = "text-align: right; background-color: #ff6230;"] { - @format!("{ratio:.02}%") - } - } - - td[style = "text-align: right; background-color: #cad7fe;"] { - @summary.total_lines - } - td[style = "text-align: right; background-color: #cad7fe;"] { - @summary.hit_lines - } - } - tr { - th[style = "text-align: right;"] { - "Functions" - } - @let ratio = if summary.total_functions == 0 { - 0.0 - } else { - summary.hit_functions as f32 / summary.total_functions as f32 * 100.0 - }; - @if ratio >= 90.0 { - td[style = "text-align: right; background-color: #a7fc9d;"] { - @format!("{ratio:.02}%") - } - } else if ratio >= 30.0 { - td[style = "text-align: right; background-color: #ffea20;"] { - @format!("{ratio:.02}%") - } - - } else { - td[style = "text-align: right; background-color: #ff6230;"] { - @format!("{ratio:.02}%") - } - } - td[style = "text-align: right; background-color: #cad7fe;"] { - @summary.total_functions - } - td[style = "text-align: right; background-color: #cad7fe;"] { - @summary.hit_functions - } - } - } - } - - Listing( - lines: Vec, - ) { - table[ - cellpadding = 0, - cellspacing = 0, - border = 0, - style = "font-family: monospace, monospace; border-collapse: separate; border-spacing: 1em 0;" - ] { - tbody { - tr { - th[style = "text-align: right;"] { - "Line" - } - th[style = "text-align: right;"] { - "Hits" - } - th[style = "text-align: left;"] { - "Source Code" - } - } - @for (i, line_info) in lines.iter().enumerate() { - tr { - td[style = "text-align: right;"] { - @{i + 1} - } - td[style = "text-align: right;"] { - @if let Some(hit_count) = line_info.hit_count { - @hit_count - } else { - "-" - } - } - @if let Some(hit_count) = line_info.hit_count { - @if hit_count == 0 { - td[style = "text-align: left; background-color: #ff6230;"] { - @for _ in 0..line_info.leading_spaces { - @markup::raw(" ") - } - @line_info.line - } - } else { - td[style = "text-align: left; background-color: #cad7fe;"] { - @for _ in 0..line_info.leading_spaces { - @markup::raw(" ") - } - @line_info.line - } - } - } else { - td[style = "text-align: left;"] { - @for _ in 0..line_info.leading_spaces { - @markup::raw(" ") - } - @line_info.line - } - } - } - } - } - } - } - - FunctionListing( - functions: Vec, - ) { - table[ - cellpadding = 0, - cellspacing = 0, - border = 0, - style = "font-family: monospace, monospace; border-collapse: separate; border-spacing: 1em 0;" - ] { - tbody { - tr { - th[style = "text-align: right;"] { - "Function" - } - th[style = "text-align: right;"] { - "Hits" - } - } - @for function_info in functions.iter() { - tr { - @if let Some(hit_count) = function_info.hit_count { - @if hit_count == 0 { - td[style = "text-align: left; background-color: #ff6230;"] { - @function_info.name - } - } else { - td[style = "text-align: left; background-color: #cad7fe;"] { - @function_info.name - } - } - } else { - td[style = "text-align: left;"] { - @function_info.name - } - } - td[style = "text-align: right;"] { - @if let Some(hit_count) = function_info.hit_count { - @hit_count - } else { - "-" - } - } - } - } - } - } - } - - FilePage(listing: L, function_listing: FL) where L: Render, FL: Render { - tr[style = "border-bottom: 1px solid black;"] { - td { - @listing - } - } - tr[style = "border-bottom: 1px solid black;"] { - td { - @function_listing - } - } - } - - DirectoryPage(summaries: Vec) { - tr[style = "border-bottom: 1px solid black;"] { - table[width = "100%", style = "border-collapse: collapse;"] { - tr[style = "border-bottom: 1px solid black;"] { - th { - "File/Directory" - } - th { - "Line Coverage" - } - th { - "Total Lines" - } - th { - "Hit Lines" - } - th { - "Function Coverage" - } - th { - "Total Functions" - } - th { - "Hit Functions" - } - } - @for summary in summaries { - tr[style = "border-bottom: 1px solid black;"] { - td { - @if summary.is_dir { - @if let Some(filename) = &summary.filename { - a[href = PathBuf::from(filename).join("index.html").to_string_lossy().to_string()] { - @summary.filename - } - } else { - "" - } - - } else if let Some(filename) = &summary.filename { - a[href = format!("{}.html", filename)] { - @summary.filename - } - } else { - "" - } - } - @let line_ratio = if summary.total_lines == 0 { - 0.0 - } else { - summary.hit_lines as f32 / summary.total_lines as f32 * 100.0 - }; - - @let function_ratio = if summary.total_functions == 0 { - 0.0 - } else { - summary.hit_functions as f32 / summary.total_functions as f32 * 100.0 - }; - - @if line_ratio >= 90.0 { - td[style = "text-align: right; background-color: #a7fc9d;"] { - @format!("{line_ratio:.02}%") - } - } else if line_ratio >= 30.0 { - td[style = "text-align: right; background-color: #ffea20;"] { - @format!("{line_ratio:.02}%") - } - } else { - td[style = "text-align: right; background-color: #ff6230;"] { - @format!("{line_ratio:.02}%") - } - } - - td[style = "text-align: right; background-color: #cad7fe;"] { - @summary.total_lines - } - - td[style = "text-align: right; background-color: #cad7fe;"] { - @summary.hit_lines - } - - @if function_ratio >= 90.0 { - td[style = "text-align: right; background-color: #a7fc9d;"] { - @format!("{function_ratio:.02}%") - } - } else if function_ratio >= 30.0 { - td[style = "text-align: right; background-color: #ffea20;"] { - @format!("{function_ratio:.02}%") - } - } else { - td[style = "text-align: right; background-color: #ff6230;"] { - @format!("{function_ratio:.02}%") - } - } - - td[style = "text-align: right; background-color: #cad7fe;"] { - @summary.total_functions - } - - td[style = "text-align: right; background-color: #cad7fe;"] { - @summary.hit_functions - } - } - } - } - } - } - - - Page(head: H, current_view: CV, summary: S, main: M) where H: Render, CV: Render, S: Render, M: Render { - @markup::doctype() - html { - head { - @head - @Style {} - meta[charse = "utf-8"]; - title { "TSFFS Coverage Report" } - } - body { - table[width = "100%", style = "border-collapse: collapse;"] { - tr[style = "text-align: center; border-bottom: 1px solid black;"] { - td { - "TSFFS Code Coverage Report" - } - } - tr[style = "border-bottom: 1px solid black;"] { - td { - @current_view - } - td { - @summary - } - } - @main - } - } - } - } - -} diff --git a/src/source_cov/lcov.rs b/src/source_cov/lcov.rs deleted file mode 100644 index 7e9f4126..00000000 --- a/src/source_cov/lcov.rs +++ /dev/null @@ -1,794 +0,0 @@ -use anyhow::{anyhow, Result}; -use petgraph::{ - graph::{DiGraph, NodeIndex}, - visit::DfsPostOrder, - Direction, -}; -use std::{ - collections::{btree_map::Entry, BTreeMap, HashMap}, - fs::{create_dir_all, read, write}, - iter::repeat, - path::{Component, Path, PathBuf}, -}; - -use super::html::{ - CurrentView, DirectoryPage, FilePage, FunctionListing, Head, HtmlFunctionInfo, HtmlLineInfo, - HtmlSummaryInfo, Listing, Page, Summary, -}; - -#[derive(Debug, Clone, Default)] -pub struct TestNameRecordEntry(String); - -impl std::fmt::Display for TestNameRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "TN:{}", self.0) - } -} - -#[derive(Debug, Clone)] -pub struct SourceFileRecordEntry(PathBuf); - -impl std::fmt::Display for SourceFileRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "SF:{}", self.0.to_string_lossy()) - } -} - -impl Default for SourceFileRecordEntry { - fn default() -> Self { - Self(PathBuf::new()) - } -} - -#[derive(Debug, Clone)] -pub struct VersionRecordEntry(usize); - -impl Default for VersionRecordEntry { - fn default() -> Self { - Self(1) - } -} - -impl std::fmt::Display for VersionRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "VER:{}", self.0) - } -} - -#[derive(Debug, Clone)] -pub struct FunctionRecordEntry { - start_line: usize, - end_line: Option, - name: String, -} - -impl std::fmt::Display for FunctionRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.end_line { - Some(end_line) => write!(f, "FN:{},{},{}", self.start_line, end_line, self.name), - None => write!(f, "FN:{},{}", self.start_line, self.name), - } - } -} - -impl std::cmp::PartialEq for FunctionRecordEntry { - fn eq(&self, other: &Self) -> bool { - self.start_line == other.start_line && self.name == other.name - } -} - -impl std::cmp::PartialOrd for FunctionRecordEntry { - fn partial_cmp(&self, other: &Self) -> Option { - // Entries are ordered by start line - self.start_line.partial_cmp(&other.start_line) - } -} - -#[derive(Debug, Clone)] -pub struct FunctionDataRecordEntry { - hits: usize, - name: String, -} - -impl std::fmt::Display for FunctionDataRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "FNDA:{},{}", self.hits, self.name) - } -} - -impl std::cmp::PartialEq for FunctionDataRecordEntry { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - } -} - -#[derive(Debug, Clone, Default)] -pub struct FunctionsFoundRecordEntry(usize); - -impl std::fmt::Display for FunctionsFoundRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "FNF:{}", self.0) - } -} - -#[derive(Debug, Clone, Default)] -pub struct FunctionsHitRecordEntry(usize); - -impl std::fmt::Display for FunctionsHitRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "FNH:{}", self.0) - } -} - -// NOTE: We don't bother to implement branch data since we have the line data anyway - -// BRDA, BRF, BRH empty - -#[derive(Debug, Clone)] -pub struct LineRecordEntry { - line_number: usize, - hit_count: usize, - /// MD5 hash of line saved as base64, typically not used - checksum: Option, -} - -impl std::fmt::Display for LineRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.checksum { - Some(checksum) => { - write!(f, "DA:{},{},{}", self.line_number, self.hit_count, checksum) - } - None => write!(f, "DA:{},{}", self.line_number, self.hit_count), - } - } -} - -#[derive(Debug, Clone, Default)] -pub struct LinesFoundRecordEntry(usize); - -impl std::fmt::Display for LinesFoundRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "LF:{}", self.0) - } -} - -#[derive(Debug, Clone, Default)] -pub struct LinesHitRecordEntry(usize); - -impl std::fmt::Display for LinesHitRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "LH:{}", self.0) - } -} - -#[derive(Debug, Clone, Default)] -pub struct EndOfRecordEntry; - -impl std::fmt::Display for EndOfRecordEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "end_of_record") - } -} - -#[derive(Debug, Clone, Default)] -pub struct Record { - test_name: TestNameRecordEntry, - source_file: SourceFileRecordEntry, - version: VersionRecordEntry, - // Functions ordered by start line - functions: BTreeMap, - // Function datas are unique - function_data: HashMap, - functions_found: FunctionsFoundRecordEntry, - functions_hit: FunctionsHitRecordEntry, - // Lines are ordered by line and unique - lines: BTreeMap, - lines_found: LinesFoundRecordEntry, - lines_hit: LinesHitRecordEntry, - end_of_record: EndOfRecordEntry, -} - -impl Record { - pub fn new

(path: P) -> Self - where - P: AsRef, - { - Self { - source_file: SourceFileRecordEntry(path.as_ref().to_path_buf()), - functions: BTreeMap::new(), - function_data: HashMap::new(), - lines: BTreeMap::new(), - ..Default::default() - } - } - - pub fn add_function_if_not_exists( - &mut self, - start_line: usize, - end_line: Option, - name: S, - ) -> bool - where - S: AsRef, - { - match self.functions.entry(start_line) { - Entry::Occupied(_) => false, - Entry::Vacant(entry) => { - entry.insert(FunctionRecordEntry { - start_line, - end_line, - name: name.as_ref().to_string(), - }); - self.functions_found.0 += 1; - self.function_data - .entry(name.as_ref().to_string()) - .or_insert_with(|| FunctionDataRecordEntry { - hits: 0, - name: name.as_ref().to_string(), - }); - true - } - } - } - - pub fn increment_function_data(&mut self, name: S) - where - S: AsRef, - { - let entry = self - .function_data - .entry(name.as_ref().to_string()) - .or_insert_with(|| { - self.functions_found.0 += 1; - FunctionDataRecordEntry { - hits: 0, - name: name.as_ref().to_string(), - } - }); - - if entry.hits == 0 { - self.functions_hit.0 += 1; - } - - entry.hits += 1; - } - - pub fn add_line_if_not_exists(&mut self, line_number: usize) -> bool { - match self.lines.entry(line_number) { - Entry::Occupied(_) => false, - Entry::Vacant(entry) => { - entry.insert(LineRecordEntry { - line_number, - hit_count: 0, - checksum: None, - }); - self.lines_found.0 += 1; - true - } - } - } - - pub fn increment_line(&mut self, line_number: usize) { - let entry = self.lines.entry(line_number).or_insert_with(|| { - self.lines_found.0 += 1; - LineRecordEntry { - line_number, - hit_count: 0, - checksum: None, - } - }); - - if entry.hit_count == 0 { - self.lines_hit.0 += 1; - } - - entry.hit_count += 1; - } -} - -impl std::fmt::Display for Record { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "{}", self.test_name)?; - writeln!(f, "{}", self.source_file)?; - writeln!(f, "{}", self.version)?; - for function in self.functions.values() { - writeln!(f, "{}", function)?; - } - for function_data in self.function_data.values() { - writeln!(f, "{}", function_data)?; - } - writeln!(f, "{}", self.functions_found)?; - writeln!(f, "{}", self.functions_hit)?; - for line in self.lines.values() { - writeln!(f, "{}", line)?; - } - writeln!(f, "{}", self.lines_found)?; - writeln!(f, "{}", self.lines_hit)?; - writeln!(f, "{}", self.end_of_record)?; - Ok(()) - } -} - -impl Record { - pub fn source_filename(&self) -> String { - self.source_file - .0 - .file_name() - .map(|fname| fname.to_string_lossy().to_string()) - .unwrap_or("".to_string()) - } - - pub fn summary(&self, top_level: PathBuf, parent: Option) -> HtmlSummaryInfo { - HtmlSummaryInfo { - is_dir: false, - top_level, - parent, - filename: Some(self.source_filename()), - total_lines: self.lines_found.0, - hit_lines: self.lines_hit.0, - total_functions: self.functions_found.0, - hit_functions: self.functions_hit.0, - } - } - - pub fn lines(&self) -> Result> { - let contents_raw = read(self.source_file.0.as_path())?; - let contents = String::from_utf8_lossy(&contents_raw); - let lines = contents - .lines() - .enumerate() - .map(|(i, line)| { - let hit_count = self.lines.get(&(i + 1)).map(|l| l.hit_count); - let leading_spaces = line.chars().take_while(|c| c.is_whitespace()).count(); - let trimmed = line.trim().to_string(); - HtmlLineInfo { - hit_count, - leading_spaces, - line: trimmed, - } - }) - .collect::>(); - Ok(lines) - } - - pub fn functions(&self) -> Vec { - let mut functions = self - .functions - .values() - .map(|f| HtmlFunctionInfo { - hit_count: self.function_data.get(&f.name).map(|d| d.hits), - name: f.name.as_str().to_string(), - }) - .collect::>(); - functions.sort_by(|a, b| a.name.cmp(&b.name)); - functions - } -} - -#[derive(Debug, Clone, Default)] -pub struct Records(HashMap); - -impl Records { - pub fn get_or_insert_mut

(&mut self, path: P) -> &mut Record - where - P: AsRef, - { - self.0 - .entry(path.as_ref().to_path_buf()) - .or_insert_with(|| Record::new(path)) - } - - pub fn get(&self, path: &Path) -> Option<&Record> { - self.0.get(path) - } -} - -impl std::fmt::Display for Records { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for record in self.0.values() { - write!(f, "{}", record)?; - } - Ok(()) - } -} - -struct GraphNode { - pub path: PathBuf, - pub summary: Option, -} - -impl GraphNode { - pub fn new(path: PathBuf, summary: Option) -> Self { - Self { path, summary } - } -} - -impl std::fmt::Display for GraphNode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.path.to_string_lossy()) - } -} - -struct GraphEdge {} - -impl std::fmt::Display for GraphEdge { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "") - } -} - -impl Records { - /// Output LCOV records to HTML format, like a mini genhtml - pub fn to_html

(&self, output_directory: P) -> Result<()> - where - P: AsRef, - { - // Build a tree out of the output paths - let mut graph = DiGraph::::new(); - let mut node_ids = HashMap::::new(); - - let entries = self - .0 - .values() - .map(|record| { - let absolute_source_path = record.source_file.0.canonicalize()?; - let mut output_path = output_directory - .as_ref() - .components() - .chain( - absolute_source_path - .components() - .filter(|c| matches!(c, Component::Normal(_))), - ) - .collect::(); - output_path.set_file_name( - output_path - .file_name() - .map(|fname| fname.to_string_lossy().to_string()) - .unwrap_or_default() - + ".html", - ); - if let std::collections::hash_map::Entry::Vacant(entry) = - node_ids.entry(output_path.clone()) - { - entry.insert(graph.add_node(GraphNode::new(output_path.clone(), None))); - } - - let mut path = output_path.as_path(); - while let Some(parent) = path.parent() { - if let std::collections::hash_map::Entry::Vacant(entry) = - node_ids.entry(parent.to_path_buf()) - { - entry.insert(graph.add_node(GraphNode::new(parent.to_path_buf(), None))); - } - - if graph - .find_edge( - *node_ids - .get(parent) - .ok_or_else(|| anyhow!("parent not found"))?, - *node_ids - .get(path) - .ok_or_else(|| anyhow!("output path not found"))?, - ) - .is_none() - { - graph.add_edge( - *node_ids - .get(parent) - .ok_or_else(|| anyhow!("parent not found"))?, - *node_ids - .get(path) - .ok_or_else(|| anyhow!("output path not found"))?, - GraphEdge {}, - ); - } - - path = parent; - - if !path.is_dir() { - create_dir_all(path)?; - } - - if path == output_directory.as_ref() { - break; - } - } - - Ok((output_path, record)) - }) - .collect::>>()? - .into_iter() - .collect::>(); - - let root = node_ids - .get(output_directory.as_ref()) - .ok_or_else(|| anyhow!("root not found"))?; - - let mut traversal = DfsPostOrder::new(&graph, *root); - - while let Some(node) = traversal.next(&graph) { - let path = graph - .node_weight(node) - .ok_or_else(|| anyhow!("No weight for node"))? - .path - .clone(); - // Calculate the depth of this path from the output directory - if let Some(record) = entries.get(path.as_path()) { - let depth = path - .components() - .count() - .saturating_sub(output_directory.as_ref().components().count()) - .saturating_sub(1); - // This is a file node - let summary = record.summary( - repeat("..") - .take(depth) - .collect::() - .join("index.html"), - path.parent().map(|p| p.join("index.html")), - ); - graph - .node_weight_mut(node) - .ok_or_else(|| anyhow!("No weight for node"))? - .summary = Some(summary.clone()); - let lines = record.lines()?; - let functions = record.functions(); - let page = Page { - head: Head {}, - current_view: CurrentView { - summary: summary.clone(), - }, - summary: Summary { summary }, - main: FilePage { - listing: Listing { lines }, - function_listing: FunctionListing { functions }, - }, - }; - write(&path, page.to_string())?; - } else { - let depth = path - .components() - .count() - .saturating_sub(output_directory.as_ref().components().count()); - let (top_level, parent) = if path == output_directory.as_ref() { - // This is the root node - (PathBuf::from("index.html"), None) - } else { - // This is a directory node - ( - repeat("..") - .take(depth) - .collect::() - .join("index.html"), - path.parent().map(|p| p.join("index.html")), - ) - }; - let (total_lines, hit_lines, total_functions, hit_functions) = graph - .neighbors_directed(node, Direction::Outgoing) - .try_fold( - (0, 0, 0, 0), - |(total_lines, hit_lines, total_functions, hit_functions), neighbor| { - let summary = graph - .node_weight(neighbor) - .ok_or_else(|| anyhow!("No weight for node"))? - .summary - .as_ref() - .ok_or_else(|| anyhow!("No summary for node"))?; - Ok::<(usize, usize, usize, usize), anyhow::Error>(( - total_lines + summary.total_lines, - hit_lines + summary.hit_lines, - total_functions + summary.total_functions, - hit_functions + summary.hit_functions, - )) - }, - )?; - - let summary = HtmlSummaryInfo { - is_dir: true, - top_level, - parent, - filename: path - .file_name() - .map(|fname| fname.to_string_lossy().to_string()), - total_lines, - hit_lines, - total_functions, - hit_functions, - }; - - let page = Page { - head: Head {}, - current_view: CurrentView { - summary: summary.clone(), - }, - summary: Summary { - summary: summary.clone(), - }, - main: DirectoryPage { - summaries: graph - .neighbors_directed(node, Direction::Outgoing) - .filter_map(|neighbor| { - graph - .node_weight(neighbor) - .ok_or_else(|| anyhow!("No weight for node")) - .ok() - .and_then(|weight| weight.summary.as_ref().cloned()) - }) - .collect(), - }, - }; - write(path.join("index.html"), page.to_string())?; - - graph - .node_weight_mut(node) - .ok_or_else(|| anyhow!("No weight for node"))? - .summary = Some(summary); - } - } - - // NOTE: Left for easy debugging of the directory graph - // let dot = petgraph::dot::Dot::new(&graph); - // write( - // output_directory.as_ref().join("graph.dot"), - // format!("{}", dot), - // )?; - - Ok(()) - } -} - -#[allow(clippy::unwrap_used)] -#[cfg(test)] -mod test { - use std::path::PathBuf; - - use super::Records; - - #[test] - fn test_records() { - let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let mut records = Records::default(); - let record_test = records.get_or_insert_mut( - manifest_dir - .join("tests") - .join("rsrc") - .join("test-lcov") - .join("test.c"), - ); - record_test.add_function_if_not_exists(4, Some(16), "main"); - record_test.increment_function_data("main"); - record_test.add_line_if_not_exists(4); - record_test.add_line_if_not_exists(5); - record_test.add_line_if_not_exists(7); - record_test.add_line_if_not_exists(9); - record_test.add_line_if_not_exists(11); - record_test.add_line_if_not_exists(12); - record_test.add_line_if_not_exists(14); - record_test.increment_line(4); - record_test.increment_line(5); - record_test.increment_line(7); - record_test.increment_line(9); - record_test.increment_line(11); - record_test.increment_line(14); - let record_test2 = records.get_or_insert_mut( - manifest_dir - .join("tests") - .join("rsrc") - .join("test-lcov") - .join("test2.c"), - ); - record_test2.add_function_if_not_exists(1, Some(3), "x"); - record_test2.increment_function_data("x"); - record_test2.add_line_if_not_exists(1); - record_test2.add_line_if_not_exists(2); - record_test2.add_line_if_not_exists(3); - record_test2.increment_line(1); - record_test2.increment_line(2); - record_test2.increment_line(3); - println!("{}", records); - } - - #[test] - fn test_records_to_html() { - let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let mut records = Records::default(); - - let record_test = records.get_or_insert_mut( - manifest_dir - .join("tests") - .join("rsrc") - .join("test-lcov") - .join("test.c"), - ); - record_test.add_function_if_not_exists(4, Some(16), "main"); - record_test.increment_function_data("main"); - record_test.add_line_if_not_exists(4); - record_test.add_line_if_not_exists(5); - record_test.add_line_if_not_exists(7); - record_test.add_line_if_not_exists(9); - record_test.add_line_if_not_exists(11); - record_test.add_line_if_not_exists(12); - record_test.add_line_if_not_exists(14); - record_test.increment_line(4); - record_test.increment_line(5); - record_test.increment_line(7); - record_test.increment_line(9); - record_test.increment_line(11); - record_test.increment_line(14); - - let record_test = records.get_or_insert_mut( - manifest_dir - .join("tests") - .join("rsrc") - .join("test-lcov") - .join("subdir1") - .join("test.c"), - ); - record_test.add_function_if_not_exists(4, Some(16), "main"); - record_test.increment_function_data("main"); - record_test.add_line_if_not_exists(4); - record_test.add_line_if_not_exists(5); - record_test.add_line_if_not_exists(7); - record_test.add_line_if_not_exists(9); - record_test.add_line_if_not_exists(11); - record_test.add_line_if_not_exists(12); - record_test.add_line_if_not_exists(14); - record_test.increment_line(4); - record_test.increment_line(5); - record_test.increment_line(7); - record_test.increment_line(9); - record_test.increment_line(11); - record_test.increment_line(14); - - let record_test = records.get_or_insert_mut( - manifest_dir - .join("tests") - .join("rsrc") - .join("test-lcov") - .join("subdir2") - .join("test-subdir2.c"), - ); - record_test.add_function_if_not_exists(4, Some(16), "main"); - record_test.increment_function_data("main"); - record_test.add_line_if_not_exists(4); - record_test.add_line_if_not_exists(5); - record_test.add_line_if_not_exists(7); - record_test.add_line_if_not_exists(9); - record_test.add_line_if_not_exists(11); - record_test.add_line_if_not_exists(12); - record_test.add_line_if_not_exists(14); - record_test.increment_line(4); - record_test.increment_line(5); - record_test.increment_line(7); - record_test.increment_line(9); - record_test.increment_line(11); - record_test.increment_line(14); - - let record_test2 = records.get_or_insert_mut( - manifest_dir - .join("tests") - .join("rsrc") - .join("test-lcov") - .join("test2.c"), - ); - record_test2.add_function_if_not_exists(1, Some(3), "x"); - record_test2.increment_function_data("x"); - record_test2.add_line_if_not_exists(1); - record_test2.add_line_if_not_exists(2); - record_test2.add_line_if_not_exists(3); - record_test2.increment_line(1); - record_test2.increment_line(2); - record_test2.increment_line(3); - - records - .to_html( - manifest_dir - .join("tests") - .join("rsrc") - .join("test-lcov") - .join("html"), - ) - .unwrap(); - } -} diff --git a/src/source_cov/mod.rs b/src/source_cov/mod.rs index 1ee4d58d..6bdcb87a 100644 --- a/src/source_cov/mod.rs +++ b/src/source_cov/mod.rs @@ -13,9 +13,6 @@ use simics::{debug, get_object}; use typed_path::{TypedComponent, TypedPath, UnixComponent, WindowsComponent}; use walkdir::WalkDir; -pub(crate) mod html; -pub(crate) mod lcov; - #[derive(Debug, Clone, Default)] pub struct SourceCache { prefix_lookup: HashMap, PathBuf>, From b2cc04c958b75f928805f669e6305a36bd5c7ab8 Mon Sep 17 00:00:00 2001 From: novafacing Date: Fri, 9 Aug 2024 15:23:39 -0700 Subject: [PATCH 14/14] Simplify windows-sys imports --- Cargo.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a128a7c2..5f70a094 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,14 +78,12 @@ yaxpeax-arm = "0.3.0" chrono = "0.4.38" vergilius = "0.1.2" windows-sys = { features = [ - "Win32", - "Win32_System", + "Win32_Foundation", "Win32_System_SystemServices", "Win32_System_Diagnostics_Debug", "Win32_System_Diagnostics", "Win32_System_SystemInformation", "Win32_System_Kernel", - "Win32_Foundation", ], version = "0.59.0" } reqwest = { version = "0.12.5", features = [ "blocking",