Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions rust-crates/symblib-capi/c/symblib.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,44 @@ extern SymblibStatus symblib_retpadextr_submit(
// Frees a return pad extractor.
extern void symblib_retpadextr_free(SymblibRetPadExtractor* extr);

// Opaque handle to SymblibPointResolver.
typedef struct SymblibPointResolver SymblibPointResolver;

// Creates a new SymblibPointResolver.
extern SymblibStatus symblib_goruntime_new(
const char* executable,
SymblibPointResolver** runtime // out arg
);

// Frees a SymblibPointResolver.
extern void symblib_goruntime_free(SymblibPointResolver* runtime);

// Contains information about a symbol and its origin.
typedef struct SymblibResolvedSymbol {
uint64_t start_addr;
SymblibString function_name;
SymblibString file_name;
uint32_t line_number;
} SymblibResolvedSymbol;

// Enveloping struct that contains len number of symbols in data.
typedef struct SymblibSlice_SymblibResolvedSymbol {
const SymblibResolvedSymbol* data;
size_t len;
} SymblibSlice_SymblibResolvedSymbol;

// Single point lookup for pc using SymblibPointResolver.
SymblibStatus symblib_point_resolver_symbols_for_pc(
const SymblibPointResolver* resolver,
uint64_t pc,
SymblibSlice_SymblibResolvedSymbol** symbols // out arg
);

// Frees a SymblibSlice_SymblibResolvedSymbol.
void symblib_slice_symblibresolved_symbol_free(
SymblibSlice_SymblibResolvedSymbol* slice
);

#ifdef __cplusplus
}
#endif
Expand Down
67 changes: 67 additions & 0 deletions rust-crates/symblib-capi/src/gosym.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

use crate::{FfiResult, StatusCode, SymblibPointResolver};
use std::ffi::{c_char, CStr};
use std::mem;
use std::path::Path;
use symblib::symbconv::{PointResolver, ResolvedSymbol, Result as SymconvResult};
use symblib::{gosym::GoRuntimeInfo, objfile};

pub struct SymblibGoRuntime {
#[allow(unused)]
obj: Box<objfile::File>,
runtime: GoRuntimeInfo<'static>,
}

impl PointResolver for SymblibGoRuntime {
fn symbols_for_pc(&self, pc: symblib::VirtAddr) -> SymconvResult<Vec<ResolvedSymbol>> {
self.runtime.symbols_for_pc(pc)
}
}

#[no_mangle]
pub unsafe extern "C" fn symblib_goruntime_new(
executable: *const c_char,
runtime: *mut *mut SymblibPointResolver,
) -> StatusCode {
match goruntime_new_impl(executable, runtime) {
Ok(()) => StatusCode::Ok,
Err(e) => e,
}
}

unsafe fn goruntime_new_impl(
executable: *const c_char,
runtime: *mut *mut SymblibPointResolver,
) -> FfiResult {
let executable = CStr::from_ptr(executable)
.to_str()
.map(Path::new)
.map_err(|_| StatusCode::BadUtf8)?;

let obj = Box::new(objfile::File::load(executable)?);
let obj_reader = obj.parse()?;
let go_runtime = GoRuntimeInfo::open(&obj_reader)?;

// Transmute away lifetime to allow for self-referential struct.
let go_runtime: GoRuntimeInfo<'static> = mem::transmute(go_runtime);

let resolver = SymblibGoRuntime {
obj,
runtime: go_runtime,
};

let point_resolver = Box::new(SymblibPointResolver::new(
Box::new(resolver) as Box<dyn PointResolver + Send>
));
*runtime = Box::into_raw(point_resolver);
Ok(())
}

#[no_mangle]
pub unsafe extern "C" fn symblib_goruntime_free(runtime: *mut SymblibPointResolver) {
if !runtime.is_null() {
drop(Box::from_raw(runtime));
}
}
4 changes: 4 additions & 0 deletions rust-crates/symblib-capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@

mod ffislice;
mod ffistr;
mod gosym;
mod pointresolver;
mod rangeextr;
mod retpadextr;
mod status;

pub use ffislice::*;
pub use ffistr::*;
pub use gosym::*;
pub use pointresolver::*;
pub use rangeextr::*;
pub use retpadextr::*;
pub use status::*;
66 changes: 66 additions & 0 deletions rust-crates/symblib-capi/src/pointresolver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

use crate::{StatusCode, SymblibSlice, SymblibString};
use symblib::symbconv;
use symblib::symbconv::PointResolver;
use symblib::VirtAddr;

#[repr(C)]
#[derive(Debug)]
pub struct SymblibResolvedSymbol {
pub start_addr: VirtAddr,
pub function_name: SymblibString,
pub file_name: SymblibString, // may be empty
pub line_number: u32, // 0 = unknown
}

impl From<symbconv::ResolvedSymbol> for SymblibResolvedSymbol {
fn from(sym: symbconv::ResolvedSymbol) -> Self {
Self {
start_addr: sym.start_addr,
function_name: sym.function_name.into(),
file_name: sym.file_name.unwrap_or("".to_string()).into(),
line_number: sym.line_number.unwrap_or(0),
}
}
}

#[repr(C)]
pub struct SymblibPointResolver {
Comment thread
florianl marked this conversation as resolved.
inner: Box<dyn PointResolver + Send>,
}

impl SymblibPointResolver {
pub fn new(resolver: Box<dyn PointResolver + Send>) -> Self {
Self { inner: resolver }
}
}

#[no_mangle]
pub extern "C" fn symblib_point_resolver_symbols_for_pc(
resolver: &SymblibPointResolver,
pc: VirtAddr,
out_symbols: *mut *mut SymblibSlice<SymblibResolvedSymbol>,
) -> StatusCode {
let symbols: Vec<_> = match resolver.inner.symbols_for_pc(pc) {
Ok(syms) => syms.into_iter().map(Into::into).collect(),
Err(e) => return StatusCode::from(e),
};

unsafe {
*out_symbols = Box::into_raw(Box::new(symbols.into()));
}
StatusCode::Ok
}

#[no_mangle]
pub extern "C" fn symblib_slice_symblibresolved_symbol_free(
slice: *mut SymblibSlice<SymblibResolvedSymbol>,
) {
if !slice.is_null() {
unsafe {
drop(Box::from_raw(slice));
}
}
}
11 changes: 10 additions & 1 deletion rust-crates/symblib-capi/src/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! Defines FFI error codes and their conversion from Rust error types.

use std::io;
use symblib::{dwarf, objfile, retpads, symbconv};
use symblib::{dwarf, gosym, objfile, retpads, symbconv};

pub type FfiResult<T = ()> = Result<T, StatusCode>;

Expand Down Expand Up @@ -42,6 +42,9 @@ pub enum StatusCode {

#[error("The channel was already closed in a previous call")]
AlreadyClosed = 8,

#[error("Point resolver error")]
PointResolver = 9,
}

impl From<StatusCode> for FfiResult {
Expand Down Expand Up @@ -117,3 +120,9 @@ impl From<retpads::Error> for StatusCode {
Self::Retpad
}
}

impl From<gosym::Error> for StatusCode {
fn from(_: gosym::Error) -> Self {
StatusCode::Symbconv
}
}
48 changes: 47 additions & 1 deletion rust-crates/symblib/src/gosym/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ mod errors;
pub use errors::*;
mod raw;

use crate::{objfile, VirtAddr};
use crate::{objfile, symbconv, VirtAddr};
use fallible_iterator::FallibleIterator;
use std::ops::Range;

Expand Down Expand Up @@ -540,3 +540,49 @@ fn range_rel2abs(base: VirtAddr, rng: Range<raw::TextStartOffset>) -> Range<Virt
end: base.wrapping_add(rng.end.0),
}
}

impl symbconv::PointResolver for GoRuntimeInfo<'_> {
/// NOTE: this is currently doesn't support inline functions
fn symbols_for_pc(&self, pc: VirtAddr) -> symbconv::Result<Vec<symbconv::ResolvedSymbol>> {
Comment thread
florianl marked this conversation as resolved.
let func = match self.find_func(pc) {
Ok(Some(func)) => func,
Ok(None) => return Ok(Vec::new()),
Err(e) => return Err(symbconv::Error::Go(symbconv::go::Error::Gosym(e))),
};

let mut symbols = Vec::new();
let mut source_file = None;
let mut line_number = None;

// For file mappings
let mut file_iter = func
.file_mapping()
.map_err(|e| symbconv::Error::Go(symbconv::go::Error::Gosym(e)))?;
while let Ok(Some((range, file))) = file_iter.next() {
if range.contains(&VirtAddr::from(pc)) {
source_file = Some(file.unwrap_or("<unknown>").into());
break;
}
}

// For line mappings
let mut line_iter = func
.line_mapping()
.map_err(|e| symbconv::Error::Go(symbconv::go::Error::Gosym(e)))?;
while let Ok(Some((range, line))) = line_iter.next() {
if range.contains(&VirtAddr::from(pc)) {
line_number = Some(line.unwrap_or(0));
break;
}
}

symbols.push(symbconv::ResolvedSymbol {
start_addr: func.start_addr(),
function_name: func.name().ok().map(|s| s.to_string()),
file_name: source_file,
line_number: line_number,
});

Ok(symbols)
}
}
23 changes: 22 additions & 1 deletion rust-crates/symblib/src/symbconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

//! Extract symbol info and convert it to [`symbfile`] format.

use crate::{objfile, symbfile, AnyError};
use crate::{objfile, symbfile, AnyError, VirtAddr};
use std::io;

/// Result type shorthand.
Expand Down Expand Up @@ -79,6 +79,27 @@ pub trait RangeExtractor {
}
}

/// Hold information about a symbol and its origin.
pub struct ResolvedSymbol {
/// Start address of a symbol
pub start_addr: VirtAddr,
/// Function name associated with an address.
pub function_name: Option<String>,
/// File name that hold this function.
pub file_name: Option<String>,
/// Line number associcated with this virtual address.
pub line_number: Option<u32>,
}

/// Common interface to tesolve symbols for a specific program counter address.
pub trait PointResolver {
/// Returns all symbols that match the given program counter address.
///
/// The returned vector contains all resolved symbols at the given address,
/// which can include both the direct function and any inline frames
fn symbols_for_pc(&self, pc: VirtAddr) -> Result<Vec<ResolvedSymbol>>;
}

fn _assert_obj_safe(_: &dyn RangeExtractor) {}

pub mod dwarf;
Expand Down