前言
改了一版bash, 给同事用了好长时间,现在需要再加点东西,突然发现gdb附加后,不能正常调试输入的shell命令了。
在原版上试一下, 是能单步源码调试的。
那可能是改版的bash, 在处理用户输入命令之前,在处理过程中,又自己执行了shell命令去干其他活,建立了bash子进程引起不能有效调试的。
记录一下原版bash单步调试的过程。
实验
将bash-4.4.tar.gz上传到debian虚拟机中的实验目录。
解压
tar -xzvf ./bash-4.4.tar.gz
cd /home/dev/bash-4.4
设置configure为调试版
export CFLAGS='-DGCC_HASCLASSVISIBILITY -g -Wall -W'
./configure --enable-debug
看了 ./configure --help, 好像 --enable-debug 是个无效参数啊
主要还是依赖自己导出的CFLAGS中带的-g选项
make clean
将干净的目录回传到本地,用si建立工程进行代码走读。
如果为了标记这是实验的版本,可以改–version的实现,那样再运行bash时,就能确定是自己改过的版本。
/* Give version information about this shell. */
char *
shell_version_string ()
{
const char* psz_my_ver = "ls_2019_0422_1354"; // @note 长度不要 >= 32
static char tt[32 * 2] = { '\0' };
if (tt[0] == '\0')
{
if (release_status)
#if HAVE_SNPRINTF
snprintf (tt, sizeof (tt), "%s_%s.%d(%d)-%s", psz_my_ver, dist_version, patch_level, build_version, release_status);
#else
sprintf (tt, "%s_%s.%d(%d)-%s", psz_my_ver, dist_version, patch_level, build_version, release_status);
#endif
else
#if HAVE_SNPRINTF
snprintf (tt, sizeof (tt), "%s_%s.%d(%d)", psz_my_ver, dist_version, patch_level, build_version);
#else
sprintf (tt, "%s_%s.%d(%d)", psz_my_ver, dist_version, patch_level, build_version);
#endif
}
return tt;
}
make
这时,当前目录下,就生成好了新版的bash.
新bash的安装
make install
但是这样,在我现在的虚拟机debian8.8下是安装失败的,没拷贝到/bin中。
也有另外的可能,bash没有拷贝到当前系统的目录中替换掉原始bash, 而是拷贝到了其他目录.
这时,需要自己手工拷贝。
将make install结果定向到日志中,看到bash拷贝到了/usr/local/lib/下,而实际系统运行的bash是/bin/bash
installing example loadable builtins in /usr/local/lib/bash
用gdb附加另外的bash时,如果break main或已经存在的file:line时,提示不成功,说明有可能是bash不是我们编译出的版本。
用cmp命令验证一下。
cmp -l /bin/bash ./bash | gawk '{printf "%08X %02X %02X\n", $1, strtonum(0$2), strtonum(0$3)}'
如果bash被替换到了系统bash位置,cmp命令行执行后,结果是空的(没输出)。
如果当前编译出的bash和系统中的不一样,cmp命令行会输出那些字节不一样。
如果发现用make install 安装bash不成功,需要手工换一下bash.
mv /bin/bash /bin/bash.org.bk
cp ./bash /bin/
bash的调试
在开发目录的SecureCRT控制台中,输入tty, 先看看自己的tty号码。
然后用ps aux | grep bash 列出当前所有的bash.
再新开一个SecureCRT控制台, 用tty看看tty号码。
然后回到开发目录所在的控制台中,重新用ps aux | grep bash列出当前所有的bash.
新增的bash很容易看出来,那个bash的进程ID就在第2列。
拿到新开控制台窗口的bash进程ID后,就可以用gdb进行源码调试了。
在开发目录的控制台中,输入以下命令进行附加调试。
gdb -tui -p new_bash_pid
这时:
* 下断点,应该有效的。
* 在新bash控制台(被调试的bash), 按键是没有反应的。
下好断点后,按c继续跑起。
在被调试的bash控制台,输入预期的命令后,就会被开发目录所在的bash断住。
然后就可以用s, n 开始单步调试了。
2019_0423
用原版和修改版做了比对,知道修改版为啥编译不过了。
因为是在虚拟机或工控机里面编译的, 回传到本地之前(因为修改了bash源码),会先make clean.
bash的make clean, 并不会清除./support/man2html.o
当在不同计算机上编译后,归档时,导致man2html.o不是当前计算机编译出来的. 在架构不同的计算机上编译时,编译器编译时,不会更新man2html.o(也许是时间比当前的时间更旧), 而是会直接包含上用来编译。这样就导致编译不过。
写了一个编译bash的脚本,这样以后就不会忘了怎么才能正确编译bash工程
rebuild.sh
root@localhost:/home/dev/bash-4.4# vi ./rebuild.sh
#! /bin/sh
export CFLAGS='-DGCC_HASCLASSVISIBILITY -g -Wall'
./configure --enable-debug
make clean
rm -f ./support/man2html.o
make
make install
mv /bin/bash /bin/bash.bk_$(date "+%Y%m%e_%H%M%S_%s")
cp ./bash /bin/