打印给定进程的thread_info值

时间:2014-03-05 22:08:21

标签: linux gdb kernel

从给定进程的内存转储中,我想提取th​​read_info的值,例如: preempt_count。

在x86中定义:

struct thread_info {
    struct task_struct  *task;
    struct exec_domain  *exec_domain;
    __u32                flags;
    __u32                status;
    __u32                cpu;
    int                  preempt_count;
    mm_segment_t         addr_limit;
    struct restart_block restart_block;
    void __user         *sysenter_return;
#ifdef CONFIG_X86_32
    unsigned long        previous_esp;
    __u8                 supervisor_stack[0];
#endif
    int                  uaccess_err;
};

并驻留在流程堆栈的底部。

在x86上,可以通过屏蔽堆栈指针的13个最低有效位(假设堆栈大小为8KB)来获得thread_info结构的地址。这是通过current_thread_info()函数完成的(参见Love,“Linux内核开发”)。

我测试了一个带有pid 2419(我开始/ bin / bash)的进程,使用gdb获取stackpointer和python的值来获取内存地址:

$ gdb --pid 2419
(gdb) print $sp
$1 = (void *) 0xbf870fa8
(gdb) python print "%x" % (0xbf870fa8 & 0xffffe000)
bf870000

因此,thread_info应驻留在bf870000:

(gdb) x/40xb 0xbf870000
0xbf870000:     0x08    0x66    0x2d    0x0a    0x08    0x66    0x2d    0x0a
0xbf870008:     0x88    0x8f    0x08    0x0a    0xb8    0x18    0x07    0x08
0xbf870010:     0xe8    0xe6    0x60    0x0a    0x08    0x65    0x2d    0x0a
0xbf870018:     0x08    0x60    0x61    0x0a    0x88    0x8f    0x08    0x0a
0xbf870020:     0x08    0x66    0x2d    0x0a    0x88    0x8f    0x08    0x0a

问题是:如何将此数据连接到thread_info结构?

现在我可以将结构映射到内存,但是,我认为地址0xbf870000是错误的......

(gdb) symbol-file /usr/lib/debug/boot/vmlinux-3.2.0-52-generic-pae
Reading symbols from /usr/lib/debug/boot/vmlinux-3.2.0-52-generic-pae...done.
(gdb) p (struct thread_info *)0xbf870000
$2 = (struct thread_info *) 0xbf870000
(gdb) p *$2
$4 = {task = 0xa2d6608, exec_domain = 0xa2d6608, flags = 168333192, status = 134682808, cpu = 174122728, preempt_count = 170747144, addr_limit = {seg = 174153736}, restart_block = {
    fn = 0xa088f88, {futex = {uaddr = 0xa2d6608, val = 168333192, flags = 174153736, bitset = 134622139, time = 580707212407115656, uaddr2 = 0x1524}, nanosleep = {clockid = 170747400, 
        rmtp = 0xa088f88, expires = 578197684496719880}, poll = {ufds = 0xa2d6608, nfds = 168333192, has_timeout = 174153736, tv_sec = 134622139, tv_nsec = 168333192}}}, 
  sysenter_return = 0xbf87007c, previous_esp = 4294967264, supervisor_stack = 0xbf870044 "\274F\017\b\002", uaccess_err = 135218876}

感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

我相信你很困惑:

struct thread_info内核结构,驻留在内核 8K线程堆栈中。

然而,您希望通过屏蔽和转换用户空间堆栈指针,在用户空间堆栈中找到它。

您在用户空间中搜索的数据。如果是这样的话,用户空间程序可以自由地覆盖它并对内核造成各种各样的混乱。

答案 1 :(得分:1)

我找到了解决方案。雇用俄语是对的,东西在于内核空间。您可以使用stap(ubuntu上的apt-get install systemtap)来检查结构。

请注意,您可以将其用于任何进程以检查thread_info和task_struct。

这是我的工作流程:

  1. 启动shell并输入echo $$以获取shell的pid
  2. $> echo $$ 5296 2.创建订阅脚本thread_info.stp:

    #cf. /usr/share/doc/systemtap-doc/examples/process/dumpstack.stp
    
    // Add a dummy probe for loading kernel symbols.
    probe kernel.function("printk") { next }
    
    probe begin {
            process_pid = target()
    
            //pid2task(process_pid) defined in /usr/share/systemtap/tapset/task.stp
            //returns task_struct of process_pid
            //pt:long serves as "pointer to task_struct"
            pt = pid2task(process_pid) 
    
            //task_execname(pt): defined in /usr/share/systemtap/tapset/task.stp
            printf("Execname of process %d: %s\n", process_pid, task_execname(pt) )
            pt_utime = @cast(pt, "task_struct", "kernel<linux/sched.h>")->utime
            printf("Utime of process: %d\n", pt_utime)
    
            //taken from task_cpu@/usr/share/systemtap/tapset/task.stp
            ti = (@defined(@cast(pt, "task_struct", "kernel<linux/sched.h>")->stack)
              ? @cast(pt, "task_struct", "kernel<linux/sched.h>")->stack
              : @cast(pt, "task_struct", "kernel<linux/sched.h>")->thread_info);
            printf("Cpu: %d\n", @cast(ti, "thread_info", "kernel<linux/sched.h>")->cpu)
    
            //does the above code make sense?
            printf("%s == %s ???\n", task_execname(pt), task_execname(@cast(ti, "thread_info", "kernel<linux/sched.h>")->task))
    
            exit()
    }
    
    1. 以root身份输入

      $&GT; stap -x 5296 thread_info.stp

      进程5296的执行名称:bash

      过程的时间:174

      Cpu:0

      bash == bash ???

答案 2 :(得分:-2)

然而,基本上以这种方式检索thread_info数据听起来是合理的。我对GBD不是很熟悉,如果我错了请纠正我。当你通过gdb附加一个线程然后“打印$ sp”时,你怎么知道寄存器用作“堆栈指针”而不是“通用寄存器”?您应该检索正确的SP,其中“sp”实际上被指定为堆栈。