File: rmake.rs

package info (click to toggle)
rustc 1.90.0%2Bdfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid
  • size: 925,928 kB
  • sloc: xml: 158,148; javascript: 19,781; sh: 19,174; python: 15,732; ansic: 13,096; cpp: 7,181; asm: 4,376; makefile: 697; lisp: 176; sql: 15
file content (149 lines) | stat: -rw-r--r-- 6,172 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//@ ignore-cross-compile
//@ ignore-wasm (`object` can't handle wasm object files)
//@ ignore-windows
// This test should be replaced with one in tests/debuginfo once GDB or LLDB support 128-bit enums.

use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;

use gimli::read::DebuggingInformationEntry;
use gimli::{AttributeValue, EndianRcSlice, Reader, RunTimeEndian};
use object::{Object, ObjectSection};
use run_make_support::{gimli, object, rfs, rustc};

fn main() {
    // Before LLVM 20, 128-bit enums with variants didn't emit debuginfo correctly.
    // This check can be removed once Rust no longer supports LLVM 18 and 19.
    let llvm_version = rustc()
        .verbose()
        .arg("--version")
        .run()
        .stdout_utf8()
        .lines()
        .filter_map(|line| line.strip_prefix("LLVM version: "))
        .map(|version| version.split(".").next().unwrap().parse::<u32>().unwrap())
        .next()
        .unwrap();
    let is_old_llvm = llvm_version < 20;

    let output = PathBuf::from("repr128");
    let mut rustc = rustc();
    if is_old_llvm {
        rustc.cfg("old_llvm");
    }
    rustc.input("main.rs").output(&output).arg("-Cdebuginfo=2").run();
    // Mach-O uses packed debug info
    let dsym_location = output
        .with_extension("dSYM")
        .join("Contents")
        .join("Resources")
        .join("DWARF")
        .join("repr128");
    let output =
        rfs::read(if dsym_location.try_exists().unwrap() { dsym_location } else { output });
    let obj = object::File::parse(output.as_slice()).unwrap();
    let endian = if obj.is_little_endian() { RunTimeEndian::Little } else { RunTimeEndian::Big };
    let dwarf = gimli::Dwarf::load(|section| -> Result<_, ()> {
        let data = obj.section_by_name(section.name()).map(|s| s.uncompressed_data().unwrap());
        Ok(EndianRcSlice::new(Rc::from(data.unwrap_or_default().as_ref()), endian))
    })
    .unwrap();
    let mut iter = dwarf.units();

    let mut enumerators_to_find = HashMap::from([
        ("U128A", 0_u128),
        ("U128B", 1_u128),
        ("U128C", u64::MAX as u128 + 1),
        ("U128D", u128::MAX),
        ("I128A", 0_i128 as u128),
        ("I128B", (-1_i128) as u128),
        ("I128C", i128::MIN as u128),
        ("I128D", i128::MAX as u128),
    ]);
    let mut variants_to_find = HashMap::from([
        ("VariantU128A", 0_u128),
        ("VariantU128B", 1_u128),
        ("VariantU128C", u64::MAX as u128 + 1),
        ("VariantU128D", u128::MAX),
        ("VariantI128A", 0_i128 as u128),
        ("VariantI128B", (-1_i128) as u128),
        ("VariantI128C", i128::MIN as u128),
        ("VariantI128D", i128::MAX as u128),
    ]);

    while let Some(header) = iter.next().unwrap() {
        let unit = dwarf.unit(header).unwrap();
        let mut cursor = unit.entries();

        let get_name = |entry: &DebuggingInformationEntry<'_, '_, _>| {
            let name = dwarf
                .attr_string(
                    &unit,
                    entry.attr(gimli::constants::DW_AT_name).unwrap().unwrap().value(),
                )
                .unwrap();
            name.to_string().unwrap().to_string()
        };

        while let Some((_, entry)) = cursor.next_dfs().unwrap() {
            match entry.tag() {
                gimli::constants::DW_TAG_variant if !is_old_llvm => {
                    let Some(value) = entry.attr(gimli::constants::DW_AT_discr_value).unwrap()
                    else {
                        // `std` enums might have variants without `DW_AT_discr_value`.
                        continue;
                    };
                    let value = match value.value() {
                        AttributeValue::Block(value) => value.to_slice().unwrap().to_vec(),
                        // `std` has non-repr128 enums which don't use `AttributeValue::Block`.
                        value => continue,
                    };
                    // The `DW_TAG_member` that is a child of `DW_TAG_variant` will contain the
                    // variant's name.
                    let Some((1, child_entry)) = cursor.next_dfs().unwrap() else {
                        panic!("Missing child of DW_TAG_variant");
                    };
                    assert_eq!(child_entry.tag(), gimli::constants::DW_TAG_member);
                    let name = get_name(child_entry);
                    if let Some(expected) = variants_to_find.remove(name.as_str()) {
                        // This test uses LE byte order is used for consistent values across
                        // architectures.
                        assert_eq!(value.as_slice(), expected.to_le_bytes().as_slice(), "{name}");
                    }
                }

                gimli::constants::DW_TAG_enumerator => {
                    let name = get_name(entry);
                    if let Some(expected) = enumerators_to_find.remove(name.as_str()) {
                        match entry
                            .attr(gimli::constants::DW_AT_const_value)
                            .unwrap()
                            .unwrap()
                            .value()
                        {
                            AttributeValue::Block(value) => {
                                // This test uses LE byte order is used for consistent values across
                                // architectures.
                                assert_eq!(
                                    value.to_slice().unwrap(),
                                    expected.to_le_bytes().as_slice(),
                                    "{name}"
                                );
                            }
                            value => panic!("{name}: unexpected DW_AT_const_value of {value:?}"),
                        }
                    }
                }

                _ => {}
            }
        }
    }
    if !enumerators_to_find.is_empty() {
        panic!("Didn't find debug enumerator entries for {enumerators_to_find:?}");
    }
    if !is_old_llvm && !variants_to_find.is_empty() {
        panic!("Didn't find debug variant entries for {variants_to_find:?}");
    }
}