how does linux restores a thread's stack pointer, program counter and return address.

Dave Hylands dhylands at gmail.com
Tue Oct 4 14:33:15 EDT 2011


Hi Smital,

On Mon, Oct 3, 2011 at 9:20 PM, Smital Desai
<Smital.Desai at lntinfotech.com> wrote:
> Hi.
>
> I am using linux 2.6.39 and MIPS core.
>
> I am keen to find out how does linux restores a thread's stack pointer,
> program counter and return address.
>
> I have created a binary which spawns four threads. with a ps -eaL , then I
> kill one of the thread with kill -11 <thread_id>. In the kernel
> 'do_coredump' handler i can easily find out the stack pointer of the current
> crashed thread by reading the struct pt_regs->reg29. My concern is to find
> out the stack pointers of rest of the 3 threads that did not crash. I did it
> this way:
>
> 1. read the reg29 variable of struct thread_struct for each task (By
> traversing the list of task with list_for_each)
> 2. Dump the values at address stored in reg29.
> 3. Look out manually to locate the Stack pointer offset. Tried this by
> crashing all the threads and found that the stackpointer comes at a fixed
> offset and assumed that stack pointer to be correct and I went ahead.
>
> so you can say that my Stack pointer is stored at => reg29 + offset;
>
> My question now is how to find this value of 'offset' dynamically. How does
> the kernel keeps a track of any thread's stack pointer while restoring the
> context. Since my observation was that the reg29 field of struct
> thread_struct at a glance seems to be a stack pointer. but it is not. it is
> just an address where at some offset we can find our stack pointer stored.

Each thread has its own stack, which grows from high memory towards low memory.

At the low memory end of the stack, a thread_info structure contains
thread specific information, including the stack pointer for
non-running threads.

The code which actually performs the context switch starts here:
http://lxr.linux.no/linux+v2.6.39/kernel/sched.c#L2954

and the call to switch_to is where the switch actually takes place
(and will be architecture dependant).

You may be interested in looking at the SysRq-T, which (if enabled)
will dump the context of all threads in the system to the console.

A snippet from my ARM system looks like the following:

*** break sent ***
[45568.530000] SysRq : Show State
[45568.530000]   task                PC stack   pid father
[45568.530000] systemd         S c0397de8     0     1      0 0x00000000
[45568.530000] [<c0397de8>] (schedule+0x588/0x668) from [<c03990bc>]
(schedule_hrtimeout_range_clock+0x44/0x14c)
[45568.530000] [<c03990bc>]
(schedule_hrtimeout_range_clock+0x44/0x14c) from [<c010bd1c>]
(sys_epoll_wait+0x1b0/0x308)
[45568.530000] [<c010bd1c>] (sys_epoll_wait+0x1b0/0x308) from
[<c00359c0>] (ret_fast_syscall+0x0/0x30)
[45568.530000] kthreadd        S c0397de8     0     2      0 0x00000000
[45568.530000] [<c0397de8>] (schedule+0x588/0x668) from [<c0088f3c>]
(kthreadd+0x6c/0xf8)
[45568.530000] [<c0088f3c>] (kthreadd+0x6c/0xf8) from [<c00363ac>]
(kernel_thread_exit+0x0/0x8)
[45568.530000] ksoftirqd/0     S c0397de8     0     3      2 0x00000000
[45568.530000] [<c0397de8>] (schedule+0x588/0x668) from [<c0076018>]
(run_ksoftirqd+0x78/0x1bc)
[45568.530000] [<c0076018>] (run_ksoftirqd+0x78/0x1bc) from
[<c0088cf4>] (kthread+0x84/0x8c)
[45568.530000] [<c0088cf4>] (kthread+0x84/0x8c) from [<c00363ac>]
(kernel_thread_exit+0x0/0x8)

So you get to see each thread, its state (S) PC, pid, and a backtrace
(from within kernel space). For user-mode stuff, this will only show
you backtrace to the system-call interface. How the stack pointers are
stored in user-space is dependant on the C runtime library that you're
using.

The code for dumping this information calls sysrq_handle_showstate
http://lxr.linux.no/linux+v2.6.39/drivers/tty/sysrq.c#L268

which in turns calls show_state:
http://lxr.linux.no/linux+v2.6.39/include/linux/sched.h#L286

-- 
Dave Hylands
Shuswap, BC, Canada
http://www.davehylands.com



More information about the Kernelnewbies mailing list