Re: [RFC 3/7] Add basic Linux kernel support

Yao Qi
2017-02-09T13:06:10Z

Yao Qi <qiyaoltc@gmail.com> writes:

> I am playing your first three patches on x86_64 with some hacks.
> I write a small program with the same linux kernel "signature", and
> want GDB treat it as a linux kernel.
>

I make some progress on writing such small test case,
see the code below. I hacked lk_try_push_target not to do the
sanity check, and not to call lk_try_push_target in
lk_observer_inferior_created, so that I can push this target layer when
I want.

(gdb) break stop^M
Breakpoint 2 at 0x400711: file /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c, line 104.^M
(gdb) continue^M
Continuing.^M
^M
Breakpoint 2, stop () at /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c:104^M
104 {}^M
(gdb) PASS: gdb.base/linux-kernel.exp: continue to breakpoint: stop

at this point, the list of tasks are set up, switch to linux-kernel
target layer,

target linux-kernel^M
[New process 8001]^M
(gdb) PASS: gdb.base/linux-kernel.exp: target linux-kernel
maintenance print target-stack^M
The current target stack is:^M
- linux-kernel (linux kernel support)^M
- native (Native process)^M
- exec (Local exec file)^M
- None (None)^M

It works! In this way, we can test that GDB can successfully parse the
these data structures in Linux kernel without Linux kernel image at all.

Then, we can generate a coredump,

(gdb) generate-core-file
Saved corefile core.9614

Remove the hack in lk_observer_inferior_created, so that GDB can
automatically push linux-kernel target layer,

$ ./gdb ./testsuite/outputs/gdb.base/linux-kernel/linux-kernel ./core.9614
[New LWP 9614]
[New process 9614]
Core was generated by `/scratch/yao/gdb/build-git/x86_64/gdb/testsuite/outputs/gdb.base/linux-kernel/li'.
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
#0 stop () at /home/yao/SourceCode/gnu/gdb/git/gdb/testsuite/gdb.base/linux-kernel.c:104
104 {}
[Current thread is 1 (PID: 9614*, 0x602010)]
(gdb) maintenance print target-stack
The current target stack is:
- linux-kernel (linux kernel support)
- core (Local core dump file)
- exec (Local exec file)
- None (None)

The next step would be extending the test case to a multi-threaded
program, so that we can create task lists for these threads, and
generate coredump which is similar to the kernel coredump.

--
Yao (齐尧)

#include <unistd.h>
#include <stdlib.h>
#include <string.h>

static char linux_banner[10];
static int _stext = 0;
static int _etext = 0;

typedef int pid_t;

struct list_head
{
struct list_head *next, *prev;
};

#define LIST_HEAD_INIT(name) (struct list_head) { &(name), &(name) }

struct thread_struct
{};

struct task_struct
{
struct list_head tasks;
pid_t pid;
pid_t tgid;
struct list_head thread_group;
char comm[20];

struct thread_struct thread;
};

struct rq
{
struct task_struct *curr;
};

#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE 8
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof (long))

#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]

#define NR_CPUS 10

struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); };

struct cpumask __cpu_online_mask;

static void
cpumask_set_cpu (unsigned int cpu, struct cpumask* dstp)
{
dstp->bits[cpu / (BITS_PER_BYTE * sizeof (long))]
|= 1UL << (cpu % (BITS_PER_BYTE * sizeof (long)));
}

struct task_struct init_task;;
struct rq runqueues[NR_CPUS];
struct cpumask baz;

unsigned long __per_cpu_offset[NR_CPUS];

struct mm_struct
{};

struct mm_struct init_mm;

static void
setup (void)
{
int i;
struct task_struct *task;

/* Set up __per_cpu_offset. */
for (i = 0; i < NR_CPUS; i++)
__per_cpu_offset[i] = i * sizeof (runqueues[0]);

/* Mark cpu 0 is online. */
cpumask_set_cpu (0, &__cpu_online_mask);

init_task.tasks = LIST_HEAD_INIT(init_task.tasks);
init_task.thread_group = LIST_HEAD_INIT(init_task.thread_group);

task = malloc (sizeof (struct task_struct));
memset (task, 0, sizeof (sizeof (struct task_struct)));

task->pid = getpid ();

runqueues[0].curr = task;

/* Chain it tasks list. */
init_task.tasks.next = (struct list_head *) task;
init_task.tasks.prev = (struct list_head *) task;
task->tasks.next = (struct list_head *) &init_task;
task->tasks.prev = (struct list_head *) &init_task;

/* TASK is the group leader. */
task->thread_group = LIST_HEAD_INIT(task->thread_group);
}

static void
stop (void)
{}

int
main (void)
{
setup ();

stop ();
return 0;
}