Skip to content

Commit

Permalink
Load blockmap and LLVM bitcode sections from the ELF binary.
Browse files Browse the repository at this point in the history
This is to reflect a change in ykllvm. In short, it's not obviously
correct for us to add SHF_ALLOC to those sections.

For more details see:
ykjit#923
  • Loading branch information
vext01 authored and Iti Shree committed Jan 23, 2024
1 parent d7353a4 commit 5f17d55
Show file tree
Hide file tree
Showing 12 changed files with 56 additions and 115 deletions.
5 changes: 0 additions & 5 deletions bin/yk-config
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,6 @@ handle_arg() {

OUTPUT="${OUTPUT} -Xlinker --lto-newpm-passes=${POSTLINK_PASSES_STR}"

# Have the `.llvmbc` and `.llvm_bb_addr_map` sections loaded into
# memory by the loader.
OUTPUT="${OUTPUT} -Wl,--mllvm=--yk-alloc-llvmbc-section"
OUTPUT="${OUTPUT} -Wl,--mllvm=--yk-alloc-llvmbbaddrmap-section"

# Emit a basic block map section. Used for block mapping.
OUTPUT="${OUTPUT} -Wl,--lto-basic-block-sections=labels"

Expand Down
6 changes: 6 additions & 0 deletions hwtracer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ intervaltree = "0.2.7"
byteorder = "1.4.3"
leb128 = "0.2.5"
thiserror = "1"
memmap2 = "0.7"

[dependencies.object]
version = "0.32"
default-features = false
features = ["read_core", "elf"]

[target.'cfg(target_arch = "x86_64")'.dependencies]
iced-x86 = { version = "1.18.0", features = ["decoder"]}
Expand Down
53 changes: 10 additions & 43 deletions hwtracer/src/llvm_blockmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
use byteorder::{NativeEndian, ReadBytesExt};
use intervaltree::IntervalTree;
use libc::{dlsym, RTLD_DEFAULT};
use object::{self, Object, ObjectSection};
use std::{
convert::TryFrom,
ffi::CString,
io::{prelude::*, Cursor, SeekFrom},
slice,
sync::LazyLock,
};
use ykaddr::obj::SELF_BIN_MMAP;

pub static LLVM_BLOCK_MAP: LazyLock<BlockMap> = LazyLock::new(BlockMap::new);
pub static LLVM_BLOCK_MAP: LazyLock<BlockMap> = LazyLock::new(|| {
let object = object::File::parse(&**SELF_BIN_MMAP).unwrap();
let sec = object.section_by_name(".llvm_bb_addr_map").unwrap();
BlockMap::new(sec.data().unwrap())
});

/// Describes the successors (if any) of an LLVM `MachineBlock`.
#[derive(Debug)]
Expand Down Expand Up @@ -90,39 +92,6 @@ impl BlockMapEntry {
}
}

// ykllvm inserts a symbol pair marking the extent of the `.llvm_bb_addr_map` section.
// This function returns a byte slice of the memory between these two marker symbols.
//
// Note that in the "common use case" this lookup could be done statically (without `dlsym`) using:
//
// ```
// extern "C" {
// #[link(name = "ykllvm.bbaddrmaps.start")]
// BBMAPS_START_BYTE: u8;
// #[link(name = "ykllvm.bbaddrmaps.stop")]
// BBMAPS_STOP_BYTE: u8;
// }
// ```
//
// however, this would force every binary that uses this crate to provide the symbols. This is not
// desirable, e.g. Rust test binaries.
fn find_blockmap_section() -> &'static [u8] {
let start_sym = CString::new("ykllvm.bbaddrmaps.start").unwrap();
let start_addr = unsafe { dlsym(RTLD_DEFAULT, start_sym.as_ptr()) } as *const u8;
if start_addr.is_null() {
panic!("can't find ykllvm.bbaddrmaps.start");
}

let stop_sym = CString::new("ykllvm.bbaddrmaps.stop").unwrap();
let stop_addr = unsafe { dlsym(RTLD_DEFAULT, stop_sym.as_ptr()) } as *const u8;
if stop_addr.is_null() {
panic!("can't find ykllvm.bbaddrmaps.stop");
}

debug_assert!(stop_addr > start_addr);
unsafe { slice::from_raw_parts(start_addr, stop_addr.sub_ptr(start_addr)) }
}

/// Maps (unrelocated) block offsets to their corresponding block map entry.
pub struct BlockMap {
tree: IntervalTree<u64, BlockMapEntry>,
Expand All @@ -131,13 +100,11 @@ pub struct BlockMap {
impl BlockMap {
/// Parse the LLVM blockmap section of the current executable and return a struct holding the
/// mappings.
pub fn new() -> Self {
let bbaddrmap_data = find_blockmap_section();

pub fn new(data: &'static [u8]) -> Self {
// Keep reading blockmap records until we fall outside of the section's bounds.
let mut elems = Vec::new();
let mut crsr = Cursor::new(bbaddrmap_data);
while crsr.position() < u64::try_from(bbaddrmap_data.len()).unwrap() {
let mut crsr = Cursor::new(data);
while crsr.position() < u64::try_from(data.len()).unwrap() {
let version = crsr.read_u8().unwrap();
let _feature = crsr.read_u8().unwrap();
let mut last_off = crsr.read_u64::<NativeEndian>().unwrap();
Expand Down
4 changes: 1 addition & 3 deletions tests/c/blockmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
#include <yk_testing.h>

int main(int argc, char **argv) {
void *bm = __yktrace_hwt_mapper_blockmap_new();
assert(__yktrace_hwt_mapper_blockmap_len(bm) > 0);
__yktrace_hwt_mapper_blockmap_free(bm);
assert(__yktrace_hwt_mapper_blockmap_len() > 0);
return (EXIT_SUCCESS);
}

Expand Down
1 change: 1 addition & 0 deletions ykaddr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ license = "Apache-2.0 OR MIT"
[dependencies]
cached = { version = "0.45", features = ["proc_macro"] }
libc = "0.2"
memmap2 = "0.7"
phdrs = { git = "https://github.com/softdevteam/phdrs" }

[build-dependencies]
Expand Down
8 changes: 8 additions & 0 deletions ykaddr/src/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ use libc::c_void;
use libc::{
Elf64_Addr as Elf_Addr, Elf64_Off as Elf_Off, Elf64_Word as Elf_Word, Elf64_Xword as Elf_Xword,
};
use memmap2;
use phdrs;
use std::{
ffi::{CStr, CString},
fs,
path::PathBuf,
sync::LazyLock,
};
Expand Down Expand Up @@ -131,3 +133,9 @@ pub static SELF_BIN_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
let info = dladdr(addr as usize).unwrap(); // ptr to usize cast always safe.
PathBuf::from(info.dli_fname().unwrap().to_str().unwrap())
});

// The main binary's ELF executable mapped into the address space.
pub static SELF_BIN_MMAP: LazyLock<memmap2::Mmap> = LazyLock::new(|| {
let file = fs::File::open(&SELF_BIN_PATH.as_path()).unwrap();
unsafe { memmap2::Mmap::map(&file).unwrap() }
});
4 changes: 1 addition & 3 deletions ykcapi/yk_testing.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
#define SW_TRACING 0
#define HW_TRACING 1

void *__yktrace_hwt_mapper_blockmap_new(void);
size_t __yktrace_hwt_mapper_blockmap_len(void *mapper);
void __yktrace_hwt_mapper_blockmap_free(void *mapper);
size_t __yktrace_hwt_mapper_blockmap_len();

// Blocks the compiler from optimising the specified value or expression.
//
Expand Down
43 changes: 15 additions & 28 deletions ykrt/src/compile/jitc_llvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,25 @@ use crate::{
mt::{SideTraceInfo, MT},
trace::TracedAOTBlock,
};
use libc::dlsym;
use object::{Object, ObjectSection};
use parking_lot::Mutex;
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
use std::{
env,
error::Error,
ffi::{c_char, c_int, CString},
ffi::{c_char, c_int},
ptr,
sync::Arc,
sync::{Arc, LazyLock},
};
use tempfile::NamedTempFile;
use ykaddr::obj::SELF_BIN_MMAP;

pub static LLVM_BITCODE: LazyLock<&[u8]> = LazyLock::new(|| {
let object = object::File::parse(&**SELF_BIN_MMAP).unwrap();
let sec = object.section_by_name(".llvmbc").unwrap();
sec.data().unwrap()
});

pub(crate) struct JITCLLVM;

Expand All @@ -32,7 +39,7 @@ impl Compiler for JITCLLVM {
) -> Result<CompiledTrace, Box<dyn Error>> {
let (func_names, bbs, trace_len) = self.encode_trace(&irtrace);

let (llvmbc_data, llvmbc_len) = llvmbc_section();
let llvmbc = llvmbc_section();
let (di_tmp, di_fd, di_tmpname_c) = Self::create_debuginfo_temp_file();

let (callstack, aotvalsptr, aotvalslen) = match sti {
Expand All @@ -45,8 +52,8 @@ impl Compiler for JITCLLVM {
func_names.as_ptr(),
bbs.as_ptr(),
trace_len,
llvmbc_data,
llvmbc_len,
llvmbc.as_ptr(),
u64::try_from(llvmbc.len()).unwrap(),
di_fd,
di_tmpname_c,
callstack,
Expand Down Expand Up @@ -134,27 +141,7 @@ impl JITCLLVM {
}
}

/// The `llvm.embedded.module` symbol in the `.llvmbc` section.
#[repr(C)]
struct EmbeddedModule {
/// The length of the bitcode.
len: u64,
/// The start of the bitcode itself.
first_byte_of_bitcode: u8,
}

/// Returns a pointer to (and the size of) the raw LLVM bitcode in the current address space.
pub(crate) fn llvmbc_section() -> (*const u8, u64) {
// ykllvm adds the `SHF_ALLOC` flag to the `.llvmbc` section so that the loader puts it into
// our address space at load time.
let bc = unsafe {
&*(dlsym(
std::ptr::null_mut(),
CString::new("llvm.embedded.module")
.unwrap()
.as_c_str()
.as_ptr(),
) as *const EmbeddedModule)
};
(&bc.first_byte_of_bitcode as *const u8, bc.len)
pub(crate) fn llvmbc_section() -> &'static [u8] {
&**LLVM_BITCODE
}
7 changes: 5 additions & 2 deletions ykrt/src/deopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,11 @@ unsafe extern "C" fn __ykrt_deopt(

let infoptr = Box::into_raw(Box::new(&mut info));

let (data, len) = crate::compile::jitc_llvm::llvmbc_section();
let moduleref = __yktracec_get_aot_module(&BitcodeSection { data, len });
let bc = crate::compile::jitc_llvm::llvmbc_section();
let moduleref = __yktracec_get_aot_module(&BitcodeSection {
data: bc.as_ptr(),
len: u64::try_from(bc.len()).unwrap(),
});

// The LLVM CAPI doesn't allow us to manually lock/unlock a ThreadSafeModule, and uses a
// call-back function instead which it runs after locking the module. This means we need to
Expand Down
22 changes: 5 additions & 17 deletions ykrt/src/frame/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@

use llvm_sys::{core::*, prelude::LLVMValueRef};
use object::{Object, ObjectSection};
use std::{convert::TryFrom, ffi::c_void, fs, ptr, slice, sync::LazyLock, thread};
use ykaddr::{
addr::off_to_vaddr,
obj::{PHDR_MAIN_OBJ, SELF_BIN_PATH},
};
use std::{convert::TryFrom, ffi::c_void, ptr, sync::LazyLock, thread};
use ykaddr::obj::SELF_BIN_MMAP;
use yksmp::{Location as SMLocation, PrologueInfo, Record, StackMapParser};

mod llvmbridge;
Expand All @@ -28,21 +25,12 @@ impl AOTStackmapInfo {
}

static AOT_STACKMAPS: LazyLock<AOTStackmapInfo> = LazyLock::new(|| {
// Load the stackmap from the binary to parse in the stackmaps.
//
// OPT: Don't look at the ELF binary to do this. Get ykllvm to insert start/end symbols for the
// section and look those up instead.
let file = fs::File::open(&*SELF_BIN_PATH).unwrap();
let exemmap = unsafe { memmap2::Mmap::map(&file).unwrap() };
let object = object::File::parse(&*exemmap).unwrap();
// Load the stackmap from the binary to parse in tthe stackmaps.
let object = object::File::parse(&**SELF_BIN_MMAP).unwrap();
let sec = object.section_by_name(".llvm_stackmaps").unwrap();
let sec_vaddr = off_to_vaddr(&PHDR_MAIN_OBJ, sec.address()).unwrap();

// Parse the stackmap.
let slice = unsafe {
slice::from_raw_parts(sec_vaddr as *const _, usize::try_from(sec.size()).unwrap())
};
let (entries, numrecs) = StackMapParser::get_entries(slice);
let (entries, numrecs) = StackMapParser::get_entries(sec.data().unwrap());
let mut pinfos = Vec::new();
let mut records = Vec::new();
records.resize_with(usize::try_from(numrecs).unwrap(), || (Record::empty(), 0));
Expand Down
16 changes: 3 additions & 13 deletions ykrt/src/trace/hwt/testing.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
//! This module is only enabled when the `yk_testing` feature is enabled. It contains functions
//! that are only needed when testing internal yk code.
use hwtracer::llvm_blockmap::BlockMap;
use hwtracer::llvm_blockmap::LLVM_BLOCK_MAP;

#[no_mangle]
pub extern "C" fn __yktrace_hwt_mapper_blockmap_new() -> *mut BlockMap {
Box::into_raw(Box::new(BlockMap::new()))
}

#[no_mangle]
pub extern "C" fn __yktrace_hwt_mapper_blockmap_free(bm: *mut BlockMap) {
drop(unsafe { Box::from_raw(bm) });
}

#[no_mangle]
pub extern "C" fn __yktrace_hwt_mapper_blockmap_len(bm: *mut BlockMap) -> usize {
unsafe { &*bm }.len()
pub extern "C" fn __yktrace_hwt_mapper_blockmap_len() -> usize {
LLVM_BLOCK_MAP.len()
}

0 comments on commit 5f17d55

Please sign in to comment.