* Migrate back to `std::` stylistically This commit moves away from idioms such as `alloc::` and `core::` as imports of standard data structures and types. Instead it migrates all crates to uniformly use `std::` for importing standard data structures and types. This also removes the `std` and `core` features from all crates to and removes any conditional checking for `feature = "std"` All of this support was previously added in #407 in an effort to make wasmtime/cranelift "`no_std` compatible". Unfortunately though this change comes at a cost: * The usage of `alloc` and `core` isn't idiomatic. Especially trying to dual between types like `HashMap` from `std` as well as from `hashbrown` causes imports to be surprising in some cases. * Unfortunately there was no CI check that crates were `no_std`, so none of them actually were. Many crates still imported from `std` or depended on crates that used `std`. It's important to note, however, that **this does not mean that wasmtime will not run in embedded environments**. The style of the code today and idioms aren't ready in Rust to support this degree of multiplexing and makes it somewhat difficult to keep up with the style of `wasmtime`. Instead it's intended that embedded runtime support will be added as necessary. Currently only `std` is necessary to build `wasmtime`, and platforms that natively need to execute `wasmtime` will need to use a Rust target that supports `std`. Note though that not all of `std` needs to be supported, but instead much of it could be configured off to return errors, and `wasmtime` would be configured to gracefully handle errors. The goal of this PR is to move `wasmtime` back to idiomatic usage of features/`std`/imports/etc and help development in the short-term. Long-term when platform concerns arise (if any) they can be addressed by moving back to `no_std` crates (but fixing the issues mentioned above) or ensuring that the target in Rust has `std` available. * Start filling out platform support doc
232 lines
8.0 KiB
Rust
232 lines
8.0 KiB
Rust
use crate::transform::AddressTransform;
|
|
use gimli::constants;
|
|
use gimli::read;
|
|
use gimli::{Reader, UnitSectionOffset};
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
#[derive(Debug)]
|
|
pub struct Dependencies {
|
|
edges: HashMap<UnitSectionOffset, HashSet<UnitSectionOffset>>,
|
|
roots: HashSet<UnitSectionOffset>,
|
|
}
|
|
|
|
impl Dependencies {
|
|
fn new() -> Dependencies {
|
|
Dependencies {
|
|
edges: HashMap::new(),
|
|
roots: HashSet::new(),
|
|
}
|
|
}
|
|
|
|
fn add_edge(&mut self, a: UnitSectionOffset, b: UnitSectionOffset) {
|
|
use std::collections::hash_map::Entry;
|
|
match self.edges.entry(a) {
|
|
Entry::Occupied(mut o) => {
|
|
o.get_mut().insert(b);
|
|
}
|
|
Entry::Vacant(v) => {
|
|
let mut set = HashSet::new();
|
|
set.insert(b);
|
|
v.insert(set);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn add_root(&mut self, root: UnitSectionOffset) {
|
|
self.roots.insert(root);
|
|
}
|
|
|
|
pub fn get_reachable(&self) -> HashSet<UnitSectionOffset> {
|
|
let mut reachable = self.roots.clone();
|
|
let mut queue = Vec::new();
|
|
for i in self.roots.iter() {
|
|
if let Some(deps) = self.edges.get(i) {
|
|
for j in deps {
|
|
if reachable.contains(j) {
|
|
continue;
|
|
}
|
|
reachable.insert(*j);
|
|
queue.push(*j);
|
|
}
|
|
}
|
|
}
|
|
while let Some(i) = queue.pop() {
|
|
if let Some(deps) = self.edges.get(&i) {
|
|
for j in deps {
|
|
if reachable.contains(j) {
|
|
continue;
|
|
}
|
|
reachable.insert(*j);
|
|
queue.push(*j);
|
|
}
|
|
}
|
|
}
|
|
reachable
|
|
}
|
|
}
|
|
|
|
pub fn build_dependencies<R: Reader<Offset = usize>>(
|
|
dwarf: &read::Dwarf<R>,
|
|
at: &AddressTransform,
|
|
) -> read::Result<Dependencies> {
|
|
let mut deps = Dependencies::new();
|
|
let mut units = dwarf.units();
|
|
while let Some(unit) = units.next()? {
|
|
build_unit_dependencies(unit, dwarf, at, &mut deps)?;
|
|
}
|
|
Ok(deps)
|
|
}
|
|
|
|
fn build_unit_dependencies<R: Reader<Offset = usize>>(
|
|
header: read::CompilationUnitHeader<R>,
|
|
dwarf: &read::Dwarf<R>,
|
|
at: &AddressTransform,
|
|
deps: &mut Dependencies,
|
|
) -> read::Result<()> {
|
|
let unit = dwarf.unit(header)?;
|
|
let mut tree = unit.entries_tree(None)?;
|
|
let root = tree.root()?;
|
|
build_die_dependencies(root, dwarf, &unit, at, deps)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn has_die_back_edge<R: Reader<Offset = usize>>(die: &read::DebuggingInformationEntry<R>) -> bool {
|
|
match die.tag() {
|
|
constants::DW_TAG_variable
|
|
| constants::DW_TAG_constant
|
|
| constants::DW_TAG_inlined_subroutine
|
|
| constants::DW_TAG_lexical_block
|
|
| constants::DW_TAG_label
|
|
| constants::DW_TAG_with_stmt
|
|
| constants::DW_TAG_try_block
|
|
| constants::DW_TAG_catch_block
|
|
| constants::DW_TAG_template_type_parameter
|
|
| constants::DW_TAG_member
|
|
| constants::DW_TAG_formal_parameter => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
fn has_valid_code_range<R: Reader<Offset = usize>>(
|
|
die: &read::DebuggingInformationEntry<R>,
|
|
dwarf: &read::Dwarf<R>,
|
|
unit: &read::Unit<R>,
|
|
at: &AddressTransform,
|
|
) -> read::Result<bool> {
|
|
match die.tag() {
|
|
constants::DW_TAG_subprogram => {
|
|
if let Some(ranges_attr) = die.attr_value(constants::DW_AT_ranges)? {
|
|
let offset = match ranges_attr {
|
|
read::AttributeValue::RangeListsRef(val) => val,
|
|
read::AttributeValue::DebugRngListsIndex(index) => {
|
|
dwarf.ranges_offset(unit, index)?
|
|
}
|
|
_ => return Ok(false),
|
|
};
|
|
let mut has_valid_base = if let Some(read::AttributeValue::Addr(low_pc)) =
|
|
die.attr_value(constants::DW_AT_low_pc)?
|
|
{
|
|
Some(at.can_translate_address(low_pc))
|
|
} else {
|
|
None
|
|
};
|
|
let mut it = dwarf.ranges.raw_ranges(offset, unit.encoding())?;
|
|
while let Some(range) = it.next()? {
|
|
// If at least one of the range addresses can be converted,
|
|
// declaring code range as valid.
|
|
match range {
|
|
read::RawRngListEntry::AddressOrOffsetPair { .. }
|
|
if has_valid_base.is_some() =>
|
|
{
|
|
if has_valid_base.unwrap() {
|
|
return Ok(true);
|
|
}
|
|
}
|
|
read::RawRngListEntry::StartEnd { begin, .. }
|
|
| read::RawRngListEntry::StartLength { begin, .. }
|
|
| read::RawRngListEntry::AddressOrOffsetPair { begin, .. } => {
|
|
if at.can_translate_address(begin) {
|
|
return Ok(true);
|
|
}
|
|
}
|
|
read::RawRngListEntry::StartxEndx { begin, .. }
|
|
| read::RawRngListEntry::StartxLength { begin, .. } => {
|
|
let addr = dwarf.address(unit, begin)?;
|
|
if at.can_translate_address(addr) {
|
|
return Ok(true);
|
|
}
|
|
}
|
|
read::RawRngListEntry::BaseAddress { addr } => {
|
|
has_valid_base = Some(at.can_translate_address(addr));
|
|
}
|
|
read::RawRngListEntry::BaseAddressx { addr } => {
|
|
let addr = dwarf.address(unit, addr)?;
|
|
has_valid_base = Some(at.can_translate_address(addr));
|
|
}
|
|
read::RawRngListEntry::OffsetPair { .. } => (),
|
|
}
|
|
}
|
|
return Ok(false);
|
|
} else if let Some(low_pc) = die.attr_value(constants::DW_AT_low_pc)? {
|
|
if let read::AttributeValue::Addr(a) = low_pc {
|
|
return Ok(at.can_translate_address(a));
|
|
}
|
|
}
|
|
}
|
|
_ => (),
|
|
}
|
|
Ok(false)
|
|
}
|
|
|
|
fn build_die_dependencies<R: Reader<Offset = usize>>(
|
|
die: read::EntriesTreeNode<R>,
|
|
dwarf: &read::Dwarf<R>,
|
|
unit: &read::Unit<R>,
|
|
at: &AddressTransform,
|
|
deps: &mut Dependencies,
|
|
) -> read::Result<()> {
|
|
let entry = die.entry();
|
|
let offset = entry.offset().to_unit_section_offset(unit);
|
|
let mut attrs = entry.attrs();
|
|
while let Some(attr) = attrs.next()? {
|
|
build_attr_dependencies(&attr, offset, dwarf, unit, at, deps)?;
|
|
}
|
|
|
|
let mut children = die.children();
|
|
while let Some(child) = children.next()? {
|
|
let child_entry = child.entry();
|
|
let child_offset = child_entry.offset().to_unit_section_offset(unit);
|
|
deps.add_edge(child_offset, offset);
|
|
if has_die_back_edge(child_entry) {
|
|
deps.add_edge(offset, child_offset);
|
|
}
|
|
if has_valid_code_range(child_entry, dwarf, unit, at)? {
|
|
deps.add_root(child_offset);
|
|
}
|
|
build_die_dependencies(child, dwarf, unit, at, deps)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn build_attr_dependencies<R: Reader<Offset = usize>>(
|
|
attr: &read::Attribute<R>,
|
|
offset: UnitSectionOffset,
|
|
_dwarf: &read::Dwarf<R>,
|
|
unit: &read::Unit<R>,
|
|
_at: &AddressTransform,
|
|
deps: &mut Dependencies,
|
|
) -> read::Result<()> {
|
|
match attr.value() {
|
|
read::AttributeValue::UnitRef(val) => {
|
|
let ref_offset = val.to_unit_section_offset(unit);
|
|
deps.add_edge(offset, ref_offset);
|
|
}
|
|
read::AttributeValue::DebugInfoRef(val) => {
|
|
let ref_offset = UnitSectionOffset::DebugInfoOffset(val);
|
|
deps.add_edge(offset, ref_offset);
|
|
}
|
|
_ => (),
|
|
}
|
|
Ok(())
|
|
}
|