1 use core::str;
2 
3 use crate::endian::LittleEndian as LE;
4 use crate::pe;
5 use crate::read::{
6     self, ComdatKind, ObjectComdat, ReadError, ReadRef, Result, SectionIndex, SymbolIndex,
7 };
8 
9 use super::CoffFile;
10 
11 /// An iterator over the COMDAT section groups of a `CoffFile`.
12 #[derive(Debug)]
13 pub struct CoffComdatIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> {
14     pub(super) file: &'file CoffFile<'data, R>,
15     pub(super) index: usize,
16 }
17 
18 impl<'data, 'file, R: ReadRef<'data>> Iterator for CoffComdatIterator<'data, 'file, R> {
19     type Item = CoffComdat<'data, 'file, R>;
20 
next(&mut self) -> Option<Self::Item>21     fn next(&mut self) -> Option<Self::Item> {
22         loop {
23             let index = self.index;
24             let symbol = self.file.common.symbols.symbol(index).ok()?;
25             self.index += 1 + symbol.number_of_aux_symbols as usize;
26             if let Some(comdat) = CoffComdat::parse(self.file, symbol, index) {
27                 return Some(comdat);
28             }
29         }
30     }
31 }
32 
33 /// A COMDAT section group of a `CoffFile`.
34 #[derive(Debug)]
35 pub struct CoffComdat<'data, 'file, R: ReadRef<'data> = &'data [u8]> {
36     file: &'file CoffFile<'data, R>,
37     symbol_index: SymbolIndex,
38     symbol: &'data pe::ImageSymbol,
39     selection: u8,
40 }
41 
42 impl<'data, 'file, R: ReadRef<'data>> CoffComdat<'data, 'file, R> {
parse( file: &'file CoffFile<'data, R>, section_symbol: &'data pe::ImageSymbol, index: usize, ) -> Option<CoffComdat<'data, 'file, R>>43     fn parse(
44         file: &'file CoffFile<'data, R>,
45         section_symbol: &'data pe::ImageSymbol,
46         index: usize,
47     ) -> Option<CoffComdat<'data, 'file, R>> {
48         // Must be a section symbol.
49         if !section_symbol.has_aux_section() {
50             return None;
51         }
52 
53         // Auxiliary record must have a non-associative selection.
54         let aux = file.common.symbols.aux_section(index).ok()?;
55         let selection = aux.selection;
56         if selection == 0 || selection == pe::IMAGE_COMDAT_SELECT_ASSOCIATIVE {
57             return None;
58         }
59 
60         // Find the COMDAT symbol.
61         let mut symbol_index = index;
62         let mut symbol = section_symbol;
63         let section_number = section_symbol.section_number.get(LE);
64         loop {
65             symbol_index += 1 + symbol.number_of_aux_symbols as usize;
66             symbol = file.common.symbols.symbol(symbol_index).ok()?;
67             if section_number == symbol.section_number.get(LE) {
68                 break;
69             }
70         }
71 
72         Some(CoffComdat {
73             file,
74             symbol_index: SymbolIndex(symbol_index),
75             symbol,
76             selection,
77         })
78     }
79 }
80 
81 impl<'data, 'file, R: ReadRef<'data>> read::private::Sealed for CoffComdat<'data, 'file, R> {}
82 
83 impl<'data, 'file, R: ReadRef<'data>> ObjectComdat<'data> for CoffComdat<'data, 'file, R> {
84     type SectionIterator = CoffComdatSectionIterator<'data, 'file, R>;
85 
86     #[inline]
kind(&self) -> ComdatKind87     fn kind(&self) -> ComdatKind {
88         match self.selection {
89             pe::IMAGE_COMDAT_SELECT_NODUPLICATES => ComdatKind::NoDuplicates,
90             pe::IMAGE_COMDAT_SELECT_ANY => ComdatKind::Any,
91             pe::IMAGE_COMDAT_SELECT_SAME_SIZE => ComdatKind::SameSize,
92             pe::IMAGE_COMDAT_SELECT_EXACT_MATCH => ComdatKind::ExactMatch,
93             pe::IMAGE_COMDAT_SELECT_LARGEST => ComdatKind::Largest,
94             pe::IMAGE_COMDAT_SELECT_NEWEST => ComdatKind::Newest,
95             _ => ComdatKind::Unknown,
96         }
97     }
98 
99     #[inline]
symbol(&self) -> SymbolIndex100     fn symbol(&self) -> SymbolIndex {
101         self.symbol_index
102     }
103 
104     #[inline]
name(&self) -> Result<&str>105     fn name(&self) -> Result<&str> {
106         // Find the name of first symbol referring to the section.
107         let name = self.symbol.name(self.file.common.symbols.strings())?;
108         str::from_utf8(name)
109             .ok()
110             .read_error("Non UTF-8 COFF COMDAT name")
111     }
112 
113     #[inline]
sections(&self) -> Self::SectionIterator114     fn sections(&self) -> Self::SectionIterator {
115         CoffComdatSectionIterator {
116             file: self.file,
117             section_number: self.symbol.section_number.get(LE),
118             index: 0,
119         }
120     }
121 }
122 
123 /// An iterator over the sections in a COMDAT section group of a `CoffFile`.
124 #[derive(Debug)]
125 pub struct CoffComdatSectionIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> {
126     file: &'file CoffFile<'data, R>,
127     section_number: u16,
128     index: usize,
129 }
130 
131 impl<'data, 'file, R: ReadRef<'data>> Iterator for CoffComdatSectionIterator<'data, 'file, R> {
132     type Item = SectionIndex;
133 
next(&mut self) -> Option<Self::Item>134     fn next(&mut self) -> Option<Self::Item> {
135         // Find associated COMDAT symbols.
136         // TODO: it seems gcc doesn't use associated symbols for this
137         loop {
138             let index = self.index;
139             let symbol = self.file.common.symbols.symbol(index).ok()?;
140             self.index += 1 + symbol.number_of_aux_symbols as usize;
141 
142             // Must be a section symbol.
143             if !symbol.has_aux_section() {
144                 continue;
145             }
146 
147             let section_number = symbol.section_number.get(LE);
148 
149             let aux = self.file.common.symbols.aux_section(index).ok()?;
150             if aux.selection == pe::IMAGE_COMDAT_SELECT_ASSOCIATIVE {
151                 // TODO: use high_number for bigobj
152                 if aux.number.get(LE) == self.section_number {
153                     return Some(SectionIndex(section_number as usize));
154                 }
155             } else if aux.selection != 0 {
156                 if section_number == self.section_number {
157                     return Some(SectionIndex(section_number as usize));
158                 }
159             }
160         }
161     }
162 }
163