liaozhicai 2024-05-18 10:53 采纳率: 20%
浏览 11

在ld.so的__libc_csu_init 函数里跑飞了

context:
OS:Linux saturn2-sfu-eng 4.14.172.saturn2-sfu-r2.2.1.3 #1 Sat May 11 08:47:16 UTC 2024 mips GNU/Linux
arch:mips 32

进程加载时在ld.so的LIBC_START_MAIN函数里跑飞了,概率性,加打印,显示没进__libc_csu_init 函数,但在__libc_csu_init 函数之前加的打印有输出,在其后加的打印没有输出。
另外,诡异的是在__libc_csu_init 里只要一加printf或者别的什么语句,问题就不复现了。
在__libc_csu_init 之前加打印,输出__libc_csu_init 函数在内存中的机器码,加上就复现不了。

没有GDB等手段,通过ps或者ls /proc查不到该进程。

该如何调试?
在glibc官方库issue系统中report了bug,但回复说不相干。
https://sourceware.org/bugzilla/show_bug.cgi?id=31732
代码如下:


```c
    if ((pid = fork()) < 0) 
    {
        ASSERT(0);
    }

    // In child process
    if (0 == pid) 
    {
        setpriority(0, 0, (INT32)priority - 20);
        sprintf_s(priStr, sizeof(priStr), "%d", priority);
        sprintf_s(stackStr, sizeof(stackStr), "%d", stackSize);
        paraList[0] = (CHAR*)execFileName;
        paraList[1] = "-p";
        paraList[2] = priStr;
        paraList[3] = "-s";
        paraList[4] = stackStr;
        paraList[5] = NULL;
        execve(execFileName, paraList, env);
    }


  • 写回答

4条回答 默认 最新

  • 专家-赤兔[在线] 优质创作者: 编程框架技术领域 2024-05-18 10:53
    关注

    引自免费微信小程序:皆我百晓生

    针对你的问题,虽然你提到没有GDB这样的调试工具,但是我们仍然可以通过一些基本的方法来尝试定位问题。首先,我会提供一种基于日志记录的方案,然后介绍如何利用内核追踪机制kprobes进行分析。虽然这些方法可能不如GDB直观,但它们可以在没有高级调试工具的情况下提供线索。

    方案一:增强日志记录

    1. 在关键位置增加详细的日志信息,包括函数调用栈、局部变量值等。这将有助于理解程序执行流程。

      示例代码(添加到相关函数):

      void log_info(const char *msg, ...) {
          va_list args;
          va_start(args, msg);
          vfprintf(stderr, msg, args);
          va_end(args);
          fflush(stderr);
      }
      
      // 在__libc_csu_init前后添加日志
      static int __libc_csu_init(int argc, char **argv, char **envp) {
          log_info("Entering __libc_csu_init\n");
          ...
      }
      
    2. 运行程序并观察日志输出。由于你提到了printf会影响问题的重现,可以考虑使用其他方式输出日志,例如写入文件。

    方案二:使用kprobes

    如果你的内核支持kprobes,这是一种强大的内核级动态追踪工具,可以让你在运行时插入探针到特定的内核地址,比如__libc_csu_init函数的入口点。kprobes允许你在函数执行前后执行用户空间提供的回调函数,从而获取更多的上下文信息。

    以下是使用kprobes的基本步骤:

    1. 编写一个简单的内核模块,用于安装kprobe探针。注意你需要对内核编程有一定的了解,下面是一个简化的示例:

      #include <linux/module.h>
      #include <linux/kprobes.h>
      
      static struct kprobe kp;
      
      static void probe_handler(struct kprobe *kp, struct pt_regs *regs)
      {
          printk(KERN_INFO "Entered %s\n", kp->symbol->name);
          // 可以在这里添加额外的日志或数据收集逻辑
      }
      
      static int __init my_module_init(void)
      {
          struct module *owner = THIS_MODULE;
      
          kp.symbol = NULL; // 地址由下面的__get_address取得
          kp.addr = (unsigned long)__get_address("__libc_csu_init"); // 获取__libc_csu_init地址
          kp.entry_type = KPROBE_ENTRY;
          kp.handler = probe_handler;
          kp.flags = 0;
          kp.data_size = 0;
          kp.maxactive = 1;
          kp.group_leader = NULL;
          kp.next = NULL;
          kp.prev = NULL;
      
          if (register_kprobe(&kp)) {
              printk(KERN_ERR "Failed to register kprobe\n");
              return -EIO;
          }
      
          return 0;
      }
      
      static void __exit my_module_exit(void)
      {
          unregister_kprobe(&kp);
      }
      
      module_init(my_module_init);
      module_exit(my_module_exit);
      
      MODULE_LICENSE("GPL");
      
    2. 将上述模块编译为.ko文件,并将其加载到内核中。确保你的内核版本支持kprobes并且已经启用相应的配置选项。编译模块命令示例:

      make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
      
    3. 加载模块:

      insmod your_module_name.ko
      
    4. 启动你的应用程序并查看系统消息(dmesg或/var/log/kern.log),查找与探针相关的日志输出。

    5. 当问题发生时,通过kprobes日志来分析程序的行为。

    **注意:**以上方法可能会因为内核版本、硬件架构等因素有所不同。对于不熟悉的读者,建议先学习相关的内核编程知识,以便更好地理解和实施这些方法。同时,如果你有更具体的环境信息,我可以提供更针对性的建议。

    评论 编辑记录

报告相同问题?

问题事件

  • 修改了问题 5月18日
  • 创建了问题 5月18日