dwarf dump: Emit address range for compile units and functions
This commit is contained in:
parent
d9612cc9b7
commit
458d0599f5
|
@ -15,8 +15,8 @@ use syntect::{
|
||||||
|
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
dwarf::{
|
dwarf::{
|
||||||
process_root_tag, read_debug_section, should_skip_tag, tag_type_string, AttributeKind,
|
process_cu_tag, process_root_tag, read_debug_section, should_skip_tag, tag_type_string,
|
||||||
TagKind,
|
AttributeKind, TagKind,
|
||||||
},
|
},
|
||||||
file::{buf_writer, map_file},
|
file::{buf_writer, map_file},
|
||||||
};
|
};
|
||||||
|
@ -163,28 +163,34 @@ where
|
||||||
let mut units = Vec::<String>::new();
|
let mut units = Vec::<String>::new();
|
||||||
if let Some((_, mut tag)) = info.tags.first_key_value() {
|
if let Some((_, mut tag)) = info.tags.first_key_value() {
|
||||||
loop {
|
loop {
|
||||||
match tag.kind {
|
let unit = process_root_tag(tag)?;
|
||||||
TagKind::CompileUnit => {
|
if units.contains(&unit.name) {
|
||||||
let unit = tag
|
// log::warn!("Duplicate unit '{}'", unit.name);
|
||||||
.string_attribute(AttributeKind::Name)
|
|
||||||
.ok_or_else(|| anyhow!("CompileUnit without name {:?}", tag))?;
|
|
||||||
if units.contains(unit) {
|
|
||||||
// log::warn!("Duplicate unit '{}'", unit);
|
|
||||||
} else {
|
} else {
|
||||||
units.push(unit.clone());
|
units.push(unit.name.clone());
|
||||||
}
|
}
|
||||||
writeln!(w, "\n// Compile unit: {}", unit)?;
|
writeln!(w, "\n/*\n Compile unit: {}", unit.name)?;
|
||||||
|
if let Some(producer) = unit.producer {
|
||||||
|
writeln!(w, " Producer: {}", producer)?;
|
||||||
|
}
|
||||||
|
if let Some(language) = unit.language {
|
||||||
|
writeln!(w, " Language: {}", language)?;
|
||||||
|
}
|
||||||
|
if let (Some(start), Some(end)) = (unit.start_address, unit.end_address) {
|
||||||
|
writeln!(w, " Code range: {:#010X} -> {:#010X}", start, end)?;
|
||||||
|
}
|
||||||
|
writeln!(w, "*/")?;
|
||||||
|
|
||||||
let children = tag.children(&info.tags);
|
let children = tag.children(&info.tags);
|
||||||
let mut typedefs = BTreeMap::<u32, Vec<u32>>::new();
|
let mut typedefs = BTreeMap::<u32, Vec<u32>>::new();
|
||||||
for child in children {
|
for child in children {
|
||||||
let tag_type = match process_root_tag(&info, child) {
|
let tag_type = match process_cu_tag(&info, child) {
|
||||||
Ok(tag_type) => tag_type,
|
Ok(tag_type) => tag_type,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Failed to process tag {} (unit {}): {}",
|
"Failed to process tag {} (unit {}): {}",
|
||||||
child.key,
|
child.key,
|
||||||
unit,
|
unit.name,
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
writeln!(
|
writeln!(
|
||||||
|
@ -201,12 +207,7 @@ where
|
||||||
match tag_type_string(&info, &typedefs, &tag_type) {
|
match tag_type_string(&info, &typedefs, &tag_type) {
|
||||||
Ok(s) => writeln!(w, "{}", s)?,
|
Ok(s) => writeln!(w, "{}", s)?,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!(
|
log::error!("Failed to emit tag {} (unit {}): {}", child.key, unit.name, e);
|
||||||
"Failed to emit tag {} (unit {}): {}",
|
|
||||||
child.key,
|
|
||||||
unit,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
writeln!(
|
writeln!(
|
||||||
w,
|
w,
|
||||||
"// ERROR: Failed to emit tag {} ({:?})",
|
"// ERROR: Failed to emit tag {} ({:?})",
|
||||||
|
@ -218,8 +219,7 @@ where
|
||||||
|
|
||||||
if let TagKind::Typedef = child.kind {
|
if let TagKind::Typedef = child.kind {
|
||||||
// TODO fundamental typedefs?
|
// TODO fundamental typedefs?
|
||||||
if let Some(ud_type_ref) =
|
if let Some(ud_type_ref) = child.reference_attribute(AttributeKind::UserDefType)
|
||||||
child.reference_attribute(AttributeKind::UserDefType)
|
|
||||||
{
|
{
|
||||||
match typedefs.entry(ud_type_ref) {
|
match typedefs.entry(ud_type_ref) {
|
||||||
btree_map::Entry::Vacant(e) => {
|
btree_map::Entry::Vacant(e) => {
|
||||||
|
@ -232,12 +232,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
log::warn!("Expected CompileUnit, got {:?}", tag.kind);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(next) = tag.next_sibling(&info.tags) {
|
if let Some(next) = tag.next_sibling(&info.tags) {
|
||||||
tag = next;
|
tag = next;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -716,6 +716,8 @@ pub struct SubroutineType {
|
||||||
pub labels: Vec<SubroutineLabel>,
|
pub labels: Vec<SubroutineLabel>,
|
||||||
pub blocks: Vec<SubroutineBlock>,
|
pub blocks: Vec<SubroutineBlock>,
|
||||||
pub inlines: Vec<SubroutineInline>,
|
pub inlines: Vec<SubroutineInline>,
|
||||||
|
pub start_address: Option<u32>,
|
||||||
|
pub end_address: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -756,6 +758,47 @@ pub enum TagType {
|
||||||
UserDefined(UserDefinedType),
|
UserDefined(UserDefinedType),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Language {
|
||||||
|
C89 = 0x1,
|
||||||
|
C = 0x2,
|
||||||
|
Ada83 = 0x3,
|
||||||
|
CPlusPlus = 0x4,
|
||||||
|
Cobol74 = 0x5,
|
||||||
|
Cobol85 = 0x6,
|
||||||
|
Fortran77 = 0x7,
|
||||||
|
Fortran90 = 0x8,
|
||||||
|
Pascal83 = 0x9,
|
||||||
|
Modula2 = 0xa,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Language {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Language::C89 => write!(f, "C89"),
|
||||||
|
Language::C => write!(f, "C"),
|
||||||
|
Language::Ada83 => write!(f, "Ada83"),
|
||||||
|
Language::CPlusPlus => write!(f, "C++"),
|
||||||
|
Language::Cobol74 => write!(f, "Cobol74"),
|
||||||
|
Language::Cobol85 => write!(f, "Cobol85"),
|
||||||
|
Language::Fortran77 => write!(f, "Fortran77"),
|
||||||
|
Language::Fortran90 => write!(f, "Fortran90"),
|
||||||
|
Language::Pascal83 => write!(f, "Pascal83"),
|
||||||
|
Language::Modula2 => write!(f, "Modula2"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CompileUnit {
|
||||||
|
pub name: String,
|
||||||
|
pub producer: Option<String>,
|
||||||
|
pub language: Option<Language>,
|
||||||
|
pub start_address: Option<u32>,
|
||||||
|
pub end_address: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
impl UserDefinedType {
|
impl UserDefinedType {
|
||||||
pub fn name(&self) -> Option<String> {
|
pub fn name(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -1171,8 +1214,14 @@ pub fn subroutine_def_string(
|
||||||
typedefs: &TypedefMap,
|
typedefs: &TypedefMap,
|
||||||
t: &SubroutineType,
|
t: &SubroutineType,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
|
let mut out = String::new();
|
||||||
|
if let (Some(start), Some(end)) = (t.start_address, t.end_address) {
|
||||||
|
writeln!(out, "// Range: {:#X} -> {:#X}", start, end)?;
|
||||||
|
}
|
||||||
let rt = type_string(info, typedefs, &t.return_type, true)?;
|
let rt = type_string(info, typedefs, &t.return_type, true)?;
|
||||||
let mut out = if t.local { "static ".to_string() } else { String::new() };
|
if t.local {
|
||||||
|
out.push_str("static ");
|
||||||
|
}
|
||||||
if t.inline {
|
if t.inline {
|
||||||
out.push_str("inline ");
|
out.push_str("inline ");
|
||||||
}
|
}
|
||||||
|
@ -1852,6 +1901,8 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
|
||||||
let mut references = Vec::new();
|
let mut references = Vec::new();
|
||||||
let mut member_of = None;
|
let mut member_of = None;
|
||||||
let mut inline = false;
|
let mut inline = false;
|
||||||
|
let mut start_address = None;
|
||||||
|
let mut end_address = None;
|
||||||
for attr in &tag.attributes {
|
for attr in &tag.attributes {
|
||||||
match (attr.kind, &attr.value) {
|
match (attr.kind, &attr.value) {
|
||||||
(AttributeKind::Sibling, _) => {}
|
(AttributeKind::Sibling, _) => {}
|
||||||
|
@ -1865,8 +1916,11 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
|
||||||
_,
|
_,
|
||||||
) => return_type = Some(process_type(attr, info.e)?),
|
) => return_type = Some(process_type(attr, info.e)?),
|
||||||
(AttributeKind::Prototyped, _) => prototyped = true,
|
(AttributeKind::Prototyped, _) => prototyped = true,
|
||||||
(AttributeKind::LowPc, _) | (AttributeKind::HighPc, _) => {
|
(AttributeKind::LowPc, &AttributeValue::Address(addr)) => {
|
||||||
// TODO?
|
start_address = Some(addr);
|
||||||
|
}
|
||||||
|
(AttributeKind::HighPc, &AttributeValue::Address(addr)) => {
|
||||||
|
end_address = Some(addr);
|
||||||
}
|
}
|
||||||
(AttributeKind::MwGlobalRef, &AttributeValue::Reference(key)) => {
|
(AttributeKind::MwGlobalRef, &AttributeValue::Reference(key)) => {
|
||||||
references.push(key);
|
references.push(key);
|
||||||
|
@ -1988,6 +2042,8 @@ fn process_subroutine_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineType>
|
||||||
labels,
|
labels,
|
||||||
blocks,
|
blocks,
|
||||||
inlines,
|
inlines,
|
||||||
|
start_address,
|
||||||
|
end_address,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2287,7 +2343,38 @@ pub fn process_type(attr: &Attribute, e: Endian) -> Result<Type> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_root_tag(info: &DwarfInfo, tag: &Tag) -> Result<TagType> {
|
pub fn process_root_tag(tag: &Tag) -> Result<CompileUnit> {
|
||||||
|
ensure!(tag.kind == TagKind::CompileUnit, "{:?} is not a CompileUnit tag", tag.kind);
|
||||||
|
|
||||||
|
let mut name = None;
|
||||||
|
let mut producer = None;
|
||||||
|
let mut language = None;
|
||||||
|
let mut start_address = None;
|
||||||
|
let mut end_address = None;
|
||||||
|
for attr in &tag.attributes {
|
||||||
|
match (attr.kind, &attr.value) {
|
||||||
|
(AttributeKind::Sibling, _) => {}
|
||||||
|
(AttributeKind::Name, AttributeValue::String(s)) => name = Some(s.clone()),
|
||||||
|
(AttributeKind::Producer, AttributeValue::String(s)) => producer = Some(s.clone()),
|
||||||
|
(AttributeKind::Language, &AttributeValue::Data4(value)) => {
|
||||||
|
language = Some(Language::try_from_primitive(value)?)
|
||||||
|
}
|
||||||
|
(AttributeKind::LowPc, &AttributeValue::Address(addr)) => start_address = Some(addr),
|
||||||
|
(AttributeKind::HighPc, &AttributeValue::Address(addr)) => end_address = Some(addr),
|
||||||
|
(AttributeKind::StmtList, AttributeValue::Data4(_)) => {
|
||||||
|
// TODO .line support
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
bail!("Unhandled CompileUnit attribute {:?}", attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = name.ok_or_else(|| anyhow!("CompileUnit without Name: {:?}", tag))?;
|
||||||
|
Ok(CompileUnit { name, producer, language, start_address, end_address })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_cu_tag(info: &DwarfInfo, tag: &Tag) -> Result<TagType> {
|
||||||
match tag.kind {
|
match tag.kind {
|
||||||
TagKind::Typedef => Ok(TagType::Typedef(process_typedef_tag(info, tag)?)),
|
TagKind::Typedef => Ok(TagType::Typedef(process_typedef_tag(info, tag)?)),
|
||||||
TagKind::GlobalVariable | TagKind::LocalVariable => {
|
TagKind::GlobalVariable | TagKind::LocalVariable => {
|
||||||
|
@ -2352,14 +2439,13 @@ fn variable_string(
|
||||||
out.push(' ');
|
out.push(' ');
|
||||||
out.push_str(variable.name.as_deref().unwrap_or("[unknown]"));
|
out.push_str(variable.name.as_deref().unwrap_or("[unknown]"));
|
||||||
out.push_str(&ts.suffix);
|
out.push_str(&ts.suffix);
|
||||||
match &variable.address {
|
|
||||||
Some(addr) => out.push_str(&format!(" : {:#010X}", addr)),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
out.push(';');
|
out.push(';');
|
||||||
if include_extra {
|
if include_extra {
|
||||||
let size = variable.kind.size(info)?;
|
let size = variable.kind.size(info)?;
|
||||||
out.push_str(&format!(" // size: {:#X}", size));
|
out.push_str(&format!(" // size: {:#X}", size));
|
||||||
|
if let Some(addr) = variable.address {
|
||||||
|
out.push_str(&format!(", address: {:#X}", addr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue