Subject: Magic numbers about stack layout (again)

From: Atsushi Nemoto <[email-masked]>
Date: Tue, 28 May 2002 17:26:09 +0900 (JST)

>>>>> On Mon, 27 May 2002 23:11:56 -0700, Ralf Baechle <[email-masked]> said:
ralf> Modified files:
ralf> 	include/asm-mips: Tag: linux_2_4 processor.h 

ralf> Log message:
ralf> 	Daily fix of get_wchan(), bloddy piece of shit ...

I posted this message (and a patch) 9 month ago.  How about this?

>>>>> On Wed, 22 Aug 2001 14:45:47 +0900 (JST), Atsushi Nemoto <[email-masked]> said:
nemoto> There are some magic constant numbers about stack layout in
nemoto> thread_saved_pc() and get_wchan() function.

nemoto> I made a patch to eliminate these magic numbers.  This patch analyzes
nemoto> some functions prologue codes in heuristic way at run-time.  "ps -l"
nemoto> (and "MAGIC SYSRQ" feature) works fine with this patch.

Unfortunately, binutils 2.9.5 generates wrong codes with this patch.
I think this is a bug of the binutils.  Newer binutils (I tested 2.12
and 2.12.1) or binutils 2.8.1 seems fine.

---
Atsushi Nemoto
diff -ur linux.sgi/arch/mips/kernel/process.c linux/arch/mips/kernel/process.c
--- linux.sgi/arch/mips/kernel/process.c	Sun Aug  5 23:39:09 2001
+++ linux/arch/mips/kernel/process.c	Wed Aug 22 14:14:29 2001
@@ -19,6 +19,7 @@
 #include <linux/sys.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
+#include <linux/init.h>
 
 #include <asm/bootinfo.h>
 #include <asm/cpu.h>
@@ -31,6 +32,7 @@
 #include <asm/io.h>
 #include <asm/elf.h>
 #include <asm/isadep.h>
+#include <asm/inst.h>
 
 void cpu_idle(void)
 {
@@ -196,6 +198,59 @@
 #define first_sched	((unsigned long) scheduling_functions_start_here)
 #define last_sched	((unsigned long) scheduling_functions_end_here)
 
+struct mips_frame_info schedule_frame;
+static struct mips_frame_info schedule_timeout_frame;
+static struct mips_frame_info sleep_on_frame;
+static struct mips_frame_info sleep_on_timeout_frame;
+static int mips_frame_info_initialized;
+static int __init get_frame_info(struct mips_frame_info *info, void *func)
+{
+	int i;
+	union mips_instruction *ip = (union mips_instruction *)func;
+	info->pc_offset = -1;
+	info->frame_offset = -1;
+	for (i = 0; i < 128; i++, ip++) {
+		/* if jal, jalr, jr, stop. */
+		if (ip->j_format.opcode == jal_op ||
+		    (ip->r_format.opcode == spec_op &&
+		     (ip->r_format.func == jalr_op ||
+		      ip->r_format.func == jr_op)))
+			break;
+		if (ip->i_format.opcode == sw_op &&
+		    ip->i_format.rs == 29) {
+			/* sw $ra, offset($sp) */
+			if (ip->i_format.rt == 31) {
+				if (info->pc_offset != -1)
+					break;
+				info->pc_offset =
+					ip->i_format.simmediate / sizeof(long);
+			}
+			/* sw $s8, offset($sp) */
+			if (ip->i_format.rt == 30) {
+				if (info->frame_offset != -1)
+					break;
+				info->frame_offset =
+					ip->i_format.simmediate / sizeof(long);
+			}
+		}
+	}
+	if (info->pc_offset == -1 || info->frame_offset == -1) {
+		printk("Can't analize prologue code at %p\n", func);
+		info->pc_offset = -1;
+		info->frame_offset = -1;
+		return -1;
+	}
+	return 0;
+}
+void __init frame_info_init(void)
+{
+	mips_frame_info_initialized =
+		!get_frame_info(&schedule_frame, schedule) &&
+		!get_frame_info(&schedule_timeout_frame, schedule_timeout) &&
+		!get_frame_info(&sleep_on_frame, sleep_on) &&
+		!get_frame_info(&sleep_on_timeout_frame, sleep_on_timeout);
+}
+
 /* get_wchan - a maintenance nightmare ...  */
 unsigned long get_wchan(struct task_struct *p)
 {
@@ -204,6 +259,8 @@
 	if (!p || p == current || p->state == TASK_RUNNING)
 		return 0;
 
+	if (!mips_frame_info_initialized)
+		return 0;
 	pc = thread_saved_pc(&p->thread);
 	if (pc < first_sched || pc >= last_sched) {
 		return pc;
@@ -220,23 +277,24 @@
 	goto schedule_timeout_caller;
 
 schedule_caller:
-	frame = ((unsigned long *)p->thread.reg30)[9];
-	pc    = ((unsigned long *)frame)[11];
+	/* schedule_timeout called by interruptible_sleep_on or sleep_on */
+	frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset];
+	pc    = ((unsigned long *)frame)[sleep_on_frame.pc_offset];
 	return pc;
 
 schedule_timeout_caller:
 	/* Must be schedule_timeout ...  */
-	pc    = ((unsigned long *)p->thread.reg30)[10];
-	frame = ((unsigned long *)p->thread.reg30)[9];
+	pc    = ((unsigned long *)p->thread.reg30)[schedule_frame.pc_offset];
+	frame = ((unsigned long *)p->thread.reg30)[schedule_frame.frame_offset];
 
 	/* The schedule_timeout frame ...  */
-	pc    = ((unsigned long *)frame)[14];
-	frame = ((unsigned long *)frame)[13];
+	pc    = ((unsigned long *)frame)[schedule_timeout_frame.pc_offset];
+	frame = ((unsigned long *)frame)[schedule_timeout_frame.frame_offset];
 
 	if (pc >= first_sched && pc < last_sched) {
 		/* schedule_timeout called by interruptible_sleep_on_timeout */
-		pc    = ((unsigned long *)frame)[11];
-		frame = ((unsigned long *)frame)[10];
+		pc    = ((unsigned long *)frame)[sleep_on_timeout_frame.pc_offset];
+		frame = ((unsigned long *)frame)[sleep_on_timeout_frame.frame_offset];
 	}
 
 	return pc;
diff -ur linux.sgi/arch/mips/kernel/setup.c linux/arch/mips/kernel/setup.c
--- linux.sgi/arch/mips/kernel/setup.c	Sun Aug  5 23:39:15 2001
+++ linux/arch/mips/kernel/setup.c	Wed Aug 22 14:26:19 2001
@@ -518,12 +518,14 @@
 	void malta_setup(void);
 	void momenco_ocelot_setup(void);
 	void nino_setup(void);
+	void frame_info_init(void);
 
 	unsigned long bootmap_size;
 	unsigned long start_pfn, max_pfn, first_usable_pfn;
 
 	int i;
 
+	frame_info_init();
 #ifdef CONFIG_BLK_DEV_FD
 	fd_ops = &no_fd_ops;
 #endif
diff -ur linux.sgi/include/asm-mips/processor.h linux/include/asm-mips/processor.h
--- linux.sgi/include/asm-mips/processor.h	Sun Aug  5 23:41:29 2001
+++ linux/include/asm-mips/processor.h	Wed Aug 22 14:14:59 2001
@@ -217,6 +217,11 @@
 #define copy_segments(p, mm) do { } while(0)
 #define release_segments(mm) do { } while(0)
 
+struct mips_frame_info {
+	int frame_offset;
+	int pc_offset;
+};
+extern struct mips_frame_info schedule_frame;
 /*
  * Return saved PC of a blocked thread.
  */
@@ -228,7 +233,9 @@
 	if (t->reg31 == (unsigned long) ret_from_fork)
 		return t->reg31;
 
-	return ((unsigned long *)t->reg29)[10];
+	if (schedule_frame.pc_offset < 0)
+		return 0;
+	return ((unsigned long *)t->reg29)[schedule_frame.pc_offset];
 }
 
 /*