ms_pdb/dbi/
section_contrib.rs1use super::*;
12use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
13
14#[allow(missing_docs)]
16#[derive(Unaligned, IntoBytes, FromBytes, Immutable, KnownLayout, Clone, Debug)]
17#[repr(C)]
18pub struct SectionContribEntry {
19 pub section: U16<LE>,
21 pub padding1: [u8; 2],
23 pub offset: I32<LE>,
24 pub size: I32<LE>,
25 pub characteristics: U32<LE>,
26 pub module_index: U16<LE>,
28 pub padding2: [u8; 2],
30 pub data_crc: U32<LE>,
31 pub reloc_crc: U32<LE>,
32}
33
34#[allow(missing_docs)]
36#[derive(Unaligned, IntoBytes, FromBytes, Immutable, KnownLayout, Clone, Debug)]
37#[repr(C)]
38pub struct SectionContribEntry2 {
39 pub base: SectionContribEntry,
40 pub coff_section: U32<LE>,
41}
42
43impl SectionContribEntry {
44 pub fn contains_offset(&self, offset: i32) -> bool {
46 let self_offset = self.offset.get();
47 if offset < self_offset {
48 return false;
49 }
50
51 let overshoot = offset - self_offset;
52 if overshoot >= self.size.get() {
53 return false;
54 }
55
56 true
57 }
58}
59
60pub struct SectionContributionsSubstream<'a> {
62 pub contribs: &'a [SectionContribEntry],
64}
65
66pub const SECTION_CONTRIBUTIONS_SUBSTREAM_VER60: u32 = 0xeffe0000 + 19970605;
68
69impl<'a> SectionContributionsSubstream<'a> {
70 pub fn parse(bytes: &'a [u8]) -> anyhow::Result<Self> {
74 let mut p = Parser::new(bytes);
75 if p.is_empty() {
76 return Ok(Self { contribs: &[] });
77 }
78
79 let version = p.u32()?;
80
81 match version {
82 SECTION_CONTRIBUTIONS_SUBSTREAM_VER60 => {}
83 _ => {
84 bail!("The Section Contributions Substream has a version number that is not supported. Version: 0x{:08x}", version);
85 }
86 }
87
88 let records_bytes = p.into_rest();
89 let Ok(contribs) = <[SectionContribEntry]>::ref_from_bytes(records_bytes) else {
90 bail!("The Section Contributions stream has an invalid size. It is not a multiple of the section contribution record size. Size: 0x{:x}",
91 bytes.len());
92 };
93 Ok(SectionContributionsSubstream { contribs })
94 }
95
96 pub fn find(&self, section: u16, offset: i32) -> Option<&SectionContribEntry> {
99 let i = self.find_index(section, offset)?;
100 Some(&self.contribs[i])
101 }
102
103 pub fn find_index(&self, section: u16, offset: i32) -> Option<usize> {
106 match self
107 .contribs
108 .binary_search_by_key(&(section, offset), |con| {
109 (con.section.get(), con.offset.get())
110 }) {
111 Ok(i) => Some(i),
112 Err(i) => {
113 if i > 0 {
115 let previous = &self.contribs[i - 1];
116 if previous.contains_offset(offset) {
117 return Some(i - 1);
118 }
119 }
120
121 if i + 1 < self.contribs.len() {
122 let next = &self.contribs[i + 1];
123 if next.contains_offset(offset) {
124 return Some(i + 1);
125 }
126 }
127
128 None
129 }
130 }
131 }
132
133 pub fn find_brute(&self, section: u16, offset: i32) -> Option<&SectionContribEntry> {
136 let i = self.find_index_brute(section, offset)?;
137 Some(&self.contribs[i])
138 }
139
140 pub fn find_index_brute(&self, section: u16, offset: i32) -> Option<usize> {
143 self.contribs
144 .iter()
145 .position(|c| c.section.get() == section && c.contains_offset(offset))
146 }
147}
148
149pub struct SectionContributionsSubstreamMut<'a> {
151 pub contribs: &'a mut [SectionContribEntry],
153}
154
155impl<'a> SectionContributionsSubstreamMut<'a> {
156 pub fn parse(bytes: &'a mut [u8]) -> anyhow::Result<Self> {
158 let bytes_len = bytes.len();
159
160 let mut p = ParserMut::new(bytes);
161 if p.is_empty() {
162 return Ok(Self { contribs: &mut [] });
163 }
164
165 let version = p.u32()?;
166
167 match version {
168 SECTION_CONTRIBUTIONS_SUBSTREAM_VER60 => {}
169 _ => {
170 bail!("The Section Contributions Substream has a version number that is not supported. Version: 0x{:08x}", version);
171 }
172 }
173
174 let records_bytes = p.into_rest();
175
176 let Ok(contribs) = <[SectionContribEntry]>::mut_from_bytes(records_bytes) else {
177 bail!("The Section Contributions stream has an invalid size. It is not a multiple of the section contribution record size. Size: 0x{:x}",
178 bytes_len);
179 };
180 Ok(Self { contribs })
181 }
182
183 pub fn remap_module_indexes(&mut self, modules_old_to_new: &[u32]) -> anyhow::Result<()> {
186 for (i, contrib) in self.contribs.iter_mut().enumerate() {
187 let old = contrib.module_index.get();
188 if let Some(&new) = modules_old_to_new.get(old as usize) {
189 contrib.module_index.set(new as u16);
190 } else {
191 bail!("Section contribution record (at contribution index #{i} has module index {old}, \
192 which is out of range (num modules is {})",
193 modules_old_to_new.len());
194 }
195
196 contrib.padding1 = [0; 2];
198 contrib.padding2 = [0; 2];
199 }
200 Ok(())
201 }
202}