memflow_win32_defs/offsets/pdb/
data.rs

1// https://github.com/willglynn/pdb/blob/master/examples/pdb2hpp.rs
2
3use std::prelude::v1::*;
4
5use log::{info, trace};
6use std::collections::BTreeSet;
7
8pub type TypeSet = BTreeSet<pdb::TypeIndex>;
9
10pub fn type_name(
11    type_finder: &pdb::TypeFinder<'_>,
12    type_index: pdb::TypeIndex,
13    needed_types: &mut TypeSet,
14) -> pdb::Result<String> {
15    let mut name = match type_finder.find(type_index)?.parse()? {
16        pdb::TypeData::Primitive(data) => {
17            let mut name = match data.kind {
18                pdb::PrimitiveKind::Void => "void".to_string(),
19                pdb::PrimitiveKind::Char => "char".to_string(),
20                pdb::PrimitiveKind::UChar => "unsigned char".to_string(),
21
22                pdb::PrimitiveKind::I8 => "int8_t".to_string(),
23                pdb::PrimitiveKind::U8 => "uint8_t".to_string(),
24                pdb::PrimitiveKind::I16 => "int16_t".to_string(),
25                pdb::PrimitiveKind::U16 => "uint16_t".to_string(),
26                pdb::PrimitiveKind::I32 => "int32_t".to_string(),
27                pdb::PrimitiveKind::U32 => "uint32_t".to_string(),
28                pdb::PrimitiveKind::I64 => "int64_t".to_string(),
29                pdb::PrimitiveKind::U64 => "uint64_t".to_string(),
30
31                pdb::PrimitiveKind::F32 => "float".to_string(),
32                pdb::PrimitiveKind::F64 => "double".to_string(),
33
34                pdb::PrimitiveKind::Bool8 => "bool".to_string(),
35
36                _ => format!("unhandled_primitive.kind /* {:?} */", data.kind),
37            };
38
39            if data.indirection.is_some() {
40                name.push_str(" *");
41            }
42
43            name
44        }
45
46        pdb::TypeData::Class(data) => {
47            needed_types.insert(type_index);
48            data.name.to_string().into_owned()
49        }
50
51        pdb::TypeData::Enumeration(data) => {
52            needed_types.insert(type_index);
53            data.name.to_string().into_owned()
54        }
55
56        pdb::TypeData::Union(data) => {
57            needed_types.insert(type_index);
58            data.name.to_string().into_owned()
59        }
60
61        pdb::TypeData::Pointer(data) => format!(
62            "{}*",
63            type_name(type_finder, data.underlying_type, needed_types)?
64        ),
65
66        pdb::TypeData::Modifier(data) => {
67            if data.constant {
68                format!(
69                    "const {}",
70                    type_name(type_finder, data.underlying_type, needed_types)?
71                )
72            } else if data.volatile {
73                format!(
74                    "volatile {}",
75                    type_name(type_finder, data.underlying_type, needed_types)?
76                )
77            } else {
78                // ?
79                type_name(type_finder, data.underlying_type, needed_types)?
80            }
81        }
82
83        pdb::TypeData::Array(data) => {
84            let mut name = type_name(type_finder, data.element_type, needed_types)?;
85            for size in data.dimensions {
86                name = format!("{name}[{size}]");
87            }
88            name
89        }
90
91        x => format!("Type{type_index} /* TODO: figure out how to name it {x:?} */"),
92    };
93
94    // TODO: search and replace std:: patterns
95    if name == "std::basic_string<char,std::char_traits<char>,std::allocator<char> >" {
96        name = "std::string".to_string();
97    }
98
99    Ok(name)
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct Class<'p> {
104    pub kind: pdb::ClassKind,
105    pub name: pdb::RawString<'p>,
106    pub base_classes: Vec<BaseClass>,
107    pub fields: Vec<Field<'p>>,
108    pub instance_methods: Vec<Method<'p>>,
109    pub static_methods: Vec<Method<'p>>,
110}
111
112impl<'p> Class<'p> {
113    fn add_derived_from(&mut self, _: &pdb::TypeFinder<'p>, _: pdb::TypeIndex, _: &mut TypeSet) {
114        // TODO
115    }
116
117    fn add_fields(
118        &mut self,
119        type_finder: &pdb::TypeFinder<'p>,
120        type_index: pdb::TypeIndex,
121        needed_types: &mut TypeSet,
122    ) -> pdb::Result<()> {
123        match type_finder.find(type_index)?.parse()? {
124            pdb::TypeData::FieldList(data) => {
125                for field in &data.fields {
126                    self.add_field(type_finder, field, needed_types)?;
127                }
128
129                if let Some(continuation) = data.continuation {
130                    // recurse
131                    self.add_fields(type_finder, continuation, needed_types)?;
132                }
133            }
134            other => {
135                info!(
136                    "trying to Class::add_fields() got {} -> {:?}",
137                    type_index, other
138                );
139                panic!("unexpected type in Class::add_fields()");
140            }
141        }
142
143        Ok(())
144    }
145
146    fn add_field(
147        &mut self,
148        type_finder: &pdb::TypeFinder<'p>,
149        field: &pdb::TypeData<'p>,
150        needed_types: &mut TypeSet,
151    ) -> pdb::Result<()> {
152        match *field {
153            pdb::TypeData::Member(ref data) => {
154                // TODO: attributes (static, virtual, etc.)
155
156                let bit_offset = match type_finder.find(data.field_type)?.parse()? {
157                    pdb::TypeData::Bitfield(bitfield) => bitfield.position,
158                    _ => 0,
159                };
160
161                self.fields.push(Field {
162                    type_name: type_name(type_finder, data.field_type, needed_types)?,
163                    name: data.name,
164                    offset: data.offset,
165                    bit_offset,
166                });
167            }
168
169            pdb::TypeData::Method(ref data) => {
170                let method = Method::find(
171                    data.name,
172                    data.attributes,
173                    type_finder,
174                    data.method_type,
175                    needed_types,
176                )?;
177                if data.attributes.is_static() {
178                    self.static_methods.push(method);
179                } else {
180                    self.instance_methods.push(method);
181                }
182            }
183
184            pdb::TypeData::OverloadedMethod(ref data) => {
185                // this just means we have more than one method with the same name
186                // find the method list
187                match type_finder.find(data.method_list)?.parse()? {
188                    pdb::TypeData::MethodList(method_list) => {
189                        for pdb::MethodListEntry {
190                            attributes,
191                            method_type,
192                            ..
193                        } in method_list.methods
194                        {
195                            // hooray
196                            let method = Method::find(
197                                data.name,
198                                attributes,
199                                type_finder,
200                                method_type,
201                                needed_types,
202                            )?;
203
204                            if attributes.is_static() {
205                                self.static_methods.push(method);
206                            } else {
207                                self.instance_methods.push(method);
208                            }
209                        }
210                    }
211                    other => {
212                        info!(
213                            "processing OverloadedMethod, expected MethodList, got {} -> {:?}",
214                            data.method_list, other
215                        );
216                        panic!("unexpected type in Class::add_field()");
217                    }
218                }
219            }
220
221            pdb::TypeData::BaseClass(ref data) => self.base_classes.push(BaseClass {
222                type_name: type_name(type_finder, data.base_class, needed_types)?,
223                offset: data.offset,
224            }),
225
226            pdb::TypeData::VirtualBaseClass(ref data) => self.base_classes.push(BaseClass {
227                type_name: type_name(type_finder, data.base_class, needed_types)?,
228                offset: data.base_pointer_offset,
229            }),
230
231            _ => {
232                // ignore everything else even though that's sad
233            }
234        }
235
236        Ok(())
237    }
238}
239
240#[derive(Debug, Clone, PartialEq, Eq)]
241pub struct BaseClass {
242    pub type_name: String,
243    pub offset: u32,
244}
245
246#[derive(Debug, Clone, PartialEq, Eq)]
247pub struct Field<'p> {
248    pub type_name: String,
249    pub name: pdb::RawString<'p>,
250    pub offset: u64,
251    pub bit_offset: u8,
252}
253
254#[derive(Debug, Clone, PartialEq, Eq)]
255pub struct Method<'p> {
256    pub name: pdb::RawString<'p>,
257    pub return_type_name: String,
258    pub arguments: Vec<String>,
259    pub is_virtual: bool,
260}
261
262impl<'p> Method<'p> {
263    fn find(
264        name: pdb::RawString<'p>,
265        attributes: pdb::FieldAttributes,
266        type_finder: &pdb::TypeFinder<'p>,
267        type_index: pdb::TypeIndex,
268        needed_types: &mut TypeSet,
269    ) -> pdb::Result<Method<'p>> {
270        match type_finder.find(type_index)?.parse()? {
271            pdb::TypeData::MemberFunction(data) => Ok(Method {
272                name,
273                return_type_name: type_name(type_finder, data.return_type, needed_types)?,
274                arguments: argument_list(type_finder, data.argument_list, needed_types)?,
275                is_virtual: attributes.is_virtual(),
276            }),
277
278            other => {
279                info!("other: {:?}", other);
280                Err(pdb::Error::UnimplementedFeature("that"))
281            }
282        }
283    }
284}
285
286fn argument_list(
287    type_finder: &pdb::TypeFinder<'_>,
288    type_index: pdb::TypeIndex,
289    needed_types: &mut TypeSet,
290) -> pdb::Result<Vec<String>> {
291    match type_finder.find(type_index)?.parse()? {
292        pdb::TypeData::ArgumentList(data) => {
293            let mut args: Vec<String> = Vec::new();
294            for arg_type in data.arguments {
295                args.push(type_name(type_finder, arg_type, needed_types)?);
296            }
297            Ok(args)
298        }
299        _ => Err(pdb::Error::UnimplementedFeature(
300            "argument list of non-argument-list type",
301        )),
302    }
303}
304
305#[derive(Debug, Clone, PartialEq, Eq)]
306pub struct Enum<'p> {
307    name: pdb::RawString<'p>,
308    underlying_type_name: String,
309    values: Vec<EnumValue<'p>>,
310}
311
312impl<'p> Enum<'p> {
313    fn add_fields(
314        &mut self,
315        type_finder: &pdb::TypeFinder<'p>,
316        type_index: pdb::TypeIndex,
317        needed_types: &mut TypeSet,
318    ) -> pdb::Result<()> {
319        match type_finder.find(type_index)?.parse()? {
320            pdb::TypeData::FieldList(data) => {
321                for field in &data.fields {
322                    self.add_field(type_finder, field, needed_types);
323                }
324
325                if let Some(continuation) = data.continuation {
326                    // recurse
327                    self.add_fields(type_finder, continuation, needed_types)?;
328                }
329            }
330            other => {
331                info!(
332                    "trying to Enum::add_fields() got {} -> {:?}",
333                    type_index, other
334                );
335                panic!("unexpected type in Enum::add_fields()");
336            }
337        }
338
339        Ok(())
340    }
341
342    fn add_field(&mut self, _: &pdb::TypeFinder<'p>, field: &pdb::TypeData<'p>, _: &mut TypeSet) {
343        // ignore everything else even though that's sad
344        if let pdb::TypeData::Enumerate(ref data) = field {
345            self.values.push(EnumValue {
346                name: data.name,
347                value: data.value,
348            });
349        }
350    }
351}
352
353#[derive(Debug, Clone, PartialEq, Eq)]
354pub struct EnumValue<'p> {
355    name: pdb::RawString<'p>,
356    value: pdb::Variant,
357}
358
359#[derive(Debug, Clone, PartialEq, Eq)]
360pub struct ForwardReference<'p> {
361    kind: pdb::ClassKind,
362    name: pdb::RawString<'p>,
363}
364
365#[derive(Debug, Clone, PartialEq, Eq)]
366pub struct Data<'p> {
367    pub forward_references: Vec<ForwardReference<'p>>,
368    pub classes: Vec<Class<'p>>,
369    pub enums: Vec<Enum<'p>>,
370}
371
372impl<'p> Data<'p> {
373    pub fn new() -> Data<'p> {
374        Data {
375            forward_references: Vec::new(),
376            classes: Vec::new(),
377            enums: Vec::new(),
378        }
379    }
380
381    pub fn add(
382        &mut self,
383        type_finder: &pdb::TypeFinder<'p>,
384        type_index: pdb::TypeIndex,
385        needed_types: &mut TypeSet,
386    ) -> pdb::Result<()> {
387        match type_finder.find(type_index)?.parse()? {
388            pdb::TypeData::Class(data) => {
389                if data.properties.forward_reference() {
390                    self.forward_references.push(ForwardReference {
391                        kind: data.kind,
392                        name: data.name,
393                    });
394
395                    return Ok(());
396                }
397
398                let mut class = Class {
399                    kind: data.kind,
400                    name: data.name,
401                    fields: Vec::new(),
402                    base_classes: Vec::new(),
403                    instance_methods: Vec::new(),
404                    static_methods: Vec::new(),
405                };
406
407                if let Some(derived_from) = data.derived_from {
408                    class.add_derived_from(type_finder, derived_from, needed_types);
409                }
410
411                if let Some(fields) = data.fields {
412                    class.add_fields(type_finder, fields, needed_types)?;
413                }
414
415                self.classes.insert(0, class);
416            }
417
418            pdb::TypeData::Enumeration(data) => {
419                let mut e = Enum {
420                    name: data.name,
421                    underlying_type_name: type_name(
422                        type_finder,
423                        data.underlying_type,
424                        needed_types,
425                    )?,
426                    values: Vec::new(),
427                };
428
429                e.add_fields(type_finder, data.fields, needed_types)?;
430
431                self.enums.insert(0, e);
432            }
433
434            // ignore
435            other => trace!("don't know how to add {:?}", other),
436        }
437
438        Ok(())
439    }
440}