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