squid/frontend/
section.rs

1use std::cmp::Ordering;
2
3use goblin;
4use paste::paste;
5
6use crate::{
7    event::EventPool,
8    frontend::{
9        error::LoaderError,
10        idmap::{
11            idmap_functions,
12            HasId,
13            HasIdMut,
14            Id,
15            IdMap,
16            IdMapValues,
17            IdMapValuesMut,
18        },
19        image::VAddr,
20        perms::Perms,
21        symbol::{
22            Symbol,
23            SymbolParser,
24        },
25    },
26    listing::ListingManager,
27};
28
29/// A Section is consecutive group of symbols that share the same permissions
30#[derive(Debug, Hash)]
31pub struct Section {
32    id: Id,
33    perms: Perms,
34    vaddr: VAddr,
35    offset: Option<usize>,
36    size: usize,
37    idmap: IdMap<Symbol>,
38    cursor: usize,
39}
40
41impl Section {
42    pub(crate) fn new(perms: Perms, vaddr: VAddr, offset: Option<usize>, size: usize) -> Self {
43        Self {
44            id: Id::default(),
45            perms,
46            vaddr,
47            offset,
48            size,
49            idmap: IdMap::new(),
50            cursor: 0,
51        }
52    }
53
54    pub(crate) fn offset(&self) -> Option<usize> {
55        self.offset
56    }
57
58    /// The last virtual address occupied by this section (size - 1)
59    pub fn last_addr(&self) -> VAddr {
60        self.vaddr + self.size as VAddr - 1
61    }
62
63    /// Check whether this section contains the givem virtual address
64    pub fn contains_address(&self, vaddr: VAddr) -> bool {
65        self.vaddr <= vaddr && vaddr <= self.last_addr()
66    }
67
68    /// The size of this section
69    pub fn size(&self) -> usize {
70        self.size
71    }
72
73    /// The permissions of this section
74    pub fn perms(&self) -> Perms {
75        self.perms
76    }
77
78    /// The virtual address of this section
79    pub fn vaddr(&self) -> VAddr {
80        self.vaddr
81    }
82
83    /// Change the size of this section
84    pub fn set_size(&mut self, size: usize) {
85        self.size = size;
86    }
87
88    /// Change the permissions of this section
89    pub fn set_perms(&mut self, perms: Perms) {
90        self.perms = perms;
91    }
92
93    /// Change the virtual address of this section
94    pub fn set_vaddr(&mut self, vaddr: VAddr) {
95        self.vaddr = vaddr;
96    }
97
98    /// Create a [`SectionBuilder`] that can build Sections from scratch
99    pub fn builder() -> SectionBuilder {
100        SectionBuilder {
101            perms: None,
102            vaddr: 0,
103            size: None,
104        }
105    }
106}
107
108idmap_functions!(Section, Symbol, symbol);
109
110impl HasId for Section {
111    fn id(&self) -> Id {
112        self.id
113    }
114}
115
116impl HasIdMut for Section {
117    fn id_mut(&mut self) -> &mut Id {
118        &mut self.id
119    }
120}
121
122/// The SectionBuilder can be used to build a [`Section`] from scratch
123pub struct SectionBuilder {
124    perms: Option<Perms>,
125    vaddr: VAddr,
126    size: Option<usize>,
127}
128
129impl SectionBuilder {
130    /// Set the permissions of this section
131    pub fn perms(mut self, perms: Perms) -> Self {
132        self.perms = Some(perms);
133        self
134    }
135
136    /// Set the virtual address of this section
137    pub fn vaddr(mut self, vaddr: VAddr) -> Self {
138        self.vaddr = vaddr;
139        self
140    }
141
142    /// Set the size of this section
143    pub fn size(mut self, size: usize) -> Self {
144        self.size = Some(size);
145        self
146    }
147
148    /// Finally, create the [`Section`]
149    pub fn build(self) -> Result<Section, &'static str> {
150        let perms = self.perms.ok_or("Section permissions were not set")?;
151        let size = self.size.ok_or("Section size was not set")?;
152        Ok(Section::new(perms, self.vaddr, None, size))
153    }
154}
155
156pub(crate) struct SectionParser {
157    sections: Vec<Section>,
158}
159
160impl SectionParser {
161    pub(crate) fn parse(
162        elf: &goblin::elf::Elf,
163        content: &[u8],
164        listing: &ListingManager,
165        event_pool: &mut EventPool,
166    ) -> Result<IdMap<Section>, LoaderError> {
167        /* Parse sections */
168        let mut parser = Self::new();
169        parser.parse_program_headers(elf)?;
170        parser.parse_section_headers(elf)?;
171        parser.merge_sections();
172        parser.verify(elf)?;
173
174        /*
175        #[cfg(test)]
176        {
177            parser.dump_sections();
178        }
179        */
180
181        /* Parse symbols */
182        for section in &mut parser.sections {
183            section.idmap = SymbolParser::parse(elf, section, content, listing, event_pool)?;
184        }
185
186        /* Build IdMap */
187        let mut map = IdMap::new();
188
189        for section in parser.sections {
190            map.insert(section);
191        }
192
193        Ok(map)
194    }
195
196    fn new() -> Self {
197        Self {
198            sections: Vec::new(),
199        }
200    }
201
202    fn parse_program_headers(&mut self, elf: &goblin::elf::Elf) -> Result<(), LoaderError> {
203        for ph in &elf.program_headers {
204            if ph.p_type == goblin::elf::program_header::PT_LOAD {
205                /* Parse permissions.
206                  The compiler might merge r-x and r-- sections into the
207                  same r-x segment so we ignore the x bit in the segment flags
208                  since we don't want to lift data into the IR.
209                  The only time a squid::Section can become executable is when the
210                  underlying ELF section is executable.
211                */
212                let mut perms = Perms::from_segment_flags(ph.p_flags);
213
214                if perms.is_inaccessible() {
215                    continue;
216                }
217
218                if perms.is_readable() || perms.is_writable() {
219                    perms.clear_executable();
220                }
221
222                if ph.p_memsz < ph.p_filesz {
223                    return Err(LoaderError::InvalidELF("Segment memsz < filesz".to_string()));
224                }
225
226                /* Parse memory layout. Split segment on p_filesz. */
227                let uninit_size = ph.p_memsz - ph.p_filesz;
228
229                if ph.p_filesz > 0 {
230                    self.add_section(
231                        perms,
232                        ph.p_vaddr as VAddr,
233                        Some(ph.p_offset as usize),
234                        ph.p_filesz as usize,
235                        true,
236                    )?;
237                }
238
239                if uninit_size > 0 {
240                    self.add_section(perms, (ph.p_vaddr + ph.p_filesz) as VAddr, None, uninit_size as usize, true)?;
241                }
242            }
243        }
244
245        Ok(())
246    }
247
248    fn parse_section_headers(&mut self, elf: &goblin::elf::Elf) -> Result<(), LoaderError> {
249        for sh in &elf.section_headers {
250            if sh.is_alloc() && (sh.sh_flags & goblin::elf::section_header::SHF_TLS as u64) == 0 {
251                let offset = match sh.sh_type {
252                    goblin::elf::section_header::SHT_NOBITS => None,
253                    _ => Some(sh.sh_offset as usize),
254                };
255
256                self.add_section(
257                    Perms::from_section_header(sh),
258                    sh.sh_addr as VAddr,
259                    offset,
260                    sh.sh_size as usize,
261                    false,
262                )?;
263            }
264        }
265
266        Ok(())
267    }
268
269    fn add_section(
270        &mut self,
271        perms: Perms,
272        vaddr: VAddr,
273        offset: Option<usize>,
274        size: usize,
275        from_segment: bool,
276    ) -> Result<(), LoaderError> {
277        if size == 0 {
278            return Ok(());
279        } else if perms.is_writable() && perms.is_executable() {
280            return Err(LoaderError::InvalidELF("Binary contains rwx sections/segments".to_string()));
281        }
282
283        let new_section = Section::new(perms, vaddr, offset, size);
284
285        match self.locate_section(&new_section) {
286            Ok(idx) => {
287                if from_segment {
288                    return Err(LoaderError::InvalidELF("Found overlapping segments".to_string()));
289                }
290
291                /* If we have a duplicate just update the permissions and offset */
292                let old_section = &mut self.sections[idx];
293                if new_section.vaddr == old_section.vaddr && new_section.last_addr() == old_section.last_addr() {
294                    old_section.perms = new_section.perms;
295                    old_section.offset = new_section.offset;
296                    return Ok(());
297                }
298
299                /* Otherwise we have to split the old section into two or three parts */
300                let mut old_section = self.sections.remove(idx);
301
302                if new_section.vaddr() == old_section.vaddr() {
303                    old_section.vaddr += new_section.size() as VAddr;
304                    if let Some(offset) = &mut old_section.offset {
305                        *offset += new_section.size();
306                    }
307                    old_section.size -= new_section.size();
308
309                    self.sections.insert(idx, new_section);
310                    self.sections.insert(idx + 1, old_section);
311                } else if new_section.last_addr() == old_section.last_addr() {
312                    old_section.size -= new_section.size();
313
314                    self.sections.insert(idx, old_section);
315                    self.sections.insert(idx + 1, new_section);
316                } else {
317                    let mut left =
318                        Section::new(old_section.perms, old_section.vaddr, old_section.offset, old_section.size);
319                    let mut right = old_section;
320
321                    left.size = (new_section.vaddr - left.vaddr) as usize;
322
323                    let shift_amount = left.size() + new_section.size();
324                    right.vaddr += shift_amount as VAddr;
325                    if let Some(offset) = &mut right.offset {
326                        *offset += shift_amount;
327                    }
328                    right.size -= shift_amount;
329
330                    self.sections.insert(idx, left);
331                    self.sections.insert(idx + 1, new_section);
332                    self.sections.insert(idx + 2, right);
333                }
334            },
335            Err(idx) => {
336                if !from_segment {
337                    return Err(LoaderError::InvalidELF(format!("Section not in any segment: {:#x}", vaddr)));
338                }
339
340                self.sections.insert(idx, new_section);
341            },
342        }
343
344        Ok(())
345    }
346
347    fn locate_section(&self, section: &Section) -> Result<usize, usize> {
348        self.sections.binary_search_by(|x| {
349            if x.contains_address(section.vaddr) && x.contains_address(section.last_addr()) {
350                Ordering::Equal
351            } else {
352                let l = x.vaddr.cmp(&section.vaddr);
353                let r = x.vaddr.cmp(&section.last_addr());
354                assert_eq!(l, r, "Overlapping sections: x={:?} section={:?}", x, section);
355                l
356            }
357        })
358    }
359
360    fn merge_sections(&mut self) {
361        let mut i = 1;
362
363        while i < self.sections.len() {
364            let l = &self.sections[i - 1];
365            let r = &self.sections[i];
366
367            let same_perms = l.perms() == r.perms();
368            let cont_vaddr = l.vaddr() + l.size() as VAddr == r.vaddr();
369            let cont_offset = l.offset().map(|x| x + l.size()) == r.offset();
370
371            if same_perms && cont_vaddr && cont_offset {
372                let section = self.sections.remove(i);
373                self.sections[i - 1].size += section.size();
374            } else {
375                i += 1;
376            }
377        }
378    }
379
380    fn verify(&self, elf: &goblin::elf::Elf) -> Result<(), LoaderError> {
381        /* Check for continuous memory layout */
382        for ph in &elf.program_headers {
383            if ph.p_type == goblin::elf::program_header::PT_LOAD {
384                /* Find starting index */
385                let mut idx = 0;
386
387                while idx < self.sections.len() && self.sections[idx].vaddr < ph.p_vaddr {
388                    idx += 1;
389                }
390
391                assert!(idx < self.sections.len());
392
393                /* Check for continuity */
394                let end_addr = ph.p_vaddr + ph.p_filesz;
395                let mut vaddr_cursor = ph.p_vaddr as VAddr;
396
397                while idx < self.sections.len() && self.sections[idx].vaddr < end_addr {
398                    assert_eq!(self.sections[idx].vaddr(), vaddr_cursor);
399                    vaddr_cursor += self.sections[idx].size() as VAddr;
400                    idx += 1;
401                }
402
403                assert_eq!(vaddr_cursor, end_addr);
404            }
405        }
406
407        /* Verify each section */
408        for section in &self.sections {
409            assert!(!section.perms().is_inaccessible());
410            assert!(section.size() > 0);
411        }
412
413        Ok(())
414    }
415
416    /*
417    #[cfg(test)]
418    pub fn dump_sections(&self) {
419        for section in &self.sections {
420            print!("{:08x}  ", section.vaddr());
421
422            if let Some(offset) = section.offset() {
423                print!("{:06x}", offset);
424            } else {
425                print!("      ");
426            }
427
428            print!("  {:04x}  ", section.size());
429
430            let perms = section.perms();
431            let r = if perms.is_readable() { "r" } else { "-" };
432            let w = if perms.is_writable() { "w" } else { "-" };
433            let x = if perms.is_executable() { "x" } else { "-" };
434            println!("{}{}{}", r, w, x);
435        }
436    }
437    */
438}