【问题标题】:Using a function pointer to pthread_create causes segfault使用指向 pthread_create 的函数指针会导致段错误
【发布时间】:2016-10-26 21:54:36
【问题描述】:

我正在尝试删除 pthread_create 以便能够对模块进行完全单元测试。当从测试框架内调用函数指针时,会发生分段错误。如果我使用“gdb”调试程序,我可以直接调用函数指针并且它可以正常工作。

我使用 CppUTest 作为单元测试框架,并使用 gcc 编译了我的目标文件。

这个函数在修改它以使用 pthread_create 的函数指针之前已经在生产代码中工作过,所以我对这个函数很有信心。

来自 GDB 的堆栈跟踪

> Starting program:
> /home/lucid/depot/torr_linux_common_dev/main/src/Utilities/tests/testRunner
> [Thread debugging using libthread_db enabled] Using host libthread_db
> library "/lib/i386-linux-gnu/libthread_db.so.1".
> 
> Program received signal SIGSEGV, Segmentation fault. 0x080660c4 in
> sys_pthreads_create () (gdb) backtrace
> #0  0x080660c4 in sys_pthreads_create ()
> #1  0x08049ee4 in th_start_thread_name (thread=0x8049e64 <TestThread>, arg=0x0, opts=0x0, name=0x0) at thr.c:177
> #2  0x08049e47 in test_ThreadTestGroup_ThreadCreateUnnamed_wrapper_c () at thr_test.c:66
> #3  0x08049223 in TEST_ThreadTestGroup_ThreadCreateUnnamed_Test::testBody
> (this=0x806cc90) at testRunner.c:21
> #4  0x0805576a in PlatformSpecificSetJmpImplementation ()
> #5  0x08053ab7 in Utest::run() ()
> #6  0x080550d5 in UtestShell::runOneTestInCurrentProcess(TestPlugin*, TestResult&) ()
> #7  0x08053645 in helperDoRunOneTestInCurrentProcess ()
> #8  0x0805576a in PlatformSpecificSetJmpImplementation ()
> #9  0x08053b8f in UtestShell::runOneTest(TestPlugin*, TestResult&) ()
> #10 0x080530ef in TestRegistry::runAllTests(TestResult&) ()
> #11 0x0804a3ef in CommandLineTestRunner::runAllTests() ()
> #12 0x0804a4e9 in CommandLineTestRunner::runAllTestsMain() ()
> #13 0x0804a628 in CommandLineTestRunner::RunAllTests(int, char const**) ()
> #14 0x08049246 in main (argc=1, argv=0xbffff244) at testRunner.c:25

如果我从 gdb 中调用函数指针,它就可以工作

(gdb) p (*sys_pthreads_create)(&thr, 0, thread, arg)
[New Thread 0xb7c01b40 (LWP 17717)]
$4 = 0

我正在测试的功能

#include <pthread.h>
#include "mypthreads.h"
long th_start_thread_name(TH_THREAD_FUNC thread, void *arg, th_opts *opts, const char* name)
{
    pthread_t thr;
    int ret, sret;
    //pthread_create(opts ? &opts->thr : &thr, NULL, thread, arg);
    ret = (*sys_pthreads_create)(opts ? &opts->thr : &thr, 0, thread, arg);
    if (ret == 0 && name != NULL)
    {
       extern int pthread_setname_np(pthread_t thr, const char *name);  /* Fix warning from missing prototype. */

       sret = pthread_setname_np(opts ? opts->thr : thr, name);
       /* pthreads says that thread names must not exceed 16, including NULL. */
       if (sret != 0 && strlen(name) > 15)
       {
           ret = -1;
       }
    }
    return (long)ret;
}

mypthreads.h

extern int (*sys_pthreads_create(pthread_t *, const pthread_attr_t *,
                             void *(*) (void*), void *));

mypthreads.c

#include <stdio.h>
#include <pthread.h>

int my_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg)
{
    printf("Did you get the messsage?");
    return pthread_create(thread, attr, start_routine, arg);
}


int (*sys_pthreads_create)(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg) = my_pthread_create;

编辑:当我调用函数指针并成功时,添加了 gdb 的输出。

【问题讨论】:

  • 观察你的调试器,你甚至没有输入my_pthread_create。也许你以某种方式隐藏了你的函数指针并且在你调用的副本中有垃圾。
  • 考虑使用调试信息构建代码,并可能禁用优化。然后 Gdb 应该能够为您提供有关该错误的更多信息。另请注意,在 gdb 捕获 segfault 后,您仍然可以检查堆栈——调试信息也会对此有所帮助。
  • @Jens Gusted" 是的,我知道 my_pthread_create 从未被调用过。如果我从 gdb 中执行“p (*sys_pthreads_create)(&thr, 0, thread, arg)”,则调用函数指针正确,gdb 显示线程已生成。
  • 在您的构建中启用调试符号。这样你就可以准确地看到是哪个内存访问导致了问题
  • @kaylum 我启用了调试符号,但除此之外无法理解。

标签: c unit-testing pthreads function-pointers stub


【解决方案1】:

问题是您在mypthreads.h 中的声明类型错误:

extern int (*sys_pthreads_create(pthread_t *, const pthread_attr_t *, void *(*) (void*), void *));

由于括号放错了位置,这个符号的类型是一个返回指向int的指针的函数,但你的实际sys_pthreads_create对象是一个指向函数的指针。

这意味着当你调用时:

ret = (*sys_pthreads_create)(opts ? &opts->thr : &thr, 0, thread, arg);

sys_pthreads_create 通过隐式获取函数地址转换为指向函数的指针,然后取消引用并调用该地址。但这并不是真正的函数地址——它是指向函数的指针的地址!因此,调用会跳转到 sys_pthreads_create 所在的数据段,并在尝试将函数指针作为代码执行时崩溃(或由于不可执行的映射而崩溃)。

在 gdb 输出中有一个线索:

#0  0x080660c4 in sys_pthreads_create ()

它说它在 sys_pthreads_create 内执行 - 但 sys_pthreads_create 是一个变量,而不是一个函数。

如果您在mypthreads.c 中包含&lt;mypthreads.h&gt;,编译器会为您诊断,因为sys_pthreads_create 的冲突类型对它是可见的(这就是为什么您应该始终包含声明对象的头文件在定义这些对象的源文件中)。

正确的声明当然是匹配mypthreads.c

extern int (*sys_pthreads_create)(pthread_t *thread, const pthread_attr_t *attr,
                      void *(*start_routine) (void *), void *arg);

gdb能够成功调用函数指针的原因是gdb使用调试信息中存储的类型信息来确定sys_pthreads_create的类型,而不是头文件中的虚假信息。

【讨论】:

    猜你喜欢
    • 2018-10-19
    • 2018-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 1970-01-01
    相关资源
    最近更新 更多