From c9422780d610c6709219de2b167964805e4fab0b Mon Sep 17 00:00:00 2001
From: Sergej Isbrecht <4427685+SergejIsbrecht@users.noreply.github.com>
Date: Wed, 23 Apr 2025 14:28:42 +0200
Subject: [PATCH] aya: introduce uprobe location ElfSymOffset

Transforms symbols's virtual address to file offset as required by the
Linux Kernel.
Reference: https://github.com/libbpf/libbpf/blob/master/src/elf.c#L259C1-L270C2

Fixes: #1198
---
 aya/src/programs/uprobe.rs | 51 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)

diff --git a/aya/src/programs/uprobe.rs b/aya/src/programs/uprobe.rs
index eaba249a..4653aa69 100644
--- a/aya/src/programs/uprobe.rs
+++ b/aya/src/programs/uprobe.rs
@@ -55,8 +55,52 @@ pub enum UProbeAttachLocation<'a> {
     /// The location of the target function in the target object file, offset by
     /// the given number of bytes.
     SymbolOffset(&'a str, u64),
-    /// The offset in the target object file, in bytes.
+    /// The **file** offset in the target object file, in bytes.
     AbsoluteOffset(u64),
+    /// Transform symbol's virtual address (absolute for binaries and relative
+    /// for shared libs) into file offset, which is what kernel is expecting for
+    /// uprobe/uretprobe attachment.
+    /// See Documentation/trace/uprobetracer.rst for more details. This is done
+    /// by looking up symbol's containing section's header and using iter's
+    /// virtual address (`sh_addr`) and corresponding file offset (`sh_offset`)
+    /// to transform `st_value` (virtual address) into desired final file
+    /// offset.
+    ///
+    /// ## Formula:
+    /// ```
+    /// symbol_file_offset = st_value - sh_addr + sh_offset
+    /// ```
+    ///
+    /// ## References:
+    /// * [libbpf](https://github.com/libbpf/libbpf/blob/master/src/elf.c#L259C1-L270C2)
+    /// * [StackOverflow](https://stackoverflow.com/questions/79478284/uprobe-symbol-adress-mapping-offset)
+    ElfSymOffset {
+        /// The symbol's value (virtual address)
+        ///
+        /// ```text
+        /// ❯ readelf -Ws libart.so | c++filt | grep -i DeleteLocalRef
+        ///    Num:    Value          Size Type Bind   Vis        Ndx Name
+        ///   1208: 0000000000601300    26 FUNC GLOBAL PROTECTED  14  art::JNIEnvExt::DeleteLocalRef(_jobject*)
+        /// ```
+        /// Symbol `art::JNIEnvExt::DeleteLocalRef(_jobject*)` in `libart.so` at virtual address `0x601300`
+        st_value: u64,
+        /// The section's memory address
+        ///
+        /// ```text
+        /// ❯ readelf -S --wide libart.so
+        /// There are 29 section headers, starting at offset 0x89be68:
+        ///
+        /// Section Headers:
+        ///   [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
+        ///   [14] .text             PROGBITS        000000000035ff00 15ff00 5df958 00  AX  0   0 128
+        /// ```
+        /// ELF `.text` section in `libart.so` starts at **virtual address** `0x35ff00`
+        sh_addr: u64,
+        /// The section's file offset
+        ///
+        /// ELF `.text` section in `libart.so` starts at **file** offset `0x15ff00`. See `sh_addr`
+        sh_offset: u64,
+    },
 }
 
 impl<'a> From<&'a str> for UProbeAttachLocation<'a> {
@@ -117,6 +161,11 @@ impl UProbe {
             UProbeAttachLocation::Symbol(s) => (Some(s), 0),
             UProbeAttachLocation::SymbolOffset(s, offset) => (Some(s), offset),
             UProbeAttachLocation::AbsoluteOffset(offset) => (None, offset),
+            UProbeAttachLocation::ElfSymOffset {
+                st_value,
+                sh_addr,
+                sh_offset,
+            } => (None, st_value - sh_addr + sh_offset),
         };
         let offset = if let Some(symbol) = symbol {
             let symbol_offset =