diff --git a/rust-crates/symblib-capi/c/symblib.h b/rust-crates/symblib-capi/c/symblib.h index df3c2be0a..24141aa11 100644 --- a/rust-crates/symblib-capi/c/symblib.h +++ b/rust-crates/symblib-capi/c/symblib.h @@ -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 diff --git a/rust-crates/symblib-capi/src/gosym.rs b/rust-crates/symblib-capi/src/gosym.rs new file mode 100644 index 000000000..186388faf --- /dev/null +++ b/rust-crates/symblib-capi/src/gosym.rs @@ -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, + runtime: GoRuntimeInfo<'static>, +} + +impl PointResolver for SymblibGoRuntime { + fn symbols_for_pc(&self, pc: symblib::VirtAddr) -> SymconvResult> { + 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 + )); + *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)); + } +} diff --git a/rust-crates/symblib-capi/src/lib.rs b/rust-crates/symblib-capi/src/lib.rs index b6664ad70..fda7b8716 100644 --- a/rust-crates/symblib-capi/src/lib.rs +++ b/rust-crates/symblib-capi/src/lib.rs @@ -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::*; diff --git a/rust-crates/symblib-capi/src/pointresolver.rs b/rust-crates/symblib-capi/src/pointresolver.rs new file mode 100644 index 000000000..9d4db88ec --- /dev/null +++ b/rust-crates/symblib-capi/src/pointresolver.rs @@ -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 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 { + inner: Box, +} + +impl SymblibPointResolver { + pub fn new(resolver: Box) -> 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, +) -> 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, +) { + if !slice.is_null() { + unsafe { + drop(Box::from_raw(slice)); + } + } +} diff --git a/rust-crates/symblib-capi/src/status.rs b/rust-crates/symblib-capi/src/status.rs index 54686aa2f..8ecfb69f2 100644 --- a/rust-crates/symblib-capi/src/status.rs +++ b/rust-crates/symblib-capi/src/status.rs @@ -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 = Result; @@ -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 for FfiResult { @@ -117,3 +120,9 @@ impl From for StatusCode { Self::Retpad } } + +impl From for StatusCode { + fn from(_: gosym::Error) -> Self { + StatusCode::Symbconv + } +} diff --git a/rust-crates/symblib/src/gosym/mod.rs b/rust-crates/symblib/src/gosym/mod.rs index 6f8394a4d..3fe129823 100644 --- a/rust-crates/symblib/src/gosym/mod.rs +++ b/rust-crates/symblib/src/gosym/mod.rs @@ -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; @@ -540,3 +540,49 @@ fn range_rel2abs(base: VirtAddr, rng: Range) -> Range { + /// NOTE: this is currently doesn't support inline functions + fn symbols_for_pc(&self, pc: VirtAddr) -> symbconv::Result> { + 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("").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) + } +} diff --git a/rust-crates/symblib/src/symbconv/mod.rs b/rust-crates/symblib/src/symbconv/mod.rs index 4708a8426..dcb68a136 100644 --- a/rust-crates/symblib/src/symbconv/mod.rs +++ b/rust-crates/symblib/src/symbconv/mod.rs @@ -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. @@ -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, + /// File name that hold this function. + pub file_name: Option, + /// Line number associcated with this virtual address. + pub line_number: Option, +} + +/// 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>; +} + fn _assert_obj_safe(_: &dyn RangeExtractor) {} pub mod dwarf;