Merge pull request #2854 from uweigand/debug-endian
debug: Support big-endian architectures
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
#![allow(clippy::cast_ptr_alignment)]
|
#![allow(clippy::cast_ptr_alignment)]
|
||||||
|
|
||||||
use anyhow::{bail, ensure, Error};
|
use anyhow::{bail, ensure, Error};
|
||||||
|
use object::endian::{BigEndian, Endian, Endianness, LittleEndian};
|
||||||
use object::{RelocationEncoding, RelocationKind};
|
use object::{RelocationEncoding, RelocationKind};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@@ -18,13 +19,20 @@ pub fn create_gdbjit_image(
|
|||||||
defined_funcs_offset: usize,
|
defined_funcs_offset: usize,
|
||||||
funcs: &[*const u8],
|
funcs: &[*const u8],
|
||||||
) -> Result<Vec<u8>, Error> {
|
) -> Result<Vec<u8>, Error> {
|
||||||
ensure_supported_elf_format(&mut bytes)?;
|
let e = ensure_supported_elf_format(&mut bytes)?;
|
||||||
|
|
||||||
// patch relocs
|
// patch relocs
|
||||||
relocate_dwarf_sections(&mut bytes, defined_funcs_offset, funcs)?;
|
relocate_dwarf_sections(&mut bytes, defined_funcs_offset, funcs)?;
|
||||||
|
|
||||||
// elf is still missing details...
|
// elf is still missing details...
|
||||||
convert_object_elf_to_loadable_file(&mut bytes, code_region);
|
match e {
|
||||||
|
Endianness::Little => {
|
||||||
|
convert_object_elf_to_loadable_file::<LittleEndian>(&mut bytes, code_region)
|
||||||
|
}
|
||||||
|
Endianness::Big => {
|
||||||
|
convert_object_elf_to_loadable_file::<BigEndian>(&mut bytes, code_region)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// let mut file = ::std::fs::File::create(::std::path::Path::new("test.o")).expect("file");
|
// let mut file = ::std::fs::File::create(::std::path::Path::new("test.o")).expect("file");
|
||||||
// ::std::io::Write::write_all(&mut file, &bytes).expect("write");
|
// ::std::io::Write::write_all(&mut file, &bytes).expect("write");
|
||||||
@@ -83,18 +91,33 @@ fn relocate_dwarf_sections(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_supported_elf_format(bytes: &mut Vec<u8>) -> Result<(), Error> {
|
fn ensure_supported_elf_format(bytes: &mut Vec<u8>) -> Result<Endianness, Error> {
|
||||||
use object::elf::*;
|
use object::elf::*;
|
||||||
use object::endian::LittleEndian;
|
use object::read::elf::*;
|
||||||
|
use object::Bytes;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
let e = LittleEndian;
|
let kind = match object::FileKind::parse(bytes) {
|
||||||
let header: &FileHeader64<LittleEndian> =
|
Ok(file) => file,
|
||||||
unsafe { &*(bytes.as_mut_ptr() as *const FileHeader64<_>) };
|
Err(err) => {
|
||||||
ensure!(
|
bail!("Failed to parse file: {}", err);
|
||||||
header.e_ident.class == ELFCLASS64 && header.e_ident.data == ELFDATA2LSB,
|
}
|
||||||
"bits and endianess in .ELF",
|
};
|
||||||
);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
bail!("only 64-bit ELF files currently supported")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let e = header.endian().unwrap();
|
||||||
|
|
||||||
match header.e_machine.get(e) {
|
match header.e_machine.get(e) {
|
||||||
EM_X86_64 => (),
|
EM_X86_64 => (),
|
||||||
machine => {
|
machine => {
|
||||||
@@ -106,23 +129,25 @@ fn ensure_supported_elf_format(bytes: &mut Vec<u8>) -> Result<(), Error> {
|
|||||||
"program header table is empty"
|
"program header table is empty"
|
||||||
);
|
);
|
||||||
let e_shentsize = header.e_shentsize.get(e);
|
let e_shentsize = header.e_shentsize.get(e);
|
||||||
ensure!(
|
let req_shentsize = match e {
|
||||||
e_shentsize as usize == size_of::<SectionHeader64<LittleEndian>>(),
|
Endianness::Little => size_of::<SectionHeader64<LittleEndian>>(),
|
||||||
"size of sh"
|
Endianness::Big => size_of::<SectionHeader64<BigEndian>>(),
|
||||||
);
|
};
|
||||||
Ok(())
|
ensure!(e_shentsize as usize == req_shentsize, "size of sh");
|
||||||
|
Ok(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_object_elf_to_loadable_file(bytes: &mut Vec<u8>, code_region: (*const u8, usize)) {
|
fn convert_object_elf_to_loadable_file<E: Endian>(
|
||||||
|
bytes: &mut Vec<u8>,
|
||||||
|
code_region: (*const u8, usize),
|
||||||
|
) {
|
||||||
use object::elf::*;
|
use object::elf::*;
|
||||||
use object::endian::LittleEndian;
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
let e = LittleEndian;
|
let e = E::default();
|
||||||
let header: &FileHeader64<LittleEndian> =
|
let header: &FileHeader64<E> = unsafe { &*(bytes.as_mut_ptr() as *const FileHeader64<_>) };
|
||||||
unsafe { &*(bytes.as_mut_ptr() as *const FileHeader64<_>) };
|
|
||||||
|
|
||||||
let e_shentsize = header.e_shentsize.get(e);
|
let e_shentsize = header.e_shentsize.get(e);
|
||||||
let e_shoff = header.e_shoff.get(e);
|
let e_shoff = header.e_shoff.get(e);
|
||||||
@@ -130,7 +155,7 @@ fn convert_object_elf_to_loadable_file(bytes: &mut Vec<u8>, code_region: (*const
|
|||||||
let mut shstrtab_off = 0;
|
let mut shstrtab_off = 0;
|
||||||
for i in 0..e_shnum {
|
for i in 0..e_shnum {
|
||||||
let off = e_shoff as isize + i as isize * e_shentsize as isize;
|
let off = e_shoff as isize + i as isize * e_shentsize as isize;
|
||||||
let section: &SectionHeader64<LittleEndian> =
|
let section: &SectionHeader64<E> =
|
||||||
unsafe { &*(bytes.as_ptr().offset(off) as *const SectionHeader64<_>) };
|
unsafe { &*(bytes.as_ptr().offset(off) as *const SectionHeader64<_>) };
|
||||||
if section.sh_type.get(e) != SHT_STRTAB {
|
if section.sh_type.get(e) != SHT_STRTAB {
|
||||||
continue;
|
continue;
|
||||||
@@ -140,7 +165,7 @@ fn convert_object_elf_to_loadable_file(bytes: &mut Vec<u8>, code_region: (*const
|
|||||||
let mut segment: Option<_> = None;
|
let mut segment: Option<_> = None;
|
||||||
for i in 0..e_shnum {
|
for i in 0..e_shnum {
|
||||||
let off = e_shoff as isize + i as isize * e_shentsize as isize;
|
let off = e_shoff as isize + i as isize * e_shentsize as isize;
|
||||||
let section: &mut SectionHeader64<LittleEndian> =
|
let section: &mut SectionHeader64<E> =
|
||||||
unsafe { &mut *(bytes.as_mut_ptr().offset(off) as *mut SectionHeader64<_>) };
|
unsafe { &mut *(bytes.as_mut_ptr().offset(off) as *mut SectionHeader64<_>) };
|
||||||
if section.sh_type.get(e) != SHT_PROGBITS {
|
if section.sh_type.get(e) != SHT_PROGBITS {
|
||||||
continue;
|
continue;
|
||||||
@@ -171,12 +196,12 @@ fn convert_object_elf_to_loadable_file(bytes: &mut Vec<u8>, code_region: (*const
|
|||||||
|
|
||||||
// LLDB wants segment with virtual address set, placing them at the end of ELF.
|
// LLDB wants segment with virtual address set, placing them at the end of ELF.
|
||||||
let ph_off = bytes.len();
|
let ph_off = bytes.len();
|
||||||
let e_phentsize = size_of::<ProgramHeader64<LittleEndian>>();
|
let e_phentsize = size_of::<ProgramHeader64<E>>();
|
||||||
let e_phnum = 1;
|
let e_phnum = 1;
|
||||||
bytes.resize(ph_off + e_phentsize * e_phnum, 0);
|
bytes.resize(ph_off + e_phentsize * e_phnum, 0);
|
||||||
if let Some((sh_offset, sh_size)) = segment {
|
if let Some((sh_offset, sh_size)) = segment {
|
||||||
let (v_offset, size) = code_region;
|
let (v_offset, size) = code_region;
|
||||||
let program: &mut ProgramHeader64<LittleEndian> =
|
let program: &mut ProgramHeader64<E> =
|
||||||
unsafe { &mut *(bytes.as_ptr().add(ph_off) as *mut ProgramHeader64<_>) };
|
unsafe { &mut *(bytes.as_ptr().add(ph_off) as *mut ProgramHeader64<_>) };
|
||||||
program.p_type.set(e, PT_LOAD);
|
program.p_type.set(e, PT_LOAD);
|
||||||
program.p_offset.set(e, sh_offset);
|
program.p_offset.set(e, sh_offset);
|
||||||
@@ -189,7 +214,7 @@ fn convert_object_elf_to_loadable_file(bytes: &mut Vec<u8>, code_region: (*const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// It is somewhat loadable ELF file at this moment.
|
// It is somewhat loadable ELF file at this moment.
|
||||||
let header: &mut FileHeader64<LittleEndian> =
|
let header: &mut FileHeader64<E> =
|
||||||
unsafe { &mut *(bytes.as_mut_ptr() as *mut FileHeader64<_>) };
|
unsafe { &mut *(bytes.as_mut_ptr() as *mut FileHeader64<_>) };
|
||||||
header.e_type.set(e, ET_DYN);
|
header.e_type.set(e, ET_DYN);
|
||||||
header.e_phoff.set(e, ph_off as u64);
|
header.e_phoff.set(e, ph_off as u64);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use anyhow::{Context, Error};
|
|||||||
use gimli::write;
|
use gimli::write;
|
||||||
use gimli::{AttributeValue, DebuggingInformationEntry, Unit};
|
use gimli::{AttributeValue, DebuggingInformationEntry, Unit};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use wasmtime_environ::ir::Endianness;
|
||||||
use wasmtime_environ::isa::TargetIsa;
|
use wasmtime_environ::isa::TargetIsa;
|
||||||
use wasmtime_environ::wasm::DefinedFuncIndex;
|
use wasmtime_environ::wasm::DefinedFuncIndex;
|
||||||
use wasmtime_environ::{CompiledFunctions, ModuleMemoryOffset};
|
use wasmtime_environ::{CompiledFunctions, ModuleMemoryOffset};
|
||||||
@@ -463,6 +464,19 @@ where
|
|||||||
isa,
|
isa,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Data in WebAssembly memory always uses little-endian byte order.
|
||||||
|
// If the native architecture is big-endian, we need to mark all
|
||||||
|
// base types used to refer to WebAssembly memory as little-endian
|
||||||
|
// using the DW_AT_endianity attribute, so that the debugger will
|
||||||
|
// be able to correctly access them.
|
||||||
|
if entry.tag() == gimli::DW_TAG_base_type && isa.endianness() == Endianness::Big {
|
||||||
|
let current_scope = comp_unit.get_mut(die_id);
|
||||||
|
current_scope.set(
|
||||||
|
gimli::DW_AT_endianity,
|
||||||
|
write::AttributeValue::Endianity(gimli::DW_END_little),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() {
|
if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() {
|
||||||
append_vmctx_info(
|
append_vmctx_info(
|
||||||
comp_unit,
|
comp_unit,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ pub use crate::transform::transform_dwarf;
|
|||||||
use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
|
use gimli::write::{Address, Dwarf, EndianVec, FrameTable, Result, Sections, Writer};
|
||||||
use gimli::{RunTimeEndian, SectionId};
|
use gimli::{RunTimeEndian, SectionId};
|
||||||
use wasmtime_environ::entity::EntityRef;
|
use wasmtime_environ::entity::EntityRef;
|
||||||
|
use wasmtime_environ::ir::Endianness;
|
||||||
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
use wasmtime_environ::isa::{unwind::UnwindInfo, TargetIsa};
|
||||||
use wasmtime_environ::{CompiledFunctions, DebugInfoData, ModuleMemoryOffset};
|
use wasmtime_environ::{CompiledFunctions, DebugInfoData, ModuleMemoryOffset};
|
||||||
|
|
||||||
@@ -26,10 +27,19 @@ pub struct DwarfSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn emit_dwarf_sections(
|
fn emit_dwarf_sections(
|
||||||
|
isa: &dyn TargetIsa,
|
||||||
mut dwarf: Dwarf,
|
mut dwarf: Dwarf,
|
||||||
frames: Option<FrameTable>,
|
frames: Option<FrameTable>,
|
||||||
) -> anyhow::Result<Vec<DwarfSection>> {
|
) -> anyhow::Result<Vec<DwarfSection>> {
|
||||||
let mut sections = Sections::new(WriterRelocate::default());
|
let endian = match isa.endianness() {
|
||||||
|
Endianness::Little => RunTimeEndian::Little,
|
||||||
|
Endianness::Big => RunTimeEndian::Big,
|
||||||
|
};
|
||||||
|
let writer = WriterRelocate {
|
||||||
|
relocs: Vec::new(),
|
||||||
|
writer: EndianVec::new(endian),
|
||||||
|
};
|
||||||
|
let mut sections = Sections::new(writer);
|
||||||
dwarf.write(&mut sections)?;
|
dwarf.write(&mut sections)?;
|
||||||
if let Some(frames) = frames {
|
if let Some(frames) = frames {
|
||||||
frames.write_debug_frame(&mut sections.debug_frame)?;
|
frames.write_debug_frame(&mut sections.debug_frame)?;
|
||||||
@@ -54,15 +64,6 @@ pub struct WriterRelocate {
|
|||||||
writer: EndianVec<RunTimeEndian>,
|
writer: EndianVec<RunTimeEndian>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WriterRelocate {
|
|
||||||
fn default() -> Self {
|
|
||||||
WriterRelocate {
|
|
||||||
relocs: Vec::new(),
|
|
||||||
writer: EndianVec::new(RunTimeEndian::Little),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writer for WriterRelocate {
|
impl Writer for WriterRelocate {
|
||||||
type Endian = RunTimeEndian;
|
type Endian = RunTimeEndian;
|
||||||
|
|
||||||
@@ -156,6 +157,6 @@ pub fn emit_dwarf<'a>(
|
|||||||
) -> anyhow::Result<Vec<DwarfSection>> {
|
) -> anyhow::Result<Vec<DwarfSection>> {
|
||||||
let dwarf = transform_dwarf(isa, debuginfo_data, funcs, memory_offset)?;
|
let dwarf = transform_dwarf(isa, debuginfo_data, funcs, memory_offset)?;
|
||||||
let frame_table = create_frame_table(isa, funcs);
|
let frame_table = create_frame_table(isa, funcs);
|
||||||
let sections = emit_dwarf_sections(dwarf, frame_table)?;
|
let sections = emit_dwarf_sections(isa, dwarf, frame_table)?;
|
||||||
Ok(sections)
|
Ok(sections)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
pub mod ir {
|
pub mod ir {
|
||||||
pub use cranelift_codegen::binemit::{Reloc, StackMap};
|
pub use cranelift_codegen::binemit::{Reloc, StackMap};
|
||||||
pub use cranelift_codegen::ir::{
|
pub use cranelift_codegen::ir::{
|
||||||
types, AbiParam, ArgumentPurpose, JumpTableOffsets, LabelValueLoc, LibCall, Signature,
|
types, AbiParam, ArgumentPurpose, Endianness, JumpTableOffsets, LabelValueLoc, LibCall,
|
||||||
SourceLoc, StackSlots, TrapCode, Type, ValueLabel, ValueLoc,
|
Signature, SourceLoc, StackSlots, TrapCode, Type, ValueLabel, ValueLoc,
|
||||||
};
|
};
|
||||||
pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange};
|
pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user