From 3d34a52de77d4bf2eb6b8a033cb78b5fc8c02a1f Mon Sep 17 00:00:00 2001 From: Chunmei Xu Date: Mon, 1 Jun 2026 00:00:00 +0800 Subject: [PATCH] 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 --- tools/bashreadline.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tools/bashreadline.py b/tools/bashreadline.py index 7e8324a2c0ea..bee8b6131002 100755 --- a/tools/bashreadline.py +++ b/tools/bashreadline.py @@ -21,6 +21,7 @@ from bcc import BPF from time import strftime import argparse +import subprocess parser = argparse.ArgumentParser( description="Print entered bash commands from all running shells", @@ -44,8 +45,46 @@ def get_sym(filename): return "readline" +def is_sym_defined(filename, symname): + """Check if symbol is defined (not just imported) in an ELF binary.""" + try: + with open(filename, 'rb') as f: + elf = ELFFile(f) + dynsym = elf.get_section_by_name(".dynsym") + if dynsym: + for s in dynsym.iter_symbols(): + if s.name == symname: + return s.entry['st_shndx'] != 'SHN_UNDEF' + except Exception: + pass + return False + + +def find_readline_so(): + """Find libreadline.so that bash dynamically links against.""" + try: + out = subprocess.check_output(["ldd", "/bin/bash"], + stderr=subprocess.DEVNULL, text=True) + for line in out.splitlines(): + if "libreadline" in line and "=>" in line: + path = line.split("=>")[1].strip().split()[0] + if path.startswith("/"): + return path + except Exception: + pass + return None + + sym = get_sym(name) +# When readline is undefined in /bin/bash (dynamically linked), +# auto-detect and use libreadline.so instead. +if not args.shared and not is_sym_defined(name, sym): + lib = find_readline_so() + if lib: + name = lib + sym = get_sym(name) + # load BPF program bpf_text = """ #include