Upgrade to the latest versions of gimli, addr2line, object (#2901)

* Upgrade to the latest versions of gimli, addr2line, object

And adapt to API changes. New gimli supports wasm dwarf, resulting in
some simplifications in the debug crate.

* upgrade gimli usage in linux-specific profiling too

* Add "continue" statement after interpreting a wasm local dwarf opcode
This commit is contained in:
Benjamin Bouvier
2021-05-12 17:53:17 +02:00
committed by GitHub
parent 60f7b23ea1
commit d7053ea9c7
16 changed files with 195 additions and 190 deletions

View File

@@ -12,9 +12,9 @@ readme = "README.md"
edition = "2018"
[dependencies]
gimli = "0.23.0"
gimli = "0.24.0"
wasmparser = "0.77"
object = { version = "0.23.0", default-features = false, features = ["read_core", "elf", "write"] }
object = { version = "0.24.0", default-features = false, features = ["read_core", "elf", "write"] }
wasmtime-environ = { path = "../environ", version = "0.26.0" }
target-lexicon = { version = "0.12.0", default-features = false }
anyhow = "1.0"

View File

@@ -19,10 +19,10 @@ pub fn create_gdbjit_image(
defined_funcs_offset: usize,
funcs: &[*const u8],
) -> Result<Vec<u8>, Error> {
let e = ensure_supported_elf_format(&mut bytes)?;
let e = ensure_supported_elf_format(&bytes)?;
// patch relocs
relocate_dwarf_sections(&mut bytes, defined_funcs_offset, funcs)?;
relocate_dwarf_sections(&bytes, defined_funcs_offset, funcs)?;
// elf is still missing details...
match e {
@@ -41,7 +41,7 @@ pub fn create_gdbjit_image(
}
fn relocate_dwarf_sections(
bytes: &mut [u8],
bytes: &[u8],
defined_funcs_offset: usize,
funcs: &[*const u8],
) -> Result<(), Error> {
@@ -91,10 +91,9 @@ fn relocate_dwarf_sections(
Ok(())
}
fn ensure_supported_elf_format(bytes: &mut Vec<u8>) -> Result<Endianness, Error> {
fn ensure_supported_elf_format(bytes: &[u8]) -> Result<Endianness, Error> {
use object::elf::*;
use object::read::elf::*;
use object::Bytes;
use std::mem::size_of;
let kind = match object::FileKind::parse(bytes) {
@@ -104,14 +103,12 @@ fn ensure_supported_elf_format(bytes: &mut Vec<u8>) -> Result<Endianness, Error>
}
};
let header = match kind {
object::FileKind::Elf64 => {
match object::elf::FileHeader64::<Endianness>::parse(Bytes(bytes)) {
Ok(header) => header,
Err(err) => {
bail!("Unsupported ELF file: {}", err);
}
object::FileKind::Elf64 => match object::elf::FileHeader64::<Endianness>::parse(bytes) {
Ok(header) => header,
Err(err) => {
bail!("Unsupported ELF file: {}", err);
}
}
},
_ => {
bail!("only 64-bit ELF files currently supported")
}

View File

@@ -541,150 +541,145 @@ where
parts.push(CompiledExpressionPart::LandingPad(marker.clone()));
}
let next = buf[pc.offset_from(&expr.0).into_u64() as usize];
need_deref = true;
if next == 0xED {
// WebAssembly DWARF extension
pc.read_u8()?;
let ty = pc.read_uleb128()?;
// Supporting only wasm locals.
if ty != 0 {
// TODO support wasm globals?
let pos = pc.offset_from(&expr.0).into_u64() as usize;
let op = Operation::parse(&mut pc, encoding)?;
match op {
Operation::FrameOffset { offset } => {
// Expand DW_OP_fbreg into frame location and DW_OP_plus_uconst.
if frame_base.is_some() {
// Add frame base expressions.
flush_code_chunk!();
parts.extend_from_slice(&frame_base.unwrap().parts);
}
if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() {
// Reset local trailing flag.
*trailing = false;
}
// Append DW_OP_plus_uconst part.
let mut writer = ExpressionWriter::new();
writer.write_op(gimli::constants::DW_OP_plus_uconst)?;
writer.write_uleb128(offset as u64)?;
code_chunk.extend(writer.into_vec());
continue;
}
Operation::Drop { .. }
| Operation::Pick { .. }
| Operation::Swap { .. }
| Operation::Rot { .. }
| Operation::Nop { .. }
| Operation::UnsignedConstant { .. }
| Operation::SignedConstant { .. }
| Operation::ConstantIndex { .. }
| Operation::PlusConstant { .. }
| Operation::Abs { .. }
| Operation::And { .. }
| Operation::Or { .. }
| Operation::Xor { .. }
| Operation::Shl { .. }
| Operation::Plus { .. }
| Operation::Minus { .. }
| Operation::Div { .. }
| Operation::Mod { .. }
| Operation::Mul { .. }
| Operation::Neg { .. }
| Operation::Not { .. }
| Operation::Lt { .. }
| Operation::Gt { .. }
| Operation::Le { .. }
| Operation::Ge { .. }
| Operation::Eq { .. }
| Operation::Ne { .. }
| Operation::TypedLiteral { .. }
| Operation::Convert { .. }
| Operation::Reinterpret { .. }
| Operation::Piece { .. } => (),
Operation::Bra { target } | Operation::Skip { target } => {
flush_code_chunk!();
let arc_to = (pc.len().into_u64() as isize - target as isize) as u64;
let marker = match jump_targets.get(&arc_to) {
Some(m) => m.clone(),
None => {
// Marker not found: probably out of bounds.
return Ok(None);
}
};
push!(CompiledExpressionPart::Jump {
conditionally: match op {
Operation::Bra { .. } => true,
_ => false,
},
target: marker,
});
continue;
}
Operation::StackValue => {
need_deref = false;
// Find extra stack_value, that follow wasm-local operators,
// and mark such locals with special flag.
if let (Some(CompiledExpressionPart::Local { trailing, .. }), true) =
(parts.last_mut(), code_chunk.is_empty())
{
*trailing = true;
continue;
}
}
Operation::Deref { .. } => {
flush_code_chunk!();
push!(CompiledExpressionPart::Deref);
// Don't re-enter the loop here (i.e. continue), because the
// DW_OP_deref still needs to be kept.
}
Operation::WasmLocal { index } => {
flush_code_chunk!();
let label = ValueLabel::from_u32(index as u32);
push!(CompiledExpressionPart::Local {
label,
trailing: false,
});
continue;
}
Operation::Shr { .. } | Operation::Shra { .. } => {
// Insert value normalisation part.
// The semantic value is 32 bits (TODO: check unit)
// but the target architecture is 64-bits. So we'll
// clean out the upper 32 bits (in a sign-correct way)
// to avoid contamination of the result with randomness.
let mut writer = ExpressionWriter::new();
writer.write_op(gimli::constants::DW_OP_plus_uconst)?;
writer.write_uleb128(32)?; // increase shift amount
writer.write_op(gimli::constants::DW_OP_swap)?;
writer.write_op(gimli::constants::DW_OP_const1u)?;
writer.write_u8(32)?;
writer.write_op(gimli::constants::DW_OP_shl)?;
writer.write_op(gimli::constants::DW_OP_swap)?;
code_chunk.extend(writer.into_vec());
// Don't re-enter the loop here (i.e. continue), because the
// DW_OP_shr* still needs to be kept.
}
Operation::Address { .. }
| Operation::AddressIndex { .. }
| Operation::Call { .. }
| Operation::Register { .. }
| Operation::RegisterOffset { .. }
| Operation::CallFrameCFA
| Operation::PushObjectAddress
| Operation::TLS
| Operation::ImplicitValue { .. }
| Operation::ImplicitPointer { .. }
| Operation::EntryValue { .. }
| Operation::ParameterRef { .. } => {
return Ok(None);
}
let index = pc.read_sleb128()?;
flush_code_chunk!();
let label = ValueLabel::from_u32(index as u32);
push!(CompiledExpressionPart::Local {
label,
trailing: false,
});
} else {
let pos = pc.offset_from(&expr.0).into_u64() as usize;
let op = Operation::parse(&mut pc, encoding)?;
match op {
Operation::FrameOffset { offset } => {
// Expand DW_OP_fbreg into frame location and DW_OP_plus_uconst.
if frame_base.is_some() {
// Add frame base expressions.
flush_code_chunk!();
parts.extend_from_slice(&frame_base.unwrap().parts);
}
if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() {
// Reset local trailing flag.
*trailing = false;
}
// Append DW_OP_plus_uconst part.
let mut writer = ExpressionWriter::new();
writer.write_op(gimli::constants::DW_OP_plus_uconst)?;
writer.write_uleb128(offset as u64)?;
code_chunk.extend(writer.into_vec());
continue;
}
Operation::Drop { .. }
| Operation::Pick { .. }
| Operation::Swap { .. }
| Operation::Rot { .. }
| Operation::Nop { .. }
| Operation::UnsignedConstant { .. }
| Operation::SignedConstant { .. }
| Operation::ConstantIndex { .. }
| Operation::PlusConstant { .. }
| Operation::Abs { .. }
| Operation::And { .. }
| Operation::Or { .. }
| Operation::Xor { .. }
| Operation::Shl { .. }
| Operation::Plus { .. }
| Operation::Minus { .. }
| Operation::Div { .. }
| Operation::Mod { .. }
| Operation::Mul { .. }
| Operation::Neg { .. }
| Operation::Not { .. }
| Operation::Lt { .. }
| Operation::Gt { .. }
| Operation::Le { .. }
| Operation::Ge { .. }
| Operation::Eq { .. }
| Operation::Ne { .. }
| Operation::TypedLiteral { .. }
| Operation::Convert { .. }
| Operation::Reinterpret { .. }
| Operation::Piece { .. } => (),
Operation::Bra { target } | Operation::Skip { target } => {
flush_code_chunk!();
let arc_to = (pc.len().into_u64() as isize - target as isize) as u64;
let marker = match jump_targets.get(&arc_to) {
Some(m) => m.clone(),
None => {
// Marker not found: probably out of bounds.
return Ok(None);
}
};
push!(CompiledExpressionPart::Jump {
conditionally: match op {
Operation::Bra { .. } => true,
_ => false,
},
target: marker,
});
continue;
}
Operation::StackValue => {
need_deref = false;
// Find extra stack_value, that follow wasm-local operators,
// and mark such locals with special flag.
if let (Some(CompiledExpressionPart::Local { trailing, .. }), true) =
(parts.last_mut(), code_chunk.is_empty())
{
*trailing = true;
continue;
}
}
Operation::Deref { .. } => {
flush_code_chunk!();
push!(CompiledExpressionPart::Deref);
// Don't re-enter the loop here (i.e. continue), because the
// DW_OP_deref still needs to be kept.
}
Operation::Shr { .. } | Operation::Shra { .. } => {
// Insert value normalisation part.
// The semantic value is 32 bits (TODO: check unit)
// but the target architecture is 64-bits. So we'll
// clean out the upper 32 bits (in a sign-correct way)
// to avoid contamination of the result with randomness.
let mut writer = ExpressionWriter::new();
writer.write_op(gimli::constants::DW_OP_plus_uconst)?;
writer.write_uleb128(32)?; // increase shift amount
writer.write_op(gimli::constants::DW_OP_swap)?;
writer.write_op(gimli::constants::DW_OP_const1u)?;
writer.write_u8(32)?;
writer.write_op(gimli::constants::DW_OP_shl)?;
writer.write_op(gimli::constants::DW_OP_swap)?;
code_chunk.extend(writer.into_vec());
// Don't re-enter the loop here (i.e. continue), because the
// DW_OP_shr* still needs to be kept.
}
Operation::Address { .. }
| Operation::AddressIndex { .. }
| Operation::Call { .. }
| Operation::Register { .. }
| Operation::RegisterOffset { .. }
| Operation::CallFrameCFA
| Operation::PushObjectAddress
| Operation::TLS
| Operation::ImplicitValue { .. }
| Operation::ImplicitPointer { .. }
| Operation::EntryValue { .. }
| Operation::ParameterRef { .. } => {
return Ok(None);
}
Operation::WasmGlobal { index: _ } | Operation::WasmStack { index: _ } => {
// TODO support those two
return Ok(None);
}
let chunk = &buf[pos..pc.offset_from(&expr.0).into_u64() as usize];
code_chunk.extend_from_slice(chunk);
}
let chunk = &buf[pos..pc.offset_from(&expr.0).into_u64() as usize];
code_chunk.extend_from_slice(chunk);
}
flush_code_chunk!();

View File

@@ -189,10 +189,10 @@ where
address: row.address(),
op_index: row.op_index(),
file_index: row.file_index(),
line: row.line().unwrap_or(0),
line: row.line().map(|nonzero| nonzero.get()).unwrap_or(0),
column: match row.column() {
gimli::ColumnType::LeftEdge => 0,
gimli::ColumnType::Column(val) => val,
gimli::ColumnType::Column(val) => val.get(),
},
discriminator: row.discriminator(),
is_stmt: row.is_stmt(),

View File

@@ -22,7 +22,7 @@ serde = { version = "1.0.94", features = ["derive"] }
log = { version = "0.4.8", default-features = false }
more-asserts = "0.2.1"
cfg-if = "1.0"
gimli = "0.23"
gimli = "0.24"
[badges]
maintenance = { status = "actively-developed" }

View File

@@ -205,19 +205,28 @@ impl<'data> ModuleEnvironment<'data> {
let slice = gimli::EndianSlice::new(data, endian);
match name {
".debug_str" => dwarf.debug_str = gimli::DebugStr::new(data, endian),
// Dwarf fields.
".debug_abbrev" => dwarf.debug_abbrev = gimli::DebugAbbrev::new(data, endian),
".debug_addr" => dwarf.debug_addr = gimli::DebugAddr::from(slice),
// TODO aranges?
".debug_info" => dwarf.debug_info = gimli::DebugInfo::new(data, endian),
".debug_line" => dwarf.debug_line = gimli::DebugLine::new(data, endian),
".debug_addr" => dwarf.debug_addr = gimli::DebugAddr::from(slice),
".debug_line_str" => dwarf.debug_line_str = gimli::DebugLineStr::from(slice),
".debug_str_sup" => dwarf.debug_str_sup = gimli::DebugStr::from(slice),
".debug_ranges" => info.debug_ranges = gimli::DebugRanges::new(data, endian),
".debug_rnglists" => info.debug_rnglists = gimli::DebugRngLists::new(data, endian),
".debug_str" => dwarf.debug_str = gimli::DebugStr::new(data, endian),
".debug_str_offsets" => dwarf.debug_str_offsets = gimli::DebugStrOffsets::from(slice),
".debug_str_sup" => {
let mut dwarf_sup: Dwarf<'data> = Default::default();
dwarf_sup.debug_str = gimli::DebugStr::from(slice);
dwarf.sup = Some(Arc::new(dwarf_sup));
}
".debug_types" => dwarf.debug_types = gimli::DebugTypes::from(slice),
// Additional fields.
".debug_loc" => info.debug_loc = gimli::DebugLoc::from(slice),
".debug_loclists" => info.debug_loclists = gimli::DebugLocLists::from(slice),
".debug_str_offsets" => dwarf.debug_str_offsets = gimli::DebugStrOffsets::from(slice),
".debug_types" => dwarf.debug_types = gimli::DebugTypes::from(slice),
".debug_ranges" => info.debug_ranges = gimli::DebugRanges::new(data, endian),
".debug_rnglists" => info.debug_rnglists = gimli::DebugRngLists::new(data, endian),
other => {
log::warn!("unknown debug section `{}`", other);
return;

View File

@@ -33,10 +33,10 @@ more-asserts = "0.2.1"
anyhow = "1.0"
cfg-if = "1.0"
log = "0.4"
gimli = { version = "0.23.0", default-features = false, features = ["write"] }
object = { version = "0.23.0", default-features = false, features = ["write"] }
gimli = { version = "0.24.0", default-features = false, features = ["write"] }
object = { version = "0.24.0", default-features = false, features = ["write"] }
serde = { version = "1.0.94", features = ["derive"] }
addr2line = { version = "0.14", default-features = false }
addr2line = { version = "0.15", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.8", features = ["winnt", "impl-default"] }

View File

@@ -83,6 +83,7 @@ struct DebugInfo {
code_section_offset: u64,
debug_abbrev: Range<usize>,
debug_addr: Range<usize>,
debug_aranges: Range<usize>,
debug_info: Range<usize>,
debug_line: Range<usize>,
debug_line_str: Range<usize>,
@@ -400,6 +401,7 @@ impl CompiledModule {
let cx = addr2line::Context::from_sections(
EndianSlice::new(&data[info.debug_abbrev.clone()], endian).into(),
EndianSlice::new(&data[info.debug_addr.clone()], endian).into(),
EndianSlice::new(&data[info.debug_aranges.clone()], endian).into(),
EndianSlice::new(&data[info.debug_info.clone()], endian).into(),
EndianSlice::new(&data[info.debug_line.clone()], endian).into(),
EndianSlice::new(&data[info.debug_line_str.clone()], endian).into(),
@@ -546,6 +548,7 @@ impl From<DebugInfoData<'_>> for DebugInfo {
};
let debug_abbrev = push(raw.dwarf.debug_abbrev.reader().slice());
let debug_addr = push(raw.dwarf.debug_addr.reader().slice());
let debug_aranges = push(raw.dwarf.debug_aranges.reader().slice());
let debug_info = push(raw.dwarf.debug_info.reader().slice());
let debug_line = push(raw.dwarf.debug_line.reader().slice());
let debug_line_str = push(raw.dwarf.debug_line_str.reader().slice());
@@ -557,6 +560,7 @@ impl From<DebugInfoData<'_>> for DebugInfo {
data: data.into(),
debug_abbrev,
debug_addr,
debug_aranges,
debug_info,
debug_line,
debug_line_str,

View File

@@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
anyhow = "1.0"
wasmtime-environ = { path = "../environ", version = "0.26.0" }
object = { version = "0.23.0", default-features = false, features = ["write"] }
object = { version = "0.24.0", default-features = false, features = ["write"] }
more-asserts = "0.2.1"
target-lexicon = { version = "0.12.0", default-features = false }
wasmtime-debug = { path = "../debug", version = "0.26.0" }

View File

@@ -13,7 +13,7 @@ edition = "2018"
[dependencies]
anyhow = "1.0"
cfg-if = "1.0"
gimli = { version = "0.23.0", optional = true }
gimli = { version = "0.24.0", optional = true }
lazy_static = "1.4"
libc = { version = "0.2.60", default-features = false }
scroll = { version = "0.10.1", features = ["derive"], optional = true }
@@ -24,7 +24,7 @@ wasmtime-runtime = { path = "../runtime", version = "0.26.0" }
ittapi-rs = { version = "0.1.5", optional = true }
[dependencies.object]
version = "0.23.0"
version = "0.24.0"
optional = true
default-features = false
features = ['read_core', 'elf', 'std']

View File

@@ -371,7 +371,7 @@ impl State {
pid: u32,
tid: u32,
) -> Result<()> {
let file = object::File::parse(&dbg_image).unwrap();
let file = object::File::parse(dbg_image).unwrap();
let endian = if file.is_little_endian() {
gimli::RunTimeEndian::Little
} else {
@@ -386,8 +386,7 @@ impl State {
}
};
let load_section_sup = |_| Ok(borrow::Cow::Borrowed(&[][..]));
let dwarf_cow = gimli::Dwarf::load(&load_section, &load_section_sup)?;
let dwarf_cow = gimli::Dwarf::load(&load_section)?;
let borrow_section: &dyn for<'a> Fn(
&'a borrow::Cow<[u8]>,
)
@@ -599,9 +598,9 @@ impl State {
header: RecordHeader {
id: RecordId::JitCodeDebugInfo as u32,
record_size: 0,
timestamp: timestamp,
timestamp,
},
address: address,
address,
count: 0,
};
@@ -617,9 +616,9 @@ impl State {
)
.unwrap();
let filename = myfile.to_string_lossy()?;
let line = row.line().unwrap_or(0);
let line = row.line().map(|nonzero| nonzero.get()).unwrap_or(0);
let column = match row.column() {
gimli::ColumnType::Column(column) => column,
gimli::ColumnType::Column(column) => column.get(),
gimli::ColumnType::LeftEdge => 0,
};