Skip to content

Commit 3d34a52

Browse files
committed
tools/bashreadline: auto-detect libreadline.so for dynamically linked bash
On systems where /bin/bash dynamically links libreadline (which is the common case on modern distros), the `readline` symbol in the bash ELF is merely an undefined import (SHN_UNDEF, st_value=0). BCC's attach_uretprobe then fails with: Exception: could not determine address of symbol readline in /bin/bash The tool already provides a `-s` flag for users to manually specify the libreadline.so path, but this is inconvenient and breaks automated smoke tests. This patch adds automatic detection: after resolving the symbol name, it checks whether the symbol is actually defined (not just imported) in the target binary. If not, it uses `ldd /bin/bash` to find the real libreadline.so path and redirects the uprobe there. The logic is backward-compatible: - If bash statically links readline -> symbol is defined, no change - If user passes `-s` -> manual path used, no auto-detection - If bash dynamically links readline -> auto-fallback to .so
1 parent a3dcb9a commit 3d34a52

1 file changed

Lines changed: 39 additions & 0 deletions

File tree

tools/bashreadline.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from bcc import BPF
2222
from time import strftime
2323
import argparse
24+
import subprocess
2425

2526
parser = argparse.ArgumentParser(
2627
description="Print entered bash commands from all running shells",
@@ -44,8 +45,46 @@ def get_sym(filename):
4445
return "readline"
4546

4647

48+
def is_sym_defined(filename, symname):
49+
"""Check if symbol is defined (not just imported) in an ELF binary."""
50+
try:
51+
with open(filename, 'rb') as f:
52+
elf = ELFFile(f)
53+
dynsym = elf.get_section_by_name(".dynsym")
54+
if dynsym:
55+
for s in dynsym.iter_symbols():
56+
if s.name == symname:
57+
return s.entry['st_shndx'] != 'SHN_UNDEF'
58+
except Exception:
59+
pass
60+
return False
61+
62+
63+
def find_readline_so():
64+
"""Find libreadline.so that bash dynamically links against."""
65+
try:
66+
out = subprocess.check_output(["ldd", "/bin/bash"],
67+
stderr=subprocess.DEVNULL, text=True)
68+
for line in out.splitlines():
69+
if "libreadline" in line and "=>" in line:
70+
path = line.split("=>")[1].strip().split()[0]
71+
if path.startswith("/"):
72+
return path
73+
except Exception:
74+
pass
75+
return None
76+
77+
4778
sym = get_sym(name)
4879

80+
# When readline is undefined in /bin/bash (dynamically linked),
81+
# auto-detect and use libreadline.so instead.
82+
if not args.shared and not is_sym_defined(name, sym):
83+
lib = find_readline_so()
84+
if lib:
85+
name = lib
86+
sym = get_sym(name)
87+
4988
# load BPF program
5089
bpf_text = """
5190
#include <uapi/linux/ptrace.h>

0 commit comments

Comments
 (0)