diff --git a/.gitignore b/.gitignore
index e57cc32e..a67cb447 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@
/go
ebpf-profiler
ci-kernels
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 00000000..7fea342f
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,686 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bytes"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
+
+[[package]]
+name = "cc"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
+dependencies = [
+ "jobserver",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cmake"
+version = "0.1.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "cpp_demangle"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "either"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fastrand"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "flate2"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "gimli"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e1d97fbe9722ba9bbd0c97051c2956e726562b61f86a25a4360398a40edfc9"
+dependencies = [
+ "fallible-iterator",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "indexmap"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "intervaltree"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "270bc34e57047cab801a8c871c124d9dc7132f6473c6401f645524f4e6edd111"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "lru"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "memmap2"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "multimap"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
+
+[[package]]
+name = "object"
+version = "0.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "petgraph"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prost"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
+dependencies = [
+ "bytes",
+ "heck",
+ "itertools",
+ "log",
+ "multimap",
+ "once_cell",
+ "petgraph",
+ "prettyplease",
+ "prost",
+ "prost-types",
+ "regex",
+ "syn",
+ "tempfile",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
+dependencies = [
+ "prost",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "rustix"
+version = "0.38.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "symblib"
+version = "0.0.0"
+dependencies = [
+ "anyhow",
+ "base64",
+ "cpp_demangle",
+ "fallible-iterator",
+ "flate2",
+ "gimli",
+ "intervaltree",
+ "lru",
+ "memmap2",
+ "object",
+ "prost",
+ "prost-build",
+ "rustc-demangle",
+ "sha2",
+ "smallvec",
+ "tempfile",
+ "thiserror",
+ "zstd",
+ "zydis",
+]
+
+[[package]]
+name = "symblib-capi"
+version = "0.0.0"
+dependencies = [
+ "fallible-iterator",
+ "symblib",
+ "thiserror",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+
+[[package]]
+name = "zstd"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.10+zstd.1.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
+
+[[package]]
+name = "zydis"
+version = "4.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0395dcbec9d43ff14811624d4876db7a4a51d1ed73ce3f9e89d14a7e4eeb9ae1"
+dependencies = [
+ "bitflags",
+ "cmake",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 00000000..831c6628
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,85 @@
+# Rust workspace. Allows command like `cargo test` to work anywhere within the
+# repo and ensures that all components use the same dependency versions
+# (global Cargo.lock).
+
+[workspace]
+members = [
+ "rust-crates/symblib",
+ "rust-crates/symblib-capi",
+]
+resolver = "2"
+
+[workspace.package]
+version = "0.0.0"
+rust-version = "1.77"
+
+[profile.release]
+lto = "thin"
+codegen-units = 1
+panic = "abort"
+opt-level = 3
+strip = "debuginfo"
+
+[profile.release-unstripped]
+inherits = "release"
+strip = false
+debug = 1
+
+[profile.release-with-asserts]
+inherits = "release-unstripped"
+overflow-checks = true
+debug-assertions = true
+
+[profile.test]
+opt-level = 1 # default of 0 is annoyingly slow
+
+[workspace.dependencies]
+anyhow = "1"
+argh = "0.1"
+base64 = "0.22.0"
+cpp_demangle = "0.4"
+fallible-iterator = "0.3"
+flate2 = "1"
+memmap2 = "0.9.0"
+native-tls = "0.2"
+prost = "0.12.1"
+prost-build = "0.12.1"
+rustc-demangle = "0.1"
+serde_json = "1"
+sha2 = "0.10"
+tempfile = "3"
+thiserror = "1"
+zstd = "0.13.0"
+zydis = "4.1.1"
+
+[workspace.dependencies.gimli]
+version = "0.30.0"
+default-features = false
+features = ["std", "endian-reader", "fallible-iterator"]
+
+[workspace.dependencies.intervaltree]
+version = "0.2"
+default-features = false
+features = ["std"]
+
+[workspace.dependencies.lru]
+version = "0.12.0"
+default-features = false
+
+[workspace.dependencies.object]
+version = "0.36.0"
+default-features = false
+features = ["std", "read_core", "elf", "macho", "unaligned"]
+
+[workspace.dependencies.serde]
+version = "1"
+features = ["derive"]
+
+[workspace.dependencies.smallvec]
+version = "1"
+features = ["const_new", "union", "const_generics", "write"]
+
+[workspace.dependencies.ureq]
+version = "2"
+default-features = false
+features = ["gzip", "native-tls", "native-certs"]
diff --git a/rust-crates/README.md b/rust-crates/README.md
new file mode 100644
index 00000000..93ec4146
--- /dev/null
+++ b/rust-crates/README.md
@@ -0,0 +1,69 @@
+Rust components
+===============
+
+This directory contains the Rust components for symbolization of native traces.
+They are built using the `cargo` build system. Please refer to the README
+documents in the subdirectories for details.
+
+## Source code documentation
+
+> [!TIP]
+>
+> If you're trying to familiarize yourself with the codebase, this is heavily
+> recommended. All the important documentation and `README`s are included into
+> the rustdoc built documentation, and the generated doc is much more structured
+> than what you'd get by just browsing through the repository.
+
+The source code is extensively documented with `rustdoc`, which is invoked
+through cargo.
+
+```bash
+# Build documentation for our Rust crates and open it in a browser window
+cargo doc --document-private-items --workspace --open
+```
+
+By default, this will open the documentation for `symblib`.
+
+## Import style
+
+Whenever the name of a type or function that is being imported isn't necessarily
+unique, we instead import the module that contains it and then use the module
+name to qualify the access. This is essentially similar to how things are done
+in Golang.
+
+If the item being important has a very significant, unique name within the code-
+base, it's also acceptable to import (`use`) that type directly and refer to it
+without additional qualification.
+
+
+Examples
+
+There are many different modules that expose `File` and `Range` types. Import
+the module instead and qualify the items with `module::item`.
+
+```rust
+use std::fs;
+use symblib::objfile;
+
+let a: fs::File = todo!();
+let b: objfile::File = todo!();
+```
+
+```rust
+use std::ops;
+use symblib::symbfile;
+
+let a: ops::Range = todo!();
+let b: symbfile::Range = todo!();
+```
+
+`GoRuntimeInfo` is a very unique name that is unlikely to cause confusion even
+without further qualification. Import item directly.
+
+```rust
+use symblib::gosym::GoRuntimeInfo;
+
+let a: GoRuntimeInfo<'static> = todo!();
+```
+
+
diff --git a/doc/symb-proto/README.md b/rust-crates/symb-proto/README.md
similarity index 97%
rename from doc/symb-proto/README.md
rename to rust-crates/symb-proto/README.md
index cbc1ec09..cc00b70a 100644
--- a/doc/symb-proto/README.md
+++ b/rust-crates/symb-proto/README.md
@@ -1,7 +1,5 @@
-Elastic symbolization protocol
-==============================
-
-## `symbfile` format
+symbfile format
+===============
`symbfile` is our custom file format for efficiently storing large amounts of
symbol information. A symbfile is a concatenation of length- and message-type
@@ -20,7 +18,7 @@ We currently use two different symbol information representations:
given address, the user would sweep though the whole symbfile and collect all
ranges that contain the desired address and then order the resulting range
records by their `depth` field. This presents the ground truth for symbol
- information.
+ information.
- **Return pad records ([`ReturnPadV1`])**\
These map a single address to the symbols of a full inline trace. We generate
such records for each instruction following a `call`. The idea here is that
@@ -31,7 +29,7 @@ We currently use two different symbol information representations:
While the symbfile format would generally also allow mixing both record types
into a single file, we currently always generate a separate symbfile per record
-kind.
+kind.
More details about the format itself can be found in the documentation comments
of the [protobuf definition][symbfile-proto].
@@ -95,4 +93,4 @@ explains the failure in greater detail, for example:
`uuid` allows logically connecting user reports and logs: error reports from
the user that contain the UUID allow finding the logs needed for
-investigation and debugging.
\ No newline at end of file
+investigation and debugging.
diff --git a/doc/symb-proto/symbfile.proto b/rust-crates/symb-proto/symbfile.proto
similarity index 100%
rename from doc/symb-proto/symbfile.proto
rename to rust-crates/symb-proto/symbfile.proto
diff --git a/rust-crates/symblib-capi/.gitignore b/rust-crates/symblib-capi/.gitignore
new file mode 100644
index 00000000..9c318041
--- /dev/null
+++ b/rust-crates/symblib-capi/.gitignore
@@ -0,0 +1,2 @@
+c/demo
+go/go
diff --git a/rust-crates/symblib-capi/Cargo.toml b/rust-crates/symblib-capi/Cargo.toml
new file mode 100644
index 00000000..60f098db
--- /dev/null
+++ b/rust-crates/symblib-capi/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "symblib-capi"
+edition = "2021"
+version.workspace = true
+rust-version.workspace = true
+
+[lib]
+crate-type = ["staticlib", "cdylib"]
+
+[dependencies]
+symblib.path = "../symblib"
+
+fallible-iterator.workspace = true
+thiserror.workspace = true
diff --git a/rust-crates/symblib-capi/README.md b/rust-crates/symblib-capi/README.md
new file mode 100644
index 00000000..8c287789
--- /dev/null
+++ b/rust-crates/symblib-capi/README.md
@@ -0,0 +1,4 @@
+symblib C API
+=============
+
+This crate exposes the public core API of symblib as a C library.
diff --git a/rust-crates/symblib-capi/c/Makefile b/rust-crates/symblib-capi/c/Makefile
new file mode 100644
index 00000000..f349fed6
--- /dev/null
+++ b/rust-crates/symblib-capi/c/Makefile
@@ -0,0 +1,19 @@
+.PHONY: all clean run-demo
+
+RUST_WORKSPACE_DIR = ../../..
+TARGET_DIR = $(RUST_WORKSPACE_DIR)/target/release
+
+all: demo
+
+$(TARGET_DIR)/libsymblib_capi.so: ../src/*.rs
+ cargo build --release --manifest-path $(RUST_WORKSPACE_DIR)/Cargo.toml
+
+demo: symblib.h demo.c $(TARGET_DIR)/libsymblib_capi.so
+ cc -g -I. -o $@ demo.c -L$(TARGET_DIR) -lsymblib_capi -ldl
+
+run-demo: demo
+ LD_LIBRARY_PATH=$(TARGET_DIR) ./demo
+
+clean:
+ cargo clean --manifest-path $(RUST_CRATE_DIR)/Cargo.toml
+ rm -f demo
diff --git a/rust-crates/symblib-capi/c/demo.c b/rust-crates/symblib-capi/c/demo.c
new file mode 100644
index 00000000..23e8702f
--- /dev/null
+++ b/rust-crates/symblib-capi/c/demo.c
@@ -0,0 +1,96 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "symblib.h"
+
+// Example visitor callback for processing return pads
+SymblibStatus retpad_visitor(void* user_data, const SymblibReturnPad* ret_pad) {
+ printf("\nReturn pad at ELF VA: 0x%08" PRIx64 "\n", ret_pad->elf_va);
+
+ // Iterate over each entry in the SymblibReturnPad
+ for (size_t i = 0; i < ret_pad->entries.len; ++i) {
+ SymblibReturnPadEntry* entry = &((SymblibReturnPadEntry*)ret_pad->entries.data)[i];
+ printf("\tEntry %zu:\n", i);
+ printf("\t\tFunction: %s\n", entry->func ? entry->func : "(null)");
+ printf("\t\tFile: %s\n", entry->file ? entry->file : "(null)");
+ printf("\t\tLine: %u\n", entry->line);
+ }
+
+ return 0;
+}
+
+// Example visitor callback for processing ranges
+SymblibStatus range_visitor(void* user_data, const SymblibRange* range) {
+ printf("\nSymbol range at ELF VA: 0x08%" PRIx64 "\n", range->elf_va);
+ printf("\tFunction: %s\n", range->func);
+ printf("\tFile: %s\n", range->file ? range->file : "(null)");
+ printf("\tCall File: %s\n", range->call_file ? range->call_file : "(null)");
+ printf("\tCall Line: %u\n", range->call_line);
+ printf("\tDepth: %u\n", range->depth);
+ printf("\tLine Table Length: %zu\n", range->line_table.len);
+
+ // Submit the range to the return pad extractor.
+ SymblibStatus err = symblib_retpadextr_submit(
+ (SymblibRetPadExtractor*)user_data, range, retpad_visitor, NULL);
+ if (err != SYMBLIB_OK) {
+ fprintf(stderr, "Failed to submit range for extraction\n");
+ return err;
+ }
+
+ return 0;
+}
+
+int main(int argc, const char** argv) {
+ const char* executable;
+
+ switch (argc) {
+ case 0:
+ return EXIT_FAILURE;
+ case 1:
+ // Use this binary.
+ executable = argv[0];
+ break;
+ default:
+ // Use user-passed file.
+ executable = argv[1];
+ }
+
+ printf("Starting range extraction for executable: %s\n", executable);
+
+ // Initialize the global return pad extractor.
+ // We use it in the range extractor visitor.
+ SymblibRetPadExtractor* extr = NULL;
+ SymblibStatus err = symblib_retpadextr_new(executable, &extr);
+ if (err != SYMBLIB_OK) {
+ fprintf(stderr, "Failed to create global SymblibRetPadExtractor\n");
+ return EXIT_FAILURE;
+ }
+ assert(extr != NULL);
+
+ // Call the range extraction function with our visitor.
+ err = symblib_rangeextr(executable, false, range_visitor, extr);
+ if (err != SYMBLIB_OK) {
+ fprintf(stderr, "Error during range extraction: %d\n", err);
+ symblib_retpadextr_free(extr);
+ return EXIT_FAILURE;
+ }
+
+ // Notify the return pad extractor that we're done.
+ err = symblib_retpadextr_submit(extr, NULL, retpad_visitor, NULL);
+ if (err != SYMBLIB_OK) {
+ fprintf(stderr, "Failed to submit end-of-ranges marker\n");
+ symblib_retpadextr_free(extr);
+ return err;
+ }
+
+ printf("\nRange extraction completed successfully.\n");
+
+ symblib_retpadextr_free(extr);
+ return EXIT_SUCCESS;
+}
diff --git a/rust-crates/symblib-capi/c/symblib.h b/rust-crates/symblib-capi/c/symblib.h
new file mode 100644
index 00000000..df3c2be0
--- /dev/null
+++ b/rust-crates/symblib-capi/c/symblib.h
@@ -0,0 +1,145 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef SYMBLIB_H
+#define SYMBLIB_H
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum SymblibStatus {
+ SYMBLIB_OK = 0,
+ SYMBLIB_ERR_IOMISC = 1,
+ SYMBLIB_ERR_IOFILENOTFOUND = 2,
+ SYMBLIB_ERR_OBJFILE = 3,
+ SYMBLIB_ERR_DWARF = 4,
+ SYMBLIB_ERR_SYMBCONV = 5,
+ SYMBLIB_ERR_RETPAD = 6,
+ SYMBLIB_ERR_BADUTF8 = 7,
+ SYMBLIB_ERR_ALREADYCLOSED = 8,
+} SymblibStatus;
+
+// Opaque handle to a return pad extractor.
+typedef struct SymblibRetPadExtractor SymblibRetPadExtractor;
+
+// Rust managed string.
+typedef const char* SymblibString;
+
+// Array of objects.
+typedef struct {
+ // Pointer to the first item in the slice.
+ //
+ // May or may not be NULL if `len == 0`: don't rely on it.
+ const void* data;
+
+ // Number of entries in the slice.
+ size_t len;
+} SymblibSlice;
+
+// Entry in the return pad inline trace.
+//
+// See symbfile.proto for details.
+typedef struct {
+ SymblibString func; // never null
+ SymblibString file; // may be null
+ uint32_t line; // 0 = unknown
+} SymblibReturnPadEntry;
+
+// Symbol info for a return pad.
+//
+// See symbfile.proto for details.
+typedef struct {
+ uint64_t elf_va;
+ SymblibSlice/**/ entries;
+} SymblibReturnPad;
+
+// Symbol info for a PC range.
+//
+// See symbfile.proto for details.
+typedef struct {
+ uint64_t elf_va;
+ uint32_t length;
+ SymblibString func;
+ SymblibString file;
+ SymblibString call_file;
+ uint32_t call_line;
+ uint32_t depth;
+ SymblibSlice/**/ line_table;
+ // Omitted internal Rust-specific field rust_range
+} SymblibRange;
+
+// Entry in a range's line table.
+//
+// See symbfile.proto for details.
+typedef struct {
+ uint32_t offset;
+ uint32_t line_number;
+} SymblibLineTableEntry;
+
+// Visitor callback for extracted ranges.
+//
+// The range is **borrowed** to the callee and the pointer is only valid for
+// the duration of the visitor call. Returning an error will abort further
+// execution and return early.
+typedef SymblibStatus (*SymblibRangeVisitor)(void* user_data, const SymblibRange*);
+
+// Visitor callback for return pads.
+//
+// The return pad is **borrowed** to the callee and the pointer is only valid
+// for the duration of the visitor call. Returning an error will abort further
+// execution and return early.
+typedef SymblibStatus (*SymblibRetPadVisitor)(void* user_data, const SymblibReturnPad*);
+
+// Extract ranges from an executable.
+//
+// This creates a range extractor with all supported debug symbol formats. The
+// extractor is then run to completion and the visitor is invoked for every
+// range that is found in the executable. The user_data pointer is passed to
+// the visitor untouched and may be NULL.
+extern SymblibStatus symblib_rangeextr(
+ const char* executable,
+ bool follow_alt_link,
+ SymblibRangeVisitor visitor,
+ void* user_data
+);
+
+// Create a new return pad extractor.
+//
+// The instance must be freed via a call to `symblib_retpadextr_free`.
+extern SymblibStatus symblib_retpadextr_new(
+ const char* executable,
+ SymblibRetPadExtractor** extr
+);
+
+// Submit a new range to the return pad extractor.
+//
+// The callback may be invoked 0..n times for each range submitted. Processing
+// is happening asynchronously in the background: there is no guarantee that
+// the return pads passed to the visitor at each call correspond to the range
+// that was just submitted.
+//
+// The user_data pointer is passed to the visitor untouched and may be NULL.
+//
+// Once all ranges have been submitted, call this function with a `NULL` range
+// once to indicate this, forcing all remaining buffered return pads to be
+// flushed.
+extern SymblibStatus symblib_retpadextr_submit(
+ SymblibRetPadExtractor* extr,
+ const SymblibRange* range,
+ SymblibRetPadVisitor visitor,
+ void* user_data
+);
+
+// Frees a return pad extractor.
+extern void symblib_retpadextr_free(SymblibRetPadExtractor* extr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SYMBLIB_H
diff --git a/rust-crates/symblib-capi/go/Makefile b/rust-crates/symblib-capi/go/Makefile
new file mode 100644
index 00000000..f6d2e320
--- /dev/null
+++ b/rust-crates/symblib-capi/go/Makefile
@@ -0,0 +1,13 @@
+.PHONY: all clean
+
+all:
+ CGO_ENABLED=1 \
+ go build \
+ -mod=readonly \
+ -ldflags='-linkmode external -extldflags=-static' \
+ -trimpath \
+ -tags 'static_build'
+
+
+clean:
+ go clean
diff --git a/rust-crates/symblib-capi/go/main.go b/rust-crates/symblib-capi/go/main.go
new file mode 100644
index 00000000..17ccd33f
--- /dev/null
+++ b/rust-crates/symblib-capi/go/main.go
@@ -0,0 +1,96 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package main
+
+/*
+#cgo LDFLAGS: ${SRCDIR}/../../../target/release/libsymblib_capi.a
+#cgo CFLAGS: -g -Wall
+#include "../c/symblib.h"
+#include
+
+// Declare wrapper functions for linking.
+SymblibStatus rangeVisitorWrapper(void* user_data, SymblibRange* range);
+SymblibStatus retPadVisitorWrapper(void* user_data, SymblibReturnPad* ret_pad);
+*/
+import "C"
+import (
+ "fmt"
+ "os"
+ "unsafe"
+)
+
+//export retPadVisitorWrapper
+func retPadVisitorWrapper(_ unsafe.Pointer, retPadPtr *C.SymblibReturnPad) C.SymblibStatus {
+ // Process the return pad data
+ elfVA := uint64(retPadPtr.elf_va)
+ entriesCount := int(retPadPtr.entries.len)
+ fmt.Printf("Return Pad: ELF VA: 0x%x, Entries: %d\n", elfVA, entriesCount)
+
+ return C.SYMBLIB_OK
+}
+
+//export rangeVisitorWrapper
+func rangeVisitorWrapper(userData unsafe.Pointer, rangePtr *C.SymblibRange) C.SymblibStatus {
+ elfVA := uint64(rangePtr.elf_va)
+ length := uint32(rangePtr.length)
+ file := C.GoString(rangePtr.file)
+ // cgo transforms the field func in SymblibRange to _func
+ // as func is a reserved keyword in Go.
+ function := C.GoString(rangePtr._func)
+
+ fmt.Printf("Range: ELF VA: 0x%x, Length: %d, Function: %s File: %s\n",
+ elfVA, length, function, file)
+
+ return C.symblib_retpadextr_submit(
+ (*C.SymblibRetPadExtractor)(userData),
+ rangePtr,
+ C.SymblibRetPadVisitor(C.retPadVisitorWrapper),
+ nil,
+ )
+}
+
+func mainWithExitCode() int {
+ // For the purpose of demonstration symbolize the executable themselves.
+ executablePath := C.CString(os.Args[0])
+ defer C.free(unsafe.Pointer(executablePath))
+
+ // Initialize the global return pad extractor.
+ // We use it in the range extractor visitor.
+ var extractor *C.SymblibRetPadExtractor
+
+ //nolint:gocritic
+ status := C.symblib_retpadextr_new(executablePath, &extractor)
+ if status != C.SYMBLIB_OK {
+ fmt.Fprintf(os.Stderr, "Failed to create return pad extractor: %d\n", status)
+ return 1
+ }
+ defer C.symblib_retpadextr_free(extractor)
+
+ // Call the range extraction function with our visitor.
+ status = C.symblib_rangeextr(
+ executablePath,
+ C.bool(true),
+ C.SymblibRangeVisitor(C.rangeVisitorWrapper),
+ unsafe.Pointer(extractor),
+ )
+ if status != C.SYMBLIB_OK {
+ fmt.Fprintf(os.Stderr, "Failed to extract ranges: %d\n", status)
+ return 1
+ }
+
+ // Notify the return pad extractor that we're done.
+ status = C.symblib_retpadextr_submit(extractor, nil,
+ C.SymblibRetPadVisitor(C.retPadVisitorWrapper), nil)
+ if status != C.SYMBLIB_OK {
+ fmt.Fprintf(os.Stderr, "Failed to notify retpad extractor: %d\n", status)
+ return 1
+ }
+
+ fmt.Println("Ranges extracted successfully")
+ return 0
+}
+
+func main() {
+ os.Exit(mainWithExitCode())
+}
diff --git a/rust-crates/symblib-capi/src/ffislice.rs b/rust-crates/symblib-capi/src/ffislice.rs
new file mode 100644
index 00000000..06a825a1
--- /dev/null
+++ b/rust-crates/symblib-capi/src/ffislice.rs
@@ -0,0 +1,62 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+use std::{marker::PhantomData, mem, ptr, slice};
+
+/// Read-only, owned FFI-safe owned slice type.
+///
+/// The caller must ensure that `T` is FFI-safe (`#[repr(C)]`).
+#[repr(C)]
+#[derive(Debug)]
+pub struct SymblibSlice {
+ /// Data pointer.
+ ///
+ /// May or may not be null for empty slices: don't rely on it.
+ data: *mut T,
+
+ /// Number of entries in the slice.
+ len: usize,
+
+ /// Make compiler print warnings if `T` isn't FFI-safe.
+ _marker: PhantomData,
+}
+
+impl From> for SymblibSlice {
+ fn from(vec: Vec) -> Self {
+ let mut s = vec.into_boxed_slice();
+ let data = s.as_mut_ptr();
+ let len = s.len();
+ mem::forget(s);
+
+ Self {
+ data,
+ len,
+ _marker: PhantomData,
+ }
+ }
+}
+
+impl From> for Box<[T]> {
+ fn from(s: SymblibSlice) -> Self {
+ unsafe {
+ let std_slice = slice::from_raw_parts_mut(s.data, s.len);
+ mem::forget(s);
+ Box::<[T]>::from_raw(std_slice)
+ }
+ }
+}
+
+impl From> for Vec {
+ fn from(s: SymblibSlice) -> Self {
+ Vec::from(Box::<[T]>::from(s))
+ }
+}
+
+impl Drop for SymblibSlice {
+ fn drop(&mut self) {
+ // Drop by converting to boxed slice and then dropping the slice.
+ drop(Box::<[T]>::from(unsafe { ptr::read(self) }));
+ }
+}
+
+unsafe impl Send for SymblibSlice {}
diff --git a/rust-crates/symblib-capi/src/ffistr.rs b/rust-crates/symblib-capi/src/ffistr.rs
new file mode 100644
index 00000000..9fc0eb88
--- /dev/null
+++ b/rust-crates/symblib-capi/src/ffistr.rs
@@ -0,0 +1,48 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+use std::ffi::{c_char, CString};
+use std::{mem, ptr};
+
+/// Read-only, nullable, owned FFI-safe string type.
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct SymblibString(*mut c_char);
+
+impl From