@@ -39,11 +39,16 @@ type Executable struct {
3939 // Path of the executable on the filesystem.
4040 path string
4141 // Parsed ELF and dynamic symbols' cachedAddresses.
42- cachedAddresses map [string ]uint64
42+ cachedAddresses map [string ]symbolAddress
4343 // Keep track of symbol table lazy load.
4444 cacheAddressesOnce sync.Once
4545}
4646
47+ type symbolAddress struct {
48+ addr uint64
49+ size uint64
50+ }
51+
4752// UprobeOptions defines additional parameters that will be used
4853// when loading Uprobes.
4954type UprobeOptions struct {
@@ -111,7 +116,7 @@ func OpenExecutable(path string) (*Executable, error) {
111116
112117 return & Executable {
113118 path : path ,
114- cachedAddresses : make (map [string ]uint64 ),
119+ cachedAddresses : make (map [string ]symbolAddress ),
115120 }, nil
116121}
117122
@@ -155,20 +160,16 @@ func (ex *Executable) load(f *internal.SafeELFFile) error {
155160 }
156161 }
157162
158- ex .cachedAddresses [s .Name ] = address
163+ ex .cachedAddresses [s .Name ] = symbolAddress {
164+ addr : address ,
165+ size : s .Size ,
166+ }
159167 }
160168
161169 return nil
162170}
163171
164- // address calculates the address of a symbol in the executable.
165- //
166- // opts must not be nil.
167- func (ex * Executable ) address (symbol string , address , offset uint64 ) (uint64 , error ) {
168- if address > 0 {
169- return address + offset , nil
170- }
171-
172+ func (ex * Executable ) lazyLoadSymbols () error {
172173 var err error
173174 ex .cacheAddressesOnce .Do (func () {
174175 var f * internal.SafeELFFile
@@ -181,11 +182,23 @@ func (ex *Executable) address(symbol string, address, offset uint64) (uint64, er
181182
182183 err = ex .load (f )
183184 })
185+ return err
186+ }
187+
188+ // address calculates the address of a symbol in the executable.
189+ //
190+ // opts must not be nil.
191+ func (ex * Executable ) address (symbol string , address , offset uint64 ) (uint64 , error ) {
192+ if address > 0 {
193+ return address + offset , nil
194+ }
195+
196+ err := ex .lazyLoadSymbols ()
184197 if err != nil {
185198 return 0 , fmt .Errorf ("lazy load symbols: %w" , err )
186199 }
187200
188- address , ok := ex .cachedAddresses [symbol ]
201+ symaddress , ok := ex .cachedAddresses [symbol ]
189202 if ! ok {
190203 return 0 , fmt .Errorf ("symbol %s: %w" , symbol , ErrNoSymbol )
191204 }
@@ -196,12 +209,30 @@ func (ex *Executable) address(symbol string, address, offset uint64) (uint64, er
196209 //
197210 // Since only offset values are stored and not elf.Symbol, if the value is 0,
198211 // assume it's an external symbol.
199- if address == 0 {
212+ if symaddress . addr == 0 {
200213 return 0 , fmt .Errorf ("cannot resolve %s library call '%s': %w " +
201214 "(consider providing UprobeOptions.Address)" , ex .path , symbol , ErrNotSupported )
202215 }
203216
204- return address + offset , nil
217+ if offset >= symaddress .size {
218+ return 0 , fmt .Errorf ("offset %d is out of range of symbol %s" , offset , symbol )
219+ }
220+
221+ return symaddress .addr + offset , nil
222+ }
223+
224+ // Symbol calculates the symbol and offset for an address in the executable.
225+ func (ex * Executable ) Symbol (address uint64 ) (UprobeSymbol , error ) {
226+ if err := ex .lazyLoadSymbols (); err != nil {
227+ return UprobeSymbol {}, fmt .Errorf ("lazy load symbols: %w" , err )
228+ }
229+
230+ for name , symaddress := range ex .cachedAddresses {
231+ if symaddress .addr <= address && symaddress .addr + symaddress .size > address {
232+ return UprobeSymbol {name , address - symaddress .addr }, nil
233+ }
234+ }
235+ return UprobeSymbol {}, ErrNoSymbol
205236}
206237
207238// Uprobe attaches the given eBPF program to a perf event that fires when the
0 commit comments