【问题标题】:Descend/resolve user-space C function code down to kernel-space at compile time?在编译时将用户空间 C 函数代码下降/解析到内核空间?
【发布时间】:2013-09-18 07:50:54
【问题描述】:

我可能无法立即回过头来回答这个问题,但我想既然遇到了这个问题,我就会记下来:

首先,我不确定正确的称呼方式是什么;我在标题中尝试过“descend”和“resolve”,但我想知道是否有更合适的术语。本质上,我想要的是获得像我从Kernel System Calls - ar.linux.it 得到的这张图片所示的东西:

这是一个更具体的例子 - 考虑以下小而不工作(它只会导致“断言失败”),但可编译的 ALSA 代码:

// atest.c

#include <alsa/asoundlib.h>

static snd_pcm_t *playbck_pcm_handle;
static char wrsrcbuf[256] = {[0 ... 255] = 5}; // initialization gcc specific
static snd_pcm_uframes_t period_frames = 32;
static int ret;

int main() {

  ret = snd_pcm_writei(playbck_pcm_handle, wrsrcbuf, period_frames);

  return 0;

}

我可以用:

gcc -Wall -g atest.c -lasound -o atest

...然后,我可以使用objdump 观察组装:

$ objdump -d -M intel -S atest
...
int main() {
 ...

  ret = snd_pcm_writei(playbck_pcm_handle, wrsrcbuf, period_frames);
 804842d:       8b 15 40 a1 04 08       mov    edx,DWORD PTR ds:0x804a140
 ...
 8048447:       e8 0c ff ff ff          call   8048358 <snd_pcm_writei@plt>
 ...

  return 0;
 8048451:       b8 00 00 00 00          mov    eax,0x0

}
...

...这仅告诉我将调用一个子例程&lt;snd_pcm_writei@plt&gt; - 但它并没有告诉我,比如说,哪个库目标文件(旁注:给定编译通过,这是否意味着gcc会知道库的位置作为当前系统上的目标文件吗?)

然后,我原则上可以运行程序(虽然不是这个),并通过使用/sys/kernel/debug/tracing/trace中内置的Linux跟踪(ftrace)功能,获取运行时内核日志。由于内核的抢占性质和调度,我们不能真正指望执行顺序的任何恒定性,但原则上,我们可以得到这样的东西(虽然不是来自上面的例子,因为它没有任何适当的设备初始化):

sys_ioctl() {
  ...
  do_vfs_ioctl() {
    snd_pcm_playback_ioctl() {
      snd_pcm_playback_ioctl1() {
        _cond_resched();
        copy_from_user() {
          ...
        }
        snd_pcm_lib_write() {
          snd_pcm_lib_write1() {
            _raw_read_lock_irq();
            _raw_spin_lock();
            snd_pcm_update_hw_ptr() {
              snd_pcm_update_hw_ptr0() {
                azx_pcm_pointer() {
                ...

所以,这告诉我响应 snd_pcm_writei 命令 - 最终 sys_ioctl -> snd_pcm_playback_ioctl -> snd_pcm_lib_write 将被调用,这将是内核中内置的 ALSA 函数;但是,也会调用像 azx_pcm_pointer() 这样的函数,它们是设备驱动程序的一部分(azx_pcm_pointerhda-intel 驱动程序的一部分)。

所以我的问题是 - 是否有一个应用程序可以将程序的函数“下降”树从用户空间输出到内核空间 - 无论是在编译时(这将是 gcc 本身,但如果有一些特殊的开关它们是为此目的而存在的),还是后编译(比如使用objdump,但它不是“运行时”,因为分析的程序本身没有运行)?例如。对于这个例子,我希望输出如下:

int main() {  # atest
  ...
  ret = snd_pcm_writei(playbck_pcm_handle, wrsrcbuf, period_frames);  # atest
  ...
    <snd_pcm_writei@plt> # libasound.so ??
    ...
      sys_ioctl() {  # ???.(k)o?
      ...
        snd_pcm_playback_ioctl() { # ???.(k)o?
        ...
          azx_pcm_pointer() { # /lib/modules/.../sound/pci/hda/snd-hda-intel.ko
        ...
      ...

我了解代码可能采用多个代码路径 - 因此希望此工具能够全部解决它们 - 或允许设置变量,以限制代码路径的数量;但总的来说,输出将是一棵树(然后可以使用graphviz 来可视化)。

我也明白,如果不进入运行时解析驱动程序可能是不可能的(因为设备 - 及其驱动程序 - 可以在运行时由用户空间程序的命令行参数指定);但我希望至少有一个通知告诉我“这里将调用一个未指定的驱动程序函数”。

【问题讨论】:

    标签: c linux


    【解决方案1】:

    旁注:给定编译通过,这是否意味着 gcc 会知道库的位置作为当前系统上的目标文件?

    是的,这正是您必须指定 -lasound 的原因。这个函数是被链接器发现的。如果由于某些原因 libasound 没有它 - 你会得到链接错误。

    所以我的问题是 - 是否有一个应用程序可以将程序的函数“下降”树从用户空间输出到内核空间

    我没有听说过。这当然是可能的,但是用户空间和内核空间之间的转换远非仅仅是函数调用。事实上,用户空间库使用 syscall() 函数,它设置适当的系统调用号和参数到寄存器并发出特殊的 CPU 中断,内核捕获然后执行 - 所以 gcc 和任何目标代码解析工具都无法跟踪这个转换。我最好的猜测是转储用户和内核空间并在之后连接此日志,但这会很棘手。

    顺便问一下,你为什么要这个?

    【讨论】:

    • 非常感谢@keltar - 解释了为什么这种工具很难编写......我需要它,因为在我对 ALSA 的研究中,我对事情的实际情况感到相当惊讶最终在内核级别上工作,甚至忽略了先发制人的事情;所以我想要这种用于学习目的的工具 - 基本上以更易读的格式向我展示我原则上应该期望什么样的命令序列。干杯!
    猜你喜欢
    • 2017-07-25
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    • 2011-07-11
    • 2016-03-13
    • 2011-10-22
    • 1970-01-01
    相关资源
    最近更新 更多