fromsoftware_shared/
rtti.rs1use std::ptr::NonNull;
2
3use pelite::pe64::{
4 Pe, Rva, Va,
5 msvc::{RTTIBaseClassDescriptor, RTTIClassHierarchyDescriptor, RTTICompleteObjectLocator},
6};
7use undname::Flags;
8
9use crate::program::Program;
10
11const VA_SIZE: u32 = size_of::<Va>() as u32;
13
14pub fn find_rtti_classes<'a, T: Pe<'a>>(program: &'a T) -> impl Iterator<Item = Class<'a, T>> + 'a {
17 let text = program
18 .section_headers()
19 .by_name(".text")
20 .expect("no .text section found");
21
22 let rdata = program
23 .section_headers()
24 .by_name(".rdata")
25 .expect("no .rdata section found");
26
27 rdata
28 .virtual_range()
29 .step_by(size_of::<Va>())
30 .filter_map(move |candidate_rva| {
31 let vftable_meta_rva = candidate_rva;
32 let vftable_rva = candidate_rva + VA_SIZE;
33
34 let vftable_meta_rva = program
35 .derva(vftable_meta_rva)
36 .and_then(|va| program.va_to_rva(*va))
37 .ok()?;
38
39 let vftable_entry_rva = program
40 .derva(vftable_rva)
41 .and_then(|va| program.va_to_rva(*va))
42 .ok()?;
43
44 if rdata.virtual_range().contains(&vftable_meta_rva)
45 && text.virtual_range().contains(&vftable_entry_rva)
46 {
47 let _: &RTTICompleteObjectLocator = program.derva(vftable_meta_rva).ok()?;
48
49 Some((vftable_meta_rva, vftable_rva))
50 } else {
51 None
52 }
53 })
54 .filter_map(|(meta, vftable)| {
55 let col: &RTTICompleteObjectLocator = program.derva(meta).ok()?;
56
57 let ty_name = program
58 .derva_c_str(col.type_descriptor + 16)
59 .ok()?
60 .to_string();
61 if !ty_name
62 .chars()
63 .all(|ch| (0x20..=0x7e).contains(&(ch as u8)))
64 {
65 return None;
66 }
67
68 let demangled = undname::demangle(ty_name.as_str(), Flags::NAME_ONLY)
69 .map(|s| s.to_string())
70 .ok()?;
71
72 Some(Class {
73 program,
74 name: demangled,
75 vftable,
76 })
77 })
78}
79
80pub fn vftable_classname(program: &Program, vftable_va: usize) -> Option<String> {
83 let vftable_rva = program.va_to_rva(vftable_va as u64).ok()?;
84 let vftable_meta_rva = vftable_rva - VA_SIZE;
85
86 let rdata = program
87 .section_headers()
88 .by_name(".rdata")
89 .expect("no .rdata section found");
90
91 let vftable_meta_rva = program
92 .derva(vftable_meta_rva)
93 .and_then(|va| program.va_to_rva(*va))
94 .ok()?;
95
96 if !rdata.virtual_range().contains(&vftable_meta_rva) {
97 return None;
98 }
99
100 let col: &RTTICompleteObjectLocator = program.derva(vftable_meta_rva).ok()?;
101 let ty_name = program
102 .derva_c_str(col.type_descriptor + 16)
103 .ok()?
104 .to_string();
105 if !ty_name
106 .chars()
107 .all(|ch| (0x20..=0x7e).contains(&(ch as u8)))
108 {
109 return None;
110 }
111
112 let demangled = undname::demangle(ty_name.as_str(), Flags::NAME_ONLY)
113 .map(|s| s.to_string())
114 .ok()?;
115
116 Some(demangled)
117}
118
119pub(crate) fn is_base_class(
121 program: &Program,
122 base_class_col: &RTTICompleteObjectLocator,
123 class_col: &RTTICompleteObjectLocator,
124) -> pelite::Result<bool> {
125 let class_hierarchy_descriptor: &RTTIClassHierarchyDescriptor =
126 program.derva(class_col.class_descriptor)?;
127
128 let base_class_array: &[Rva] = program.derva_slice(
129 class_hierarchy_descriptor.base_class_array,
130 class_hierarchy_descriptor.num_base_classes as usize,
131 )?;
132
133 for base_class_rva in base_class_array {
134 let base_class_descriptor: &RTTIBaseClassDescriptor = program.derva(*base_class_rva)?;
135 if base_class_descriptor.type_descriptor == base_class_col.type_descriptor {
136 return Ok(true);
137 }
138 }
139
140 Ok(false)
141}
142
143#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
144#[allow(dead_code)]
145struct RttiCandidate {
146 vftable_meta_rva: Rva,
147 vftable_rva: Rva,
148}
149
150pub struct Class<'a, T: Pe<'a>> {
151 program: &'a T,
152 pub name: String,
153 pub vftable: Rva,
154}
155
156impl<'a, T: Pe<'a>> Class<'a, T> {
157 pub unsafe fn vmt_fn(&self, index: u32) -> Option<Va> {
162 Some(*self.program.derva(self.vftable + VA_SIZE * index).ok()?)
163 }
164
165 pub unsafe fn vmt_index(&self, index: u32) -> Option<NonNull<Va>> {
170 let ptr = self
171 .program
172 .rva_to_va(self.vftable + VA_SIZE * index)
173 .ok()? as *const u64 as *mut u64;
174
175 NonNull::new(ptr)
176 }
177}