alpine linux 上的程序段错误。我该如何解决?
Program segfaults on alpine linux. How do I resolve it?
我一直在使用 C/C++ 和 wrote a program 使用 C 编写 webrtc 数据通道库,目的是:
- 从同一进程创建两个对等点。
- 建立他们之间的联系。
- 如果成功,请关闭连接。
在 debian docker 容器和我的主机 opensuse tumbleweed(所有 x86_64 和 64 位)上一切正常,但在 alpine linux 容器(64 位 x86_64 ), 我在子进程中遇到了 SEGFAULT:
以上函数来自程序的依赖"libnice"。似乎 *agent == NULL 并且无法在 caller's 范围内将其设为 null。我什至在函数调用之前插入了一个 printf("Argument is %p", agent);
并打印出它的内存,我可以验证它不为空。从反汇编来看,它看起来像是将代理的内容 (0x557a1d20) 复制为被调用者堆栈中的局部变量导致段错误的行。即使在 make clean
和重新编译之后,段错误也始终发生在这一点上。激活记录失败?堆栈损坏?
更新:我制作了一个更轻量级的容器并 运行 它,现在它在同一个 priv_conn_keepalive_tick_unlocked
的不同位置出现段错误。虽然参数似乎已设置(注意 0x7ffff7f9ad08):
因为我认为我可能会达到 libmusl's 默认堆栈限制 80k,所以我使用 getrlimit(RLIMIT_STACK, &rl)
来获取堆栈大小,看起来它已经是 8 MB 而不是 80k。进一步增加此限制似乎没有任何区别,除非我分配超过 8 MB,我的程序会在 Gdb 中 提前崩溃。 Gdb 说它收到了一个未知信号“??”;在 gdb 之外,它会在正常点崩溃,在正常情况下它通常会在没有更改堆栈大小的情况下崩溃。
我不确定问题到底是什么(堆栈损坏?)以及下一步该怎么做才能解决这个问题。
这是我的程序流程:
对于创建的每个对等节点,都会使用 fork() 创建子 进程。父 <--> 子通信由 ZeroMQ 完成,我使用协议缓冲区将子内部触发的任何回调(及其参数)转发到父进程中的事件循环 运行。
所以对于上面的程序,有2个子进程和1个父进程。
重现步骤:
- 源文件:https://github.com/hamon-in/librtcdcpp/blob/alpine-test/examples/websocket_client/2in1.c
- Alpine docker 容器:https://github.com/hamon-in/librtcdcpp/blob/alpine-test/Dockerfile.amd64
- 运行 容器和二进制文件位于
/psl-librtcdcpp/examples/websocket_client/2in1
- 2in1 将生成两个子进程,这两个子进程都会崩溃。
将 -Werror=implicit-function-declaration
添加到您的 CFLAGS 中,您将立即找到原因。关键线索是指针值 0x557a1d20,这几乎可以肯定是将指针截断为 32 位的结果。当您未能声明一个 returns 指针的函数并且编译器(通过可怕的向后默认)假定它 returns int 而不是产生错误,然后随后允许从 int 隐式转换为尽管 C 语言不允许指针。
经进一步调查,崩溃发生在一条指令写入距离堆栈基址指针稍大的负偏移处,因此它可能只是一个简单的堆栈溢出。
解决此问题的正确方法是减少过多的堆栈使用或在 pthread_create
时显式请求大堆栈,但我看不到从何处调用 pthread_create
。快速检查以验证这是问题所在,方法是通过在程序早期的某处执行以下操作来覆盖新线程的默认堆栈大小:
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 1<<20); // 1 MB
pthread_setattr_default_np(&attr);
我一直在使用 C/C++ 和 wrote a program 使用 C 编写 webrtc 数据通道库,目的是:
- 从同一进程创建两个对等点。
- 建立他们之间的联系。
- 如果成功,请关闭连接。
在 debian docker 容器和我的主机 opensuse tumbleweed(所有 x86_64 和 64 位)上一切正常,但在 alpine linux 容器(64 位 x86_64 ), 我在子进程中遇到了 SEGFAULT:
以上函数来自程序的依赖"libnice"。似乎 *agent == NULL 并且无法在 caller's 范围内将其设为 null。我什至在函数调用之前插入了一个 printf("Argument is %p", agent);
并打印出它的内存,我可以验证它不为空。从反汇编来看,它看起来像是将代理的内容 (0x557a1d20) 复制为被调用者堆栈中的局部变量导致段错误的行。即使在 make clean
和重新编译之后,段错误也始终发生在这一点上。激活记录失败?堆栈损坏?
更新:我制作了一个更轻量级的容器并 运行 它,现在它在同一个 priv_conn_keepalive_tick_unlocked
的不同位置出现段错误。虽然参数似乎已设置(注意 0x7ffff7f9ad08):
因为我认为我可能会达到 libmusl's 默认堆栈限制 80k,所以我使用 getrlimit(RLIMIT_STACK, &rl)
来获取堆栈大小,看起来它已经是 8 MB 而不是 80k。进一步增加此限制似乎没有任何区别,除非我分配超过 8 MB,我的程序会在 Gdb 中 提前崩溃。 Gdb 说它收到了一个未知信号“??”;在 gdb 之外,它会在正常点崩溃,在正常情况下它通常会在没有更改堆栈大小的情况下崩溃。
我不确定问题到底是什么(堆栈损坏?)以及下一步该怎么做才能解决这个问题。
这是我的程序流程:
对于创建的每个对等节点,都会使用 fork() 创建子 进程。父 <--> 子通信由 ZeroMQ 完成,我使用协议缓冲区将子内部触发的任何回调(及其参数)转发到父进程中的事件循环 运行。
所以对于上面的程序,有2个子进程和1个父进程。
重现步骤:
- 源文件:https://github.com/hamon-in/librtcdcpp/blob/alpine-test/examples/websocket_client/2in1.c
- Alpine docker 容器:https://github.com/hamon-in/librtcdcpp/blob/alpine-test/Dockerfile.amd64
- 运行 容器和二进制文件位于
/psl-librtcdcpp/examples/websocket_client/2in1
- 2in1 将生成两个子进程,这两个子进程都会崩溃。
将 -Werror=implicit-function-declaration
添加到您的 CFLAGS 中,您将立即找到原因。关键线索是指针值 0x557a1d20,这几乎可以肯定是将指针截断为 32 位的结果。当您未能声明一个 returns 指针的函数并且编译器(通过可怕的向后默认)假定它 returns int 而不是产生错误,然后随后允许从 int 隐式转换为尽管 C 语言不允许指针。
经进一步调查,崩溃发生在一条指令写入距离堆栈基址指针稍大的负偏移处,因此它可能只是一个简单的堆栈溢出。
解决此问题的正确方法是减少过多的堆栈使用或在 pthread_create
时显式请求大堆栈,但我看不到从何处调用 pthread_create
。快速检查以验证这是问题所在,方法是通过在程序早期的某处执行以下操作来覆盖新线程的默认堆栈大小:
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 1<<20); // 1 MB
pthread_setattr_default_np(&attr);