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

            
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> {
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]
87
    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]
100
    fn symbol(&self) -> SymbolIndex {
101
        self.symbol_index
102
    }
103

            
104
    #[inline]
105
    fn name_bytes(&self) -> Result<&[u8]> {
106
        // Find the name of first symbol referring to the section.
107
        self.symbol.name(self.file.common.symbols.strings())
108
    }
109

            
110
    #[inline]
111
    fn name(&self) -> Result<&str> {
112
        let bytes = self.name_bytes()?;
113
        str::from_utf8(bytes)
114
            .ok()
115
            .read_error("Non UTF-8 COFF COMDAT name")
116
    }
117

            
118
    #[inline]
119
    fn sections(&self) -> Self::SectionIterator {
120
        CoffComdatSectionIterator {
121
            file: self.file,
122
            section_number: self.symbol.section_number.get(LE),
123
            index: 0,
124
        }
125
    }
126
}
127

            
128
/// An iterator over the sections in a COMDAT section group of a `CoffFile`.
129
#[derive(Debug)]
130
pub struct CoffComdatSectionIterator<'data, 'file, R: ReadRef<'data> = &'data [u8]> {
131
    file: &'file CoffFile<'data, R>,
132
    section_number: u16,
133
    index: usize,
134
}
135

            
136
impl<'data, 'file, R: ReadRef<'data>> Iterator for CoffComdatSectionIterator<'data, 'file, R> {
137
    type Item = SectionIndex;
138

            
139
    fn next(&mut self) -> Option<Self::Item> {
140
        // Find associated COMDAT symbols.
141
        // TODO: it seems gcc doesn't use associated symbols for this
142
        loop {
143
            let index = self.index;
144
            let symbol = self.file.common.symbols.symbol(index).ok()?;
145
            self.index += 1 + symbol.number_of_aux_symbols as usize;
146

            
147
            // Must be a section symbol.
148
            if !symbol.has_aux_section() {
149
                continue;
150
            }
151

            
152
            let section_number = symbol.section_number.get(LE);
153

            
154
            let aux = self.file.common.symbols.aux_section(index).ok()?;
155
            if aux.selection == pe::IMAGE_COMDAT_SELECT_ASSOCIATIVE {
156
                // TODO: use high_number for bigobj
157
                if aux.number.get(LE) == self.section_number {
158
                    return Some(SectionIndex(section_number as usize));
159
                }
160
            } else if aux.selection != 0 {
161
                if section_number == self.section_number {
162
                    return Some(SectionIndex(section_number as usize));
163
                }
164
            }
165
        }
166
    }
167
}