(done) 解析 qemu-6.0.0 源码 (2) 寻找入口函数 main

寻找 ./output/qemu/bin/qemu-system-riscv64 的入口函数 main

首先,这个时代最好还是让 AI 帮忙做软件工程分析,没有必要再去看构建系统这些东西。

毕竟构建系统换了一代又一代 (Makefile, cmake, ninja, meson ...),我们还是最好关注一些原理、设计哲学的东西,而不是盯着构建系统不放。

推荐的顺序是:

1.先让AI帮你找入口代码

2.让AI在找的同时,把可复现的步骤依据给你,大概看看就行,不需要全记住甚至不用复现(要确认AI的结论对不对加个 printf("here"); exit(0); 就行)

3.等待AI确实找错,且多尝试几遍都没用的时候,再去钻研这个构建系统、钻研那些古老的 trick

不过,为了保持严谨度,这里我们还是复现一遍古法 trick 寻找入口函数 main 的流程。

古法软件工程 trick 开始 <------- !!!

首先,看 build.sh:

bash 复制代码
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)

cd qemu-6.0.0
if [ ! -d "$SHELL_FOLDER/output/qemu" ]; then
./configure --prefix=$SHELL_FOLDER/output/qemu  --target-list=riscv64-softmmu --enable-gtk  --enable-virtfs --disable-gio
fi
make -j16
make install
cd ..

可以看到有 make -j16 和 make install

根据经验 (是的,根据一种非常普遍的开源项目非成文惯例),同时有 make -j16 和 make install 说明 ./output/qemu/bin/qemu-system-riscv64 不是原始构建产物,原始构建产物在其它地方,./output/qemu/bin/qemu-system-riscv64 是被处理后放在这里的。

通常,原始构建产物包含调试信息,./output/qemu/bin/qemu-system-riscv64 这种最终构建产物不包含。

可以使用

bash 复制代码
find . -name "qemu-system-riscv64"

去找原始构建产物,实际上很容易找到,在 qemu-6.0.0/build/qemu-system-riscv64。

运行

bash 复制代码
file qemu-6.0.0/build/qemu-system-riscv64 \
     output/qemu/bin/qemu-system-riscv64

会发现 qemu-6.0.0/build/qemu-system-riscv64 是 with debug_info, not stripped

而 output/qemu/bin/qemu-system-riscv64 是 stripped

也就是前者带调试信息,后者不带。

接下来,使用

bash 复制代码
readelf -h qemu-6.0.0/build/qemu-system-riscv64 (output/qemu/bin/qemu-system-riscv64 也行,一样的) \
    | grep -E 'Type:|Machine:|Entry point'

得到:

复制代码
  Type:                              DYN (Position-Independent Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Entry point address:               0x2bf900

也就是程序入点是 0x2bf900,当然了,这是虚拟地址空间的地址。
对了,这里有个有趣的地方:你会发现 qemu-system-riscv64 的 Type 是 PIE。 根据我的理解,PIE 汇编指令会拖慢性能。Linux 本身为应用层程序提供了独立地址空间,按理来说不需要 PIE,直接 ET_EXEC 就行了,可以避免 PIE 带来的性能开销。可这里却把 qemu-system-riscv64 编译成了 PIE 类型,为什么呢? 原因是为了防御 ROP (return-oriented programming) 攻击,现代操作系统一般会采用 ASLR 防御机制,应用程序普遍使用 PIE 类型是为了配合 ASLR 机制。

接下来运行下面的命令:

bash 复制代码
 nm -an qemu-6.0.0/build/qemu-system-riscv64 (这里如果用 output/qemu/bin/qemu-system-riscv64 会显示没有符号,因为已经被 stripped 了) \
    | grep -E ' (_start|main)$'

会得到结果:

复制代码
00000000002be650 T main
00000000002bf900 T _start

说明一开始执行的是 _start 程序。

执行:

bash 复制代码
objdump -d --disassemble=_start \
    qemu-6.0.0/build/qemu-system-riscv64

得到:

复制代码
    qemu-6.0.0/build/qemu-system-riscv64

qemu-6.0.0/build/qemu-system-riscv64:     file format elf64-x86-64


Disassembly of section .init:

Disassembly of section .plt:

Disassembly of section .plt.got:

Disassembly of section .plt.sec:

Disassembly of section .text:

00000000002bf900 <_start>:
  2bf900:       f3 0f 1e fa             endbr64
  2bf904:       31 ed                   xor    %ebp,%ebp
  2bf906:       49 89 d1                mov    %rdx,%r9
  2bf909:       5e                      pop    %rsi
  2bf90a:       48 89 e2                mov    %rsp,%rdx
  2bf90d:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  2bf911:       50                      push   %rax
  2bf912:       54                      push   %rsp
  2bf913:       45 31 c0                xor    %r8d,%r8d
  2bf916:       31 c9                   xor    %ecx,%ecx
  2bf918:       48 8d 3d 31 ed ff ff    lea    -0x12cf(%rip),%rdi        # 2be650 <main>
  2bf91f:       ff 15 c3 f6 ad 00       call   *0xadf6c3(%rip)        # d9efe8 <__libc_start_main@GLIBC_2.34>
  2bf925:       f4                      hlt

Disassembly of section .fini:

可以看到 _start 函数后面调用了 main 函数。

接下来运行下面的命令:

bash 复制代码
addr2line -e qemu-6.0.0/build/qemu-system-riscv64 (这里不能用 output/qemu/bin/qemu-system-riscv64,因为没符号) \
    -f -C 0x2be650

就能直接看到 main 符号来自具体哪个文件的第几行:

复制代码
qemu-6.0.0/build/../softmmu/main.c:48

稍微补充一下,qemu-6.0.0 构建系统里用到了 meson,所以实际上可以在 quard_star_tutorial 文件夹下执行:

bash 复制代码
meson introspect --installed qemu-6.0.0/build     | grep qemu-system-riscv64

会得到非常长的一行,往上翻一翻,会看到红色高亮的:

复制代码
"quard_star_tutorial/qemu-6.0.0/build/qemu-system-riscv64": 
"quard_star_tutorial/output/qemu/bin/qemu-system-riscv64"

说明这两者被 meson 映射了,前者是原始构建产物,后者是 make install 后的安装产物。

我们做个实验,在 qemu-6.0.0/softmmu/main.c : main 函数开头加个日志 "hello from MEEEE!!!"

运行 ./output/qemu/bin/qemu-system-riscv64 时成功打印上述日志,说明我们找对了。

到此,qemu-system-riscv64 的入口 main 已经被我们找到,就是 qemu-6.0.0/build/.../softmmu/main.c:48

相关推荐
ggaofeng19 天前
QEMU中硬盘用哪种协议读写速度快
qemu·磁盘类型
GKxx1 个月前
在 HarmonyOS 上给 QEMU 搭一个最小 aarch64 Linux guest(内核 + busybox initramfs)
linux·华为·qemu·harmonyos·鸿蒙·鸿蒙pc
k4m7v2pz2 个月前
在Arch Linux笔记本上搭建ReactOS 0.4.15虚拟机:从Mac远程开发调试Windows XP风格应用的完整指南
qemu·虚拟机·kvm·交叉编译·mingw-w64·reactos
冰山一脚20132 个月前
qemu设备监听虚拟机状态笔记
qemu
萧青山2 个月前
【信创实战】x86电脑运行麒麟ARM系统:QEMU虚拟化完全指南(含一键脚本、性能优化、AI部署)
qemu·信创·麒麟系统·arm虚拟化·ai部署
一个平凡而乐于分享的小比特3 个月前
QEMU:一台“用软件造出来的万能计算机”
qemu
凉、介3 个月前
KVM + QEMU 虚拟化
笔记·学习·嵌入式·arm·qemu·虚拟化·kvm
牛奶咖啡133 个月前
KVM虚拟化与企业应用实践——给远端主机创建虚拟机
云原生·qemu·kvm·给远端主机创建虚拟机·创建uefi模式的虚拟机·安装openeulersp2·vnc与虚拟机环境搭建
牛奶咖啡134 个月前
KVM虚拟化与企业应用实践——通过网络介质配合ks自动应答文件实现自动安装KVM虚拟机
云原生·qemu·kvm·系统网络引导与ks自动应答环境·远程资源+ks文件安装虚拟机·通过网络介质引导自动安装虚拟机·qemu的总线类型详解