llvm_mapper/block/
attributes.rs

1//! Functionality for mapping the `PARAMATTR_BLOCK` and `PARAMATTR_GROUP_BLOCK` blocks.
2
3use std::convert::{TryFrom, TryInto};
4
5use hashbrown::HashMap;
6use llvm_support::bitcodes::{AttributeCode, IrBlockId};
7use llvm_support::{AttributeId, AttributeKind, MaybeAlign};
8use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
9use thiserror::Error;
10
11use crate::block::IrBlock;
12use crate::map::{MapError, PartialMapCtx};
13use crate::unroll::{UnrolledBlock, UnrolledRecord};
14
15/// Errors that can occur when mapping attribute blocks.
16#[derive(Debug, Error)]
17pub enum AttributeError {
18    /// An unknown record code was seen.
19    #[error("unknown attribute code")]
20    UnknownAttributeCode(#[from] TryFromPrimitiveError<AttributeCode>),
21
22    /// An unknown attribute kind (format) was seen.
23    #[error("unknown attribute kind")]
24    UnknownAttributeKind(#[from] TryFromPrimitiveError<AttributeKind>),
25
26    /// The given code was seen in an unexpected block.
27    #[error("wrong block for code: {0:?}")]
28    WrongBlock(AttributeCode),
29
30    /// The attribute couldn't be constructed because of missing fields.
31    #[error("attribute structure too short")]
32    TooShort,
33
34    /// The attribute has an invalid string key or string balue.
35    #[error("bad attribute string")]
36    BadString,
37
38    /// The attribute has an unknown (integral) ID.
39    #[error("unknown attribute ID")]
40    UnknownAttributeId(#[from] TryFromPrimitiveError<AttributeId>),
41
42    /// The attribute's ID doesn't match the format supplied.
43    #[error("malformed attribute (format doesn't match ID): {0}: {1:?}")]
44    AttributeMalformed(&'static str, AttributeId),
45
46    /// We recognize the attribute's ID as an integer attribute, but we don't support it yet.
47    #[error("FIXME: unsupported integer attribute: {0:?}")]
48    IntAttributeUnsupported(AttributeId),
49
50    /// An entry record asked for a nonexistent attribute group.
51    #[error("nonexistent attribute group: {0}")]
52    BadAttributeGroup(u32),
53
54    /// An attribute group record was too short.
55    #[error("attribute group record for {0:?} too short ({1} < 3)")]
56    GroupTooShort(AttributeCode, usize),
57
58    /// Parsing an attribute group didn't fully consume the underlying record fields.
59    #[error("under/overconsumed fields in attribute group record ({0} fields, {1} consumed)")]
60    GroupSizeMismatch(usize, usize),
61
62    /// A generic mapping error occured.
63    #[error("mapping error in string table")]
64    Map(#[from] MapError),
65}
66
67/// Represents the "enum" attributes, i.e. those with a single integer identifier.
68#[non_exhaustive]
69#[derive(Copy, Clone, Debug, PartialEq, TryFromPrimitive)]
70#[repr(u64)]
71pub enum EnumAttribute {
72    /// `alwaysinline`
73    AlwaysInline = AttributeId::AlwaysInline as u64,
74    /// `byval`
75    ByVal = AttributeId::ByVal as u64,
76    /// `inlinehint`
77    InlineHint = AttributeId::InlineHint as u64,
78    /// `inreg`
79    InReg = AttributeId::InReg as u64,
80    /// `minsize`
81    MinSize = AttributeId::MinSize as u64,
82    /// `naked`
83    Naked = AttributeId::Naked as u64,
84    /// `nest`
85    Nest = AttributeId::Nest as u64,
86    /// `noalias`
87    NoAlias = AttributeId::NoAlias as u64,
88    /// `nobuiltin`
89    NoBuiltin = AttributeId::NoBuiltin as u64,
90    /// `nocapture`
91    NoCapture = AttributeId::NoCapture as u64,
92    /// `noduplicate`
93    NoDuplicate = AttributeId::NoDuplicate as u64,
94    /// `noimplicitfloat`
95    NoImplicitFloat = AttributeId::NoImplicitFloat as u64,
96    /// `noinline`
97    NoInline = AttributeId::NoInline as u64,
98    /// `nonlazybind`
99    NonLazyBind = AttributeId::NonLazyBind as u64,
100    /// `noredzone`
101    NoRedZone = AttributeId::NoRedZone as u64,
102    /// `noreturn`
103    NoReturn = AttributeId::NoReturn as u64,
104    /// `nounwind`
105    NoUnwind = AttributeId::NoUnwind as u64,
106    /// `optsize`
107    OptimizeForSize = AttributeId::OptimizeForSize as u64,
108    /// `readnone`
109    ReadNone = AttributeId::ReadNone as u64,
110    /// `readonly`
111    ReadOnly = AttributeId::ReadOnly as u64,
112    /// `returned`
113    Returned = AttributeId::Returned as u64,
114    /// `returns_twice`
115    ReturnsTwice = AttributeId::ReturnsTwice as u64,
116    /// `signext`
117    SExt = AttributeId::SExt as u64,
118    /// `ssp`
119    StackProtect = AttributeId::StackProtect as u64,
120    /// `sspreq`
121    StackProtectReq = AttributeId::StackProtectReq as u64,
122    /// `sspstrong`
123    StackProtectStrong = AttributeId::StackProtectStrong as u64,
124    /// `sret`
125    StructRet = AttributeId::StructRet as u64,
126    /// `sanitize_address`
127    SanitizeAddress = AttributeId::SanitizeAddress as u64,
128    /// `sanitize_thread`
129    SanitizeThread = AttributeId::SanitizeThread as u64,
130    /// `sanitize_memory`
131    SanitizeMemory = AttributeId::SanitizeMemory as u64,
132    /// `uwtable`
133    UwTable = AttributeId::UwTable as u64,
134    /// `zeroext`
135    ZExt = AttributeId::ZExt as u64,
136    /// `builtin`
137    Builtin = AttributeId::Builtin as u64,
138    /// `cold`
139    Cold = AttributeId::Cold as u64,
140    /// `optnone`
141    OptimizeNone = AttributeId::OptimizeNone as u64,
142    /// `inalloca`
143    InAlloca = AttributeId::InAlloca as u64,
144    /// `nonnull`
145    NonNull = AttributeId::NonNull as u64,
146    /// `jumptable`
147    JumpTable = AttributeId::JumpTable as u64,
148    /// `convergent`
149    Convergent = AttributeId::Convergent as u64,
150    /// `safestack`
151    SafeStack = AttributeId::SafeStack as u64,
152    /// `argmemonly`
153    ArgMemOnly = AttributeId::ArgMemOnly as u64,
154    /// `swiftself`
155    SwiftSelf = AttributeId::SwiftSelf as u64,
156    /// `swifterror`
157    SwiftError = AttributeId::SwiftError as u64,
158    /// `norecurse`
159    NoRecurse = AttributeId::NoRecurse as u64,
160    /// `inaccessiblememonly`
161    InaccessiblememOnly = AttributeId::InaccessiblememOnly as u64,
162    /// `inaccessiblememonly_or_argmemonly`
163    InaccessiblememOrArgmemonly = AttributeId::InaccessiblememOrArgmemonly as u64,
164    /// `writeonly`
165    WriteOnly = AttributeId::WriteOnly as u64,
166    /// `speculatable`
167    Speculatable = AttributeId::Speculatable as u64,
168    /// `strictfp`
169    StrictFp = AttributeId::StrictFp as u64,
170    /// `sanitize_hwaddress`
171    SanitizeHwAddress = AttributeId::SanitizeHwAddress as u64,
172    /// `nocf_check`
173    NoCfCheck = AttributeId::NoCfCheck as u64,
174    /// `optforfuzzing`
175    OptForFuzzing = AttributeId::OptForFuzzing as u64,
176    /// `shadowcallstack`
177    Shadowcallstack = AttributeId::Shadowcallstack as u64,
178    /// `speculative_load_hardening`
179    SpeculativeLoadHardening = AttributeId::SpeculativeLoadHardening as u64,
180    /// `immarg`
181    ImmArg = AttributeId::ImmArg as u64,
182    /// `willreturn`
183    WillReturn = AttributeId::WillReturn as u64,
184    /// `nofree`
185    NoFree = AttributeId::NoFree as u64,
186    /// `nosync`
187    NoSync = AttributeId::NoSync as u64,
188    /// `sanitize_memtag`
189    SanitizeMemtag = AttributeId::SanitizeMemtag as u64,
190    /// `preallocated`
191    Preallocated = AttributeId::Preallocated as u64,
192    /// `no_merge`
193    NoMerge = AttributeId::NoMerge as u64,
194    /// `null_pointer_is_valid`
195    NullPointerIsValid = AttributeId::NullPointerIsValid as u64,
196    /// `noundef`
197    NoUndef = AttributeId::NoUndef as u64,
198    /// `byref`
199    ByRef = AttributeId::ByRef as u64,
200    /// `mustprogress`
201    MustProgress = AttributeId::MustProgress as u64,
202    /// `no_callback`
203    NoCallback = AttributeId::NoCallback as u64,
204    /// `hot`
205    Hot = AttributeId::Hot as u64,
206    /// `no_profile`
207    NoProfile = AttributeId::NoProfile as u64,
208    /// `swift_async`
209    SwiftAsync = AttributeId::SwiftAsync as u64,
210    /// `nosanitize_coverage`
211    NoSanitizeCoverage = AttributeId::NoSanitizeCoverage as u64,
212    /// `elementtype`
213    ElementType = AttributeId::ElementType as u64,
214    /// `disable_sanitizer_instrumentation`
215    DisableSanitizerInstrumentation = AttributeId::DisableSanitizerInstrumentation as u64,
216}
217
218impl TryFrom<AttributeId> for EnumAttribute {
219    type Error = AttributeError;
220
221    fn try_from(value: AttributeId) -> Result<Self, Self::Error> {
222        (value as u64)
223            .try_into()
224            .map_err(|_| AttributeError::AttributeMalformed("non-enum attribute ID given", value))
225    }
226}
227
228/// Represents an integral attribute, i.e. an attribute that carries (at least) one integer value with it.
229#[non_exhaustive]
230#[derive(Copy, Clone, Debug, PartialEq)]
231pub enum IntAttribute {
232    /// `align(<n>)`
233    Alignment(MaybeAlign),
234    /// `alignstack(<n>)`
235    StackAlignment(MaybeAlign),
236    /// `dereferenceable(<n>)`
237    Dereferenceable(u64),
238    /// `dereferenceable_or_null(<n>)`
239    DereferenceableOrNull(u64),
240    /// `allocsize(<EltSizeParam>[, <NumEltsParam>])`
241    AllocSize(u32, Option<u32>),
242    /// `vscale_range(<Min>[, <Max>])`
243    VScaleRange(u32, u32),
244}
245
246impl TryFrom<(AttributeId, u64)> for IntAttribute {
247    type Error = AttributeError;
248
249    fn try_from((key, value): (AttributeId, u64)) -> Result<Self, Self::Error> {
250        // Test if it's an enum attribute. If it is, we know it can't be an integer attribute
251        // and any fallthrough in our match below is unsupported rather than malformed.
252        if EnumAttribute::try_from(key).is_err() {
253            return Err(AttributeError::AttributeMalformed(
254                "expected int attribute, but given enum ID",
255                key,
256            ));
257        }
258
259        Ok(match key {
260            AttributeId::Alignment => {
261                let value = u8::try_from(value).map_err(|_| {
262                    AttributeError::AttributeMalformed(
263                        "attribute value too large (invalid alignment)",
264                        key,
265                    )
266                })?;
267
268                IntAttribute::Alignment(
269                    MaybeAlign::try_from(value).map_err(|_| {
270                        AttributeError::AttributeMalformed("invalid alignment", key)
271                    })?,
272                )
273            }
274            AttributeId::StackAlignment => {
275                let value = u8::try_from(value).map_err(|_| {
276                    AttributeError::AttributeMalformed(
277                        "attribute value too large (invalid alignment)",
278                        key,
279                    )
280                })?;
281
282                IntAttribute::StackAlignment(
283                    MaybeAlign::try_from(value).map_err(|_| {
284                        AttributeError::AttributeMalformed("invalid alignment", key)
285                    })?,
286                )
287            }
288            AttributeId::Dereferenceable => IntAttribute::Dereferenceable(value),
289            AttributeId::DereferenceableOrNull => IntAttribute::DereferenceableOrNull(value),
290            AttributeId::AllocSize => {
291                if value == 0 {
292                    return Err(AttributeError::AttributeMalformed(
293                        "allocasize argument invalid: cannot be (0, 0)",
294                        key,
295                    ));
296                }
297
298                // NOTE(ww): This attribute isn't well documented. From reading the LLVM code:
299                // * `value` can't be 0, but the upper 32 bits of `value` can be
300                // * The lower 32 bits should be 0xFFFFFFFF (-1) if not present
301                let elt_size = (value >> 32) as u32;
302                let num_elts = match value as u32 {
303                    u32::MAX => None,
304                    num_elts => Some(num_elts),
305                };
306
307                IntAttribute::AllocSize(elt_size, num_elts)
308            }
309            AttributeId::VScaleRange => {
310                let min = (value >> 32) as u32;
311                let max = match value as u32 {
312                    0 => min,
313                    max => max,
314                };
315
316                IntAttribute::VScaleRange(max, min)
317            }
318            o => return Err(AttributeError::IntAttributeUnsupported(o)),
319        })
320    }
321}
322
323/// Represents a single, concrete LLVM attribute.
324#[non_exhaustive]
325#[derive(Clone, Debug, PartialEq)]
326pub enum Attribute {
327    /// An enumerated attribute.
328    Enum(EnumAttribute),
329    /// An integer attribute.
330    Int(IntAttribute),
331    /// An arbitrary string attribute.
332    Str(String),
333    /// An arbitrary string attribute with a string value.
334    StrKeyValue(String, String),
335}
336
337impl Attribute {
338    /// Parse a new `Attribute` from the given record at the given start index, returning
339    /// a tuple of the number of fields consumed and the parsed result.
340    fn from_record(start: usize, record: &UnrolledRecord) -> Result<(usize, Self), AttributeError> {
341        let mut fieldcount = 0;
342
343        // You might ask: why are these macros?
344        // I originally wrote them as clever little locally-capturing lambdas, but
345        // having both mutate their closure confused the borrow checker.
346        // Writing them as macros lets everything expand inline, keeping the checker happy.
347        macro_rules! next {
348            () => {
349                if let Some(field) = record.fields().get(start + fieldcount) {
350                    fieldcount += 1;
351                    Ok(*field)
352                } else {
353                    Err(AttributeError::TooShort)
354                }
355            };
356        }
357
358        macro_rules! take_string {
359            // NOTE(ww): Weird double-brace to make sure the macro expands as a full expression.
360            () => {{
361                let str_bytes = record.fields()[start + fieldcount..]
362                    .iter()
363                    .take_while(|f| **f != 0)
364                    .map(|f| u8::try_from(*f))
365                    .collect::<Result<Vec<_>, _>>()
366                    .map_err(|_| AttributeError::BadString)?;
367
368                if str_bytes.is_empty() {
369                    Err(AttributeError::BadString)
370                } else {
371                    let result =
372                        String::from_utf8(str_bytes).map_err(|_| AttributeError::BadString)?;
373
374                    // NOTE(ww): plus one to include the NULL byte that we consumed above.
375                    fieldcount += result.as_bytes().len() + 1;
376
377                    Ok(result)
378                }
379            }};
380        }
381
382        // Each attribute's fields look like this:
383        //  [kind, key[...], [value[...]]]
384        // ...where `kind` indicates the general attribute structure
385        // (integral or string, single-value or key-value).
386        let kind = AttributeKind::try_from(next!()?)?;
387        match kind {
388            AttributeKind::Enum => {
389                // Enum attributes: one key field, nothing else.
390                let key = AttributeId::try_from(next!()?)?;
391                Ok((fieldcount, Attribute::Enum(key.try_into()?)))
392            }
393            AttributeKind::IntKeyValue => {
394                // Integer key-value attributes: one key, one integer value.
395                let key = AttributeId::try_from(next!()?)?;
396                let value = next!()?;
397
398                Ok((fieldcount, Attribute::Int(TryInto::try_into((key, value))?)))
399            }
400            AttributeKind::StrKey => {
401                // String attributes: one string key field, nothing else.
402                let key = take_string!()?;
403
404                Ok((fieldcount, Attribute::Str(key)))
405            }
406            AttributeKind::StrKeyValue => {
407                // String key-value attributes: one string key field, one string value field.
408                let key = take_string!()?;
409                let value = take_string!()?;
410
411                Ok((fieldcount, Attribute::StrKeyValue(key, value)))
412            }
413        }
414    }
415}
416
417/// Represents all of the [`AttributeGroup`](AttributeGroup)s associated with some function.
418#[derive(Debug)]
419pub struct AttributeEntry(Vec<AttributeGroup>);
420
421/// Maps all attributes in an IR module.
422#[derive(Debug, Default)]
423pub struct Attributes(Vec<AttributeEntry>);
424
425impl Attributes {
426    pub(crate) fn get(&self, id: u64) -> Option<&AttributeEntry> {
427        self.0.get(id as usize)
428    }
429}
430
431impl IrBlock for Attributes {
432    type Error = AttributeError;
433
434    const BLOCK_ID: IrBlockId = IrBlockId::ParamAttr;
435
436    fn try_map_inner(block: &UnrolledBlock, ctx: &mut PartialMapCtx) -> Result<Self, Self::Error> {
437        let mut entries = vec![];
438
439        for record in block.records() {
440            let code = AttributeCode::try_from(record.code()).map_err(AttributeError::from)?;
441
442            match code {
443                AttributeCode::EntryOld => {
444                    unimplemented!();
445                }
446                AttributeCode::Entry => {
447                    let mut groups = vec![];
448                    for group_id in record.fields() {
449                        let group_id = *group_id as u32;
450                        log::debug!("group id: {}", group_id);
451                        groups.push(
452                            ctx.attribute_groups()
453                                .get(group_id)
454                                .ok_or(AttributeError::BadAttributeGroup(group_id))?
455                                .clone(),
456                        );
457                    }
458                    entries.push(AttributeEntry(groups));
459                }
460                AttributeCode::GroupCodeEntry => {
461                    // This is a valid attribute code, but it isn't valid in this block.
462                    return Err(AttributeError::WrongBlock(code));
463                }
464            }
465        }
466
467        Ok(Attributes(entries))
468    }
469}
470
471/// Represents the "disposition" of an attribute group, i.e. whether its attributes
472/// are associated with the return value, specific parameters, or the entire associated function.
473#[derive(Clone, Copy, Debug)]
474pub enum AttributeGroupDisposition {
475    /// The associated attributes are return value attributes.
476    Return,
477    /// The associated attributes are parameter attributes (1-indexed).
478    Parameter(u32),
479    /// The associated attributes are function attributes.
480    Function,
481}
482
483impl From<u32> for AttributeGroupDisposition {
484    fn from(value: u32) -> Self {
485        match value {
486            u32::MAX => Self::Function,
487            0 => Self::Return,
488            _ => Self::Parameter(value),
489        }
490    }
491}
492
493/// Represents a single attribute group.
494#[derive(Clone, Debug)]
495pub struct AttributeGroup {
496    /// The "disposition" of this attribute group.
497    pub disposition: AttributeGroupDisposition,
498    /// The attributes in this group.
499    pub attributes: Vec<Attribute>,
500}
501
502/// Maps all attribute groups in an IR module.
503#[derive(Debug, Default)]
504pub struct AttributeGroups(HashMap<u32, AttributeGroup>);
505
506impl AttributeGroups {
507    pub(crate) fn get(&self, group_id: u32) -> Option<&AttributeGroup> {
508        self.0.get(&group_id)
509    }
510}
511
512impl IrBlock for AttributeGroups {
513    type Error = AttributeError;
514
515    const BLOCK_ID: IrBlockId = IrBlockId::ParamAttrGroup;
516
517    fn try_map_inner(block: &UnrolledBlock, _ctx: &mut PartialMapCtx) -> Result<Self, Self::Error> {
518        let mut groups = HashMap::new();
519
520        for record in block.records() {
521            let code = AttributeCode::try_from(record.code()).map_err(AttributeError::from)?;
522
523            if code != AttributeCode::GroupCodeEntry {
524                return Err(AttributeError::WrongBlock(code));
525            }
526
527            // Structure: [grpid, paramidx, <attr0>, <attr1>, ...]
528            // Every group record must have at least one attribute.
529            if record.fields().len() < 3 {
530                return Err(AttributeError::GroupTooShort(code, record.fields().len()));
531            }
532
533            // Panic safety: We check for at least three fields above.
534            let group_id = record.fields()[0] as u32;
535            let disposition: AttributeGroupDisposition = (record.fields()[1] as u32).into();
536
537            // Each attribute in the group can potentially span multiple fields
538            // in the record. Keep track of our field index to ensure that we
539            // fully consume the records into a list of attributes.
540            let mut fieldidx = 2;
541            let mut attributes = vec![];
542            while fieldidx < record.fields().len() {
543                let (count, attr) = Attribute::from_record(fieldidx, record)?;
544                attributes.push(attr);
545                fieldidx += count;
546            }
547
548            // Sanity check: we should have consumed every single record.
549            if fieldidx != record.fields().len() {
550                return Err(AttributeError::GroupSizeMismatch(
551                    fieldidx,
552                    record.fields().len(),
553                ));
554            }
555
556            groups.insert(
557                group_id,
558                AttributeGroup {
559                    disposition,
560                    attributes,
561                },
562            );
563        }
564
565        Ok(AttributeGroups(groups))
566    }
567}