Skip to content

Commit 87c2cb1

Browse files
sancpppdkruces
authored andcommitted
Fix bashreadline (iovisor#4903)
This Pull Request fixes the issue where the bashreadline tool would lose the first bash command upon startup. At runtime, bash blocks at the readline function in bash::readline.c. Upon receiving the Enter key signal, it calls the readline_internal_teardown function to retrieve the user's input. Therefore, changing the hook function from readline to readline_internal_teardown resolves the issue of losing the first bash command after startup of bashreadline. The specific approach is to open the symbol table of bash and check if there is a symbol for readline_internal_teardown function. If not, continue to use the readline function as the hook position.
1 parent f88c053 commit 87c2cb1

File tree

5 files changed

+57
-7
lines changed

5 files changed

+57
-7
lines changed

docker/Dockerfile.ubuntu

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ COPY --from=builder /root/bcc/*.deb /root/bcc/
2424
RUN \
2525
apt-get update -y && \
2626
DEBIAN_FRONTEND=noninteractive apt-get install -y python3 python3-pip python-is-python3 binutils libelf1 kmod && \
27-
pip3 install dnslib cachetools && \
27+
pip3 install dnslib cachetools pyelftools && \
2828
dpkg -i /root/bcc/*.deb

docker/build/Dockerfile.fedora

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ RUN dnf -y install \
5959
iperf \
6060
netperf
6161

62-
RUN pip3 install pyroute2==0.5.18 netaddr==0.8.0 dnslib==0.9.14 cachetools==3.1.1
62+
RUN pip3 install pyroute2==0.5.18 netaddr==0.8.0 dnslib==0.9.14 cachetools==3.1.1 pyelftools==0.30
6363

6464
RUN wget -O ruby-install-${RUBY_INSTALL_VERSION}.tar.gz \
6565
https://github.com/postmodern/ruby-install/archive/v${RUBY_INSTALL_VERSION}.tar.gz && \

docker/build/Dockerfile.ubuntu

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ done \
8181
&& \
8282
apt-get -y clean'
8383

84-
RUN pip3 install pyroute2==0.5.18 netaddr==0.8.0 dnslib==0.9.14 cachetools==3.1.1
84+
RUN pip3 install pyroute2==0.5.18 netaddr==0.8.0 dnslib==0.9.14 cachetools==3.1.1 pyelftools==0.30
8585

8686
# FIXME this is faster than building from source, but it seems there is a bug
8787
# in probing libruby.so rather than ruby binary

libbpf-tools/bashreadline.c

+37-3
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,41 @@ static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
9090
warn("lost %llu events on CPU #%d\n", lost_cnt, cpu);
9191
}
9292

93+
static char *find_readline_function_name(const char *bash_path)
94+
{
95+
bool found = false;
96+
int fd = -1;
97+
Elf *elf = NULL;
98+
Elf_Scn *scn = NULL;
99+
GElf_Shdr shdr;
100+
101+
102+
elf = open_elf(bash_path, &fd);
103+
104+
while ((scn = elf_nextscn(elf, scn)) != NULL && !found) {
105+
gelf_getshdr(scn, &shdr);
106+
if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
107+
Elf_Data *data = elf_getdata(scn, NULL);
108+
if (data != NULL) {
109+
GElf_Sym *symtab = (GElf_Sym *) data->d_buf;
110+
int sym_count = shdr.sh_size / shdr.sh_entsize;
111+
for (int i = 0; i < sym_count; ++i) {
112+
if(strcmp("readline_internal_teardown", elf_strptr(elf, shdr.sh_link, symtab[i].st_name)) == 0){
113+
found = true;
114+
break;
115+
}
116+
}
117+
}
118+
}
119+
}
120+
121+
close_elf(elf,fd);
122+
if (found)
123+
return "readline_internal_teardown";
124+
else
125+
return "readline";
126+
}
127+
93128
static char *find_readline_so()
94129
{
95130
const char *bash_path = "/bin/bash";
@@ -100,7 +135,7 @@ static char *find_readline_so()
100135
char path[128];
101136
char *result = NULL;
102137

103-
func_off = get_elf_func_offset(bash_path, "readline");
138+
func_off = get_elf_func_offset(bash_path, find_readline_function_name(bash_path));
104139
if (func_off >= 0)
105140
return strdup(bash_path);
106141

@@ -159,7 +194,6 @@ int main(int argc, char **argv)
159194
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
160195
if (err)
161196
return err;
162-
163197
if (libreadline_path) {
164198
readline_so_path = libreadline_path;
165199
} else if ((readline_so_path = find_readline_so()) == NULL) {
@@ -187,7 +221,7 @@ int main(int argc, char **argv)
187221
goto cleanup;
188222
}
189223

190-
func_off = get_elf_func_offset(readline_so_path, "readline");
224+
func_off = get_elf_func_offset(readline_so_path, find_readline_function_name(readline_so_path));
191225
if (func_off < 0) {
192226
warn("cound not find readline in %s\n", readline_so_path);
193227
goto cleanup;

tools/bashreadline.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
# 12-Feb-2016 Allan McAleavy migrated to BPF_PERF_OUTPUT
1818

1919
from __future__ import print_function
20+
from elftools.elf.elffile import ELFFile
2021
from bcc import BPF
2122
from time import strftime
2223
import argparse
@@ -32,6 +33,19 @@
3233

3334
name = args.shared if args.shared else "/bin/bash"
3435

36+
37+
def get_sym(filename):
38+
with open(filename, 'rb') as f:
39+
elf = ELFFile(f)
40+
symbol_table = elf.get_section_by_name(".dynsym")
41+
for symbol in symbol_table.iter_symbols():
42+
if symbol.name == "readline_internal_teardown":
43+
return "readline_internal_teardown"
44+
return "readline"
45+
46+
47+
sym = get_sym(name)
48+
3549
# load BPF program
3650
bpf_text = """
3751
#include <uapi/linux/ptrace.h>
@@ -63,16 +77,18 @@
6377
"""
6478

6579
b = BPF(text=bpf_text)
66-
b.attach_uretprobe(name=name, sym="readline", fn_name="printret")
80+
b.attach_uretprobe(name=name, sym=sym, fn_name="printret")
6781

6882
# header
6983
print("%-9s %-7s %s" % ("TIME", "PID", "COMMAND"))
7084

85+
7186
def print_event(cpu, data, size):
7287
event = b["events"].event(data)
7388
print("%-9s %-7d %s" % (strftime("%H:%M:%S"), event.pid,
7489
event.str.decode('utf-8', 'replace')))
7590

91+
7692
b["events"].open_perf_buffer(print_event)
7793
while 1:
7894
try:

0 commit comments

Comments
 (0)