2025 12 23 Odr Violations Dynamic Static Libraries
C++中多个动态库链接同一个静态库: 违反ODR导致double free 引言 最近在研究C++中单例模式, 发现网上有人提到这个问题. 记录一下探究的过程. google找到了这个能够复现该问题的帖子: Global variable in static library - double free or corruption error 复现流程如下: 静态库里定义一个全局变量, 该对象构造时分配内存, 析构时释放内存 有2个动态库都使用到了该变量, 动态库都链接了该静态库 主程序里调用2个动态库的函数, 运行时libc提示检测到了double free $ ./reproduce.sh Foo::Foo(int) begin: 0x7d15500f9048 0 Foo::Foo(int) end: 0x7d15500f9048 0x61720d3316c0 Foo::Foo(int) begin: 0x7d15500f9048 0x61720d3316c0 Foo::Foo(int) end: 0x7d15500f9048 0x61720d3316e0 void a(): 0x7d15500f9048 void b(): 0x7d15500f9048 Foo::~Foo(): 0x7d15500f9048 0x61720d3316e0 Foo::~Foo(): 0x7d15500f9048 0x61720d3316e0 free(): double free detected in tcache 2 ./reproduce.sh: line 8: 5622 Aborted (core dumped) LD_LIBRARY_PATH=. ./main.elf 测试环境 $ cat ./reproduce.sh #!/bin/env bash g++ ./foo.cpp -std=c++17 -O2 -g -fPIC -c -o foo.o ar rcs libfoo.a foo.o g++ ./a.cpp -std=c++17 -O2 -g -fPIC -shared -L. -lfoo -o liba.so g++ ./b.cpp -std=c++17 -O2 -g -fPIC -shared -L. -lfoo -o libb.so g++ ./main.cpp -std=c++17 -O2 -g -L. -la -lb -o main.elf LD_LIBRARY_PATH=. ./main.elf // foo.h #pragma once #include <iostream> class Foo { public: Foo(int val) { std::cout << __PRETTY_FUNCTION__ << " begin: " << this << " " << p_ << "\n"; p_ = new int(val); std::cout << __PRETTY_FUNCTION__ << " end: " << this << " " << p_ << "\n"; } Foo(Foo const &) = delete; Foo &operator=(Foo const &) = delete; ~Foo() noexcept { std::cout << __PRETTY_FUNCTION__ << ": " << this << " " << p_ << "\n"; delete p_; } private: int *p_; }; extern Foo foo; // foo.cpp #include "foo.h" Foo foo {0x666}; // a.cpp #include "foo.h" void a() { std::cout << __PRETTY_FUNCTION__ << ": " << &foo << "\n"; } // b.cpp #include "foo.h" void b() { std::cout << __PRETTY_FUNCTION__ << ": " << &foo << "\n"; } // main.cpp extern void a(); extern void b(); int main() { a(); b(); return 0; } 原因分析 分析输出的字符串, 可以看到里同一块内存调用了2次构造函数, 2次析构函数. 第2次构造函数产生了内存泄露, 第1次申请的内存不会被释放, 而第2次申请的内存会被反复释放, 导致了double free. ...
Windows Dev Kit 2023 Install Ubuntu Kernel 6.18.0
因为之前的Linux内核编译时没有启用CONFIG_ANON_VMA_NAME, 不支持PR_SET_VMA_ANON_NAME. 所以想要再安装一个ubuntu官方编译的内核. 24年初给Windows dev kit 2023安装ubuntu时基本是按照这篇教程Windows Dev Kit 2023 Bootup瞎折腾的. 升级ubuntu官方构建的arm64 linux内核也遇到了一些问题, 根据回忆记录一下解决过程, 可能会有疏漏. 因为我不太熟悉linux编译与安装, 很多解决方案都是问的ai大模型, 能跑就行… 下载deb包 从Ubuntu Mainline Kernel PPA下载下面的deb包, 安装它们 $ ls linux-*.deb linux-headers-6.18.0-061800_6.18.0-061800.202511302339_all.deb linux-image-unsigned-6.18.0-061800-generic_6.18.0-061800.202511302339_arm64.deb linux-headers-6.18.0-061800-generic_6.18.0-061800.202511302339_arm64.deb linux-modules-6.18.0-061800-generic_6.18.0-061800.202511302339_arm64.deb 修改/etc/default/grub: GRUB_DEFAULT=saved GRUB_TIMEOUT_STYLE=menu GRUB_TIMEOUT=30 更新grub sudo update-grub grub选择新内核后自动跳回grub界面 猜测可能是ubuntu官方的arm64 deb没有提供dtbs, 先使用旧内核启动系统. 手动下载6.18源码,编译dtbs tar xf ~/Downloads/linux-6.18.tar.xz cd linux-6.18/ cp /boot/config-6.18.0-061800-generic .config make oldconfig make ARCH=arm64 dtbs 拷贝编译好的dtbs sudo cp -r arch/arm64/boot/dts/* /boot/dtbs/6.18.0-061800-generic cd /boot/ sudo ln -s dtbs/6.18.0-061800-generic/qcom/sc8280xp-microsoft-blackrock.dtb dtb-6.18.0-061800-generic 启动新内核后gnome登录后自动跳回登录界面 通过tty登录, 查看dmesg,发现报错提示找不到mbn文件. ...
f() vs f(void)
读完f() vs f(void) in C vs C++做的笔记. C中f()与f(void)区别 f()表示函数形参列表是未知的, 编译器不会帮你做检查. 可以看到gcc编译下面程序, 没有警告. $ cat test.c #include <stdio.h> void foo() { printf("foo\n"); } int main(void) { foo(1, 2, 3); } $ gcc ./test.c -std=c11 -O2 -Wall -Wextra -Werror -o test $ ./test foo f(void)才真正表示函数形参列表为空. $ cat test.c #include <stdio.h> void foo(void) { printf("foo\n"); } int main(void) { foo(1, 2, 3); } $ gcc ./test.c -std=c11 -O2 -Wall -Wextra -Werror -o test ./test.c: In function ‘main’: ./test.c:8:3: error: too many arguments to function ‘foo’ 8 | foo(1, 2, 3); | ^~~ ./test.c:3:6: note: declared here 3 | void foo(void) { | ^~~ 另外X86-64生成的机器指令也会受到影响. 可以看到下面的程序能够正常执行, faz与baz分别调用foo以及bar. ...