gun工具和自己写库
🔧 一、.so 动态库:从源码 → 编译 → 使用 完整闭环
1️⃣ 目录结构(从空白目录开始)
创建如下目录结构:
mkdir -p demo/{lib,include,src,app}
tree demo # 查看结构(可选)
目录用途:
-
lib/:存放编译出的.so动态库,如libcalc.so -
include/:存放头文件,如calc.h -
src/:存放库的源代码,如calc.c -
app/:存放调用该库的应用程序,如main.c
2️⃣ 编写库的源码
📄 src/calc.c
#include "calc.h"
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
📄 include/calc.h
#ifndef CALC_H
#define CALC_H
int add(int a, int b);
int sub(int a, int b);
#endif
3️⃣ 编译为位置无关的动态库 .so
进入项目根目录,执行:
cd demo
gcc -fPIC -shared src/calc.c -Iinclude -o lib/libcalc.so
✅ 验证编译结果:
file lib/libcalc.so # 应输出 "shared object" 字样
nm -D lib/libcalc.so # 应能看到 add 和 sub 符号
4️⃣ 编写调用动态库的程序
📄 app/main.c
#include <stdio.h>
#include "calc.h"
int main(void) {
printf("10 + 5 = %d\n", add(10, 5));
printf("10 - 5 = %d\n", sub(10, 5));
return 0;
}
5️⃣ 编译应用程序并运行(多种方式)
进入 app 目录:
cd app
▶️ 方式①:使用 -L 和 -l 并通过 LD_LIBRARY_PATH 指定库路径
gcc main.c -I../include -L../lib -lcalc -o calc_demo
# 运行(临时指定库路径)
LD_LIBRARY_PATH=../lib ./calc_demo
▶️ 方式②:使用 rpath 打包库路径到可执行文件(推荐)
gcc main.c -I../include -L../lib -Wl,-rpath,'$ORIGIN/../lib' -lcalc -o calc_demo2
# 直接运行,无需每次设置环境变量
./calc_demo2
💡 注:
$ORIGIN表示可执行文件所在目录,确保../lib/libcalc.so路径正确。
▶️ 方式③:将 .so 安装到系统库路径(需 root)
sudo cp ../lib/libcalc.so /usr/local/lib/
sudo ldconfig
# 然后直接运行(无需设置路径)
./calc_demo
6️⃣ 运行时手动加载 .so(使用 dlopen)
📄 app/dlopen_demo.c
#include <dlfcn.h>
#include <stdio.h>
int main(void) {
void *h = dlopen("../lib/libcalc.so", RTLD_LAZY);
if (!h) {
fprintf(stderr, "dlopen error: %s\n", dlerror());
return 1;
}
int (*add)(int, int) = dlsym(h, "add");
int (*sub)(int, int) = dlsym(h, "sub");
char *err;
if ((err = dlerror()) != NULL) {
fprintf(stderr, "dlsym (add/sub) err: %s\n", err);
dlclose(h);
return 1;
}
if (!add || !sub) {
fprintf(stderr, "dlsym returned NULL for add/sub\n");
dlclose(h);
return 1;
}
printf("dlopen: 7 + 3 = %d\n", add(7, 3));
printf("dlopen: 7 - 3 = %d\n", sub(7, 3));
dlclose(h);
return 0;
}
▶️ 编译并运行:
gcc dlopen_demo.c -ldl -o dlopen_demo
LD_LIBRARY_PATH=../lib ./dlopen_demo
⚙️ 二、KVM 内核模块:从源码 → 编译 → 加载 → 验证 完整闭环
✅ 适用场景:你手头有 打过补丁的 KVM 源码目录(比如
kvm/或kvm-next),想编译并替换系统自带 kvm 模块。
1️⃣ 安装编译依赖
sudo apt update
sudo apt install build-essential linux-headers-$(uname -r) \
flex bison libssl-dev libelf-dev git bc
2️⃣ 获取 KVM 源码
方法①:从官方仓库克隆(推荐)
git clone git://git.kernel.org/pub/scm/virt/kvm/kvm.git
cd kvm
你也可以解压你自己修改过的源码包,进入对应目录即可。
3️⃣ 复用当前系统内核配置(推荐)
cp /boot/config-$(uname -r) .config
make olddefconfig # 一路回车,保持默认选项
可选:如你只想快速编译 kvm 相关部分,而不是整个内核,可跳过冗余编译。
4️⃣ (可选)仅编译 kvm 目录对象文件(快速验证用)
make kvm/kvm.o
5️⃣ 编译 KVM 模块(重点!)
make -C /lib/modules/$(uname -r)/build M=$(pwd)/arch/x86/kvm modules
🔍 编译成功后,将在以下路径生成 .ko内核模块文件:
arch/x86/kvm/kvm.ko
arch/x86/kvm/kvm-intel.ko # Intel CPU
arch/x86/kvm/kvm-amd.ko # AMD CPU
✅ 你只需要关注当前 CPU 对应的模块,比如 Intel 用
kvm-intel.ko
6️⃣ 卸载旧模块,加载新编译的模块(必须 root)
⚠️ 如果你正在运行虚拟机,请先关闭!
sudo rmmod kvm_intel kvm_amd kvm # 先卸载当前模块
然后加载新的:
# 通用核心模块
sudo insmod arch/x86/kvm/kvm.ko
# 根据你的 CPU 类型选择加载:
sudo insmod arch/x86/kvm/kvm-intel.ko # Intel CPU
# 或者
sudo insmod arch/x86/kvm/kvm-amd.ko # AMD CPU
7️⃣ 验证是否加载成功
lsmod | grep kvm
# 应该能看到 kvm、kvm_intel 或 kvm_amd,并且版本是你刚刚编译的
dmesg | tail -n 20
# 查看内核日志,可能会看到类似 "kvm: module loaded" 以及自定义信息
如果你打了补丁,可能 dmesg 中会有你的自定义打印信息,可用来验证是否为新模块。
8️⃣ (可选)永久替换系统模块
如果你希望每次启动都使用你编译的 KVM 模块,而不是系统自带的:
# 复制编译好的 .ko 文件到系统模块目录
sudo cp arch/x86/kvm/*.ko /lib/modules/$(uname -r)/kernel/arch/x86/kvm/
# 更新模块依赖
sudo depmod -a
# 设置开机自动加载(创建配置文件)
echo -e "kvm\nkvm-intel" | sudo tee /etc/modules-load.d/kvm.conf
# 重启系统
sudo reboot
重启后检查:
modinfo kvm # 查看 vermagic 是否与你当前内核一致
lsmod | grep kvm
9️⃣ (可选)用 QEMU/Libvirt 启动虚拟机验证功能正常
安装必要组件:
sudo apt install qemu-kvm libvirt-daemon-system
查看虚拟机列表:
virsh list --all
创建并启动一个测试虚拟机(示例命令,可根据需求调整):
virt-install --name test --memory 1024 --vcpus 1 --disk none --boot cdrom --print-xml
只要能正常创建并启动虚拟机,就说明你的 KVM 模块工作正常 ✅
✅ 总结
|
功能 |
关键命令/步骤概要 |
|---|---|
|
.so 动态库 |
|
|
KVM 模块 |
|
GNU Binutils 工具速查表,涵盖 nm、objdump、readelf、strip、ar、ranlib、size、strings、addr2line、gdb 等最常用工具,按 “一句话作用 + 最常用命令选项 + 典型输出示例” 三栏清晰归纳,适合日常开发、逆向分析、二进制优化与调试快速查阅。
🛠 GNU Binutils 常用工具速查表(复制即用)
|
工具 |
一句话作用 |
最常用命令 & 必会选项 |
典型输出示例 |
|---|---|---|---|
|
nm |
列出目标文件/库中的符号(函数、变量、地址) |
|
|
|
objdump |
反汇编二进制、查看段信息、重定位表等 |
|
|
|
readelf |
查看 ELF 文件的详细结构(比 objdump 更细致) |
|
|
|
strip |
删除符号表和调试信息,减小二进制体积 |
|
文件体积显著变小( |
|
ar |
将多个 .o 目标文件打包成静态库 .a |
|
生成静态库文件: |
|
ranlib |
为静态库生成索引,加快链接速度(通常 ar rcs 已包含) |
|
无输出,但后续链接更快 |
|
size |
查看二进制各段(text/data/bss)占用空间大小 |
|
|
|
strings |
提取二进制中可打印的字符串(常用于找线索/错误信息) |
|
|
|
addr2line |
将地址转换为源码文件及行号(需编译时加 -g) |
|
|
|
gdb |
GNU 调试器,支持源码级调试、断点、回溯等 |
|
源码级调试界面,支持逐行执行、查看变量、回溯调用栈等 |
🧪 速查示例脚本(一键运行体验所有工具)
以下脚本假设你已有一个
main.c源文件,且用gcc -g编译为带调试信息的demo可执行文件。
# 0. 编译一个带调试信息的可执行文件(如没有可自行准备)
gcc -g main.c -o demo
# 1. 查看符号表(找到 main 函数地址等信息)
nm -C demo | grep main
# 2. 反汇编,查看 main 函数汇编代码(Intel 语法)
objdump -d -M intel demo | grep -A5 '<main>'
# 3. 查看 ELF 文件的段表结构
readelf -S demo | head
# 4. 二进制瘦身:查看原大小 -> strip 后大小对比
ls -lh demo
strip -s demo
ls -lh demo # 通常体积明显减小
# 5. 提取二进制中的字符串(比如查找隐藏的提示、错误信息等)
strings demo | grep -i error
# 6. 地址转源码行号(需确保编译带 -g,地址可从 crash log 或 objdump 中获取)
addr2line -e demo 0x401136
💡 一句话总结速记
nm 看符号,objdump 读指令,readelf 读 ELF,strip 给二进制减肥,ar 打包静态库,ranlib 加速链接,size 查段大小,strings 挖字符串,addr2line 地址反推源码,gdb 调试神器 —— 其它工具按需查表即可!
🔧 本速查表适用于:
-
🔍 二进制分析、逆向初探
-
🛠 编译优化、瘦身、发布前处理
-
🧩 调试崩溃、地址解析、符号查询
-
📦 静态库打包与链接优化
-
🧠 Linux 下 C/C++ 开发必备工具快速上手
浙公网安备 33010602011771号