llvm_mapper/record/
comdat.rs

1//! Functionality for mapping the `MODULE_CODE_COMDAT` record.
2
3use std::convert::TryInto;
4
5use llvm_support::StrtabRef;
6use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
7use thiserror::Error;
8
9use crate::block::strtab::StrtabError;
10use crate::map::{MapError, PartialCtxMappable, PartialMapCtx};
11use crate::unroll::UnrolledRecord;
12
13/// Errors that can occur when mapping a COMDAT record.
14#[non_exhaustive]
15#[derive(Debug, Error)]
16pub enum ComdatError {
17    /// The COMDAT record is in an old unsupported format.
18    #[error("unsupported COMDAT record format (v1)")]
19    V1Unsupported,
20
21    /// The COMDAT record is too short.
22    #[error("COMDAT record doesn't have enough fields ({0} < 3)")]
23    TooShort(usize),
24
25    /// We couldn't get the COMDAT's name from the string table.
26    #[error("error while accessing COMDAT name: {0}")]
27    Name(#[from] StrtabError),
28
29    /// The COMDAT's selection kind is invalid or unknown.
30    #[error("unknown or invalid COMDAT selection kind: {0}")]
31    SelectionKind(#[from] TryFromPrimitiveError<SelectionKind>),
32
33    /// A generic mapping error occured.
34    #[error("mapping error in comdat list")]
35    Map(#[from] MapError),
36}
37
38/// The different kinds of COMDAT selections.
39///
40/// This is a nearly direct copy of LLVM's `SelectionKind`; see `IR/Comdat.h`.
41#[non_exhaustive]
42#[derive(Debug, TryFromPrimitive)]
43#[repr(u64)]
44pub enum SelectionKind {
45    /// The linker may choose any COMDAT.
46    Any,
47    /// The data referenced by the COMDAT must be the same.
48    ExactMatch,
49    /// The linker will choose the largest COMDAT.
50    Largest,
51    /// No deduplication is performed.
52    NoDeduplicate,
53    /// The data referenced by the COMDAT must be the same size.
54    SameSize,
55}
56
57/// Models the `MODULE_CODE_COMDAT` record.
58#[non_exhaustive]
59#[derive(Debug)]
60pub struct Comdat {
61    /// The selection kind for this COMDAT.
62    pub selection_kind: SelectionKind,
63    /// The COMDAT key.
64    pub name: String,
65}
66
67impl PartialCtxMappable<UnrolledRecord> for Comdat {
68    type Error = ComdatError;
69
70    fn try_map(record: &UnrolledRecord, ctx: &mut PartialMapCtx) -> Result<Self, Self::Error> {
71        if !ctx.use_strtab().map_err(MapError::Context)? {
72            return Err(ComdatError::V1Unsupported);
73        }
74
75        // v2: [strtab offset, strtab size, selection kind]
76        if record.fields().len() != 3 {
77            return Err(ComdatError::TooShort(record.fields().len()));
78        }
79
80        // Index safety: we check for at least 3 fields above.
81        let name = {
82            let sref: StrtabRef = (record.fields()[0], record.fields()[1]).into();
83            ctx.strtab.try_get(&sref)?.into()
84        };
85        let selection_kind: SelectionKind = record.fields()[2].try_into()?;
86
87        Ok(Self {
88            selection_kind,
89            name: name,
90        })
91    }
92}