kernel / pub / scm / linux / kernel / git / deller / palo / 05ab9d3921cd267bdc61f4517e954e05620c98e1 / . / unwind.c

/* | |

* This file is subject to the terms and conditions of the GNU General Public | |

* License. See the file "COPYING" in the main directory of this archive | |

* for more details. | |

* | |

* Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com | |

*/ | |

#include <stdio.h> | |

#define GET_REG(reg) ({ \ | |

unsigned long r; \ | |

__asm__ __volatile__( \ | |

"copy " #reg ",%0" : "=r" (r) \ | |

); \ | |

r; \ | |

}) | |

#define GET_SP() GET_REG(30) | |

#define GET_RP() GET_REG(2) | |

#define GET_PC() ({ \ | |

unsigned long r; \ | |

__asm__ __volatile__( \ | |

"ldil L%%(.+8), %0\n\t" \ | |

"ldo R%%(.+4)(%0), %0" : "=r" (r) \ | |

); \ | |

r; \ | |

}) | |

/* These two would be MUCH easier with a quick asm() instruction! */ | |

/* extract some bits, least-sig bit is numbered 0, unsigned */ | |

#define GETBITSLSB0(i, start, length) \ | |

(((i) >> (start)) & ((1 << (length)) - 1)) | |

/* extract some bits, least-sig bit is numbered 0, signed */ | |

#define GETBITSLSB0S(i, start, length) \ | |

(((signed)((i) << (32 - (start + length)))) >> (32 - length)) | |

/* bl xxx,%r2 */ | |

#define BL2_BITS 0xe8400000 | |

#define BL2_MASK 0xffe0e000 | |

#define IS_BL2(i) (((i) & BL2_MASK) == BL2_BITS) | |

/********** Not finished ************/ | |

#define BLR_BITS 0xe8004 | |

#define BLR_MASK 0xfc00e | |

union bl | |

{ | |

unsigned instr; | |

struct | |

{ | |

unsigned opcode:6; | |

unsigned t:5; | |

unsigned w1:5; | |

unsigned dummy:3; | |

unsigned w2_0_9:10; | |

unsigned w2_10:1; | |

unsigned n:1; | |

unsigned w:1; | |

} bits; | |

}; | |

static int | |

assemble_17(unsigned instr) | |

{ | |

union bl bl; | |

int x; | |

int sx; | |

bl.instr = instr; | |

x = bl.bits.w2_0_9; | |

x |= bl.bits.w2_10 << 10; | |

x |= bl.bits.w1 << 11; | |

x |= bl.bits.w << (5 + 11); | |

sx = x << (32 - 17); | |

sx >>= (32 - 17 - 2); | |

#if 0 | |

printf("op %x t %x w1 %x w20 %x n %x w %x x %x 4x %x sx %d\n", | |

bl.bits.opcode, | |

bl.bits.t, | |

bl.bits.w1, | |

bl.bits.w2_0_9, | |

bl.bits.n, | |

bl.bits.w, x, x << 2, sx); | |

#endif | |

return sx; | |

} | |

static unsigned * | |

find_rp(unsigned *sp, unsigned *dot, unsigned **btargetp) | |

{ | |

unsigned *lim = sp - 1024; | |

extern unsigned _etext, __text_start; | |

while(sp > lim) | |

{ | |

unsigned *maybe_rp = (unsigned *)*sp; | |

if (0) printf("%p: %p\n", sp, maybe_rp); | |

if (((unsigned)maybe_rp & 0x3) == 0x3 | |

&& maybe_rp < &_etext | |

&& maybe_rp >= &__text_start) | |

{ | |

unsigned instr; | |

unsigned *btarget; | |

maybe_rp = (unsigned *)((unsigned)maybe_rp & ~0x3); | |

/* check if instr - 2 is a bl ..., %r2 */ | |

instr = maybe_rp[-2]; | |

if (IS_BL2(instr)) | |

{ | |

btarget = (unsigned *)((unsigned)maybe_rp + assemble_17(instr)); | |

printf("%p: %p: bl %p, %%r2\n", | |

sp, | |

maybe_rp - 2, | |

btarget | |

); | |

if (btarget > dot) | |

{ | |

printf("unlikely bl, .+0x%x\n", | |

(unsigned)btarget - (unsigned)maybe_rp); | |

} | |

else | |

{ | |

*btargetp = btarget; | |

return sp; | |

} | |

} | |

} | |

sp--; | |

} | |

return 0; | |

} | |

void unwind() | |

{ | |

unsigned *rp = (unsigned *)(GET_RP() & ~0x3); | |

unsigned *sp = (unsigned *)GET_SP(); | |

/* lie about these data types... _end could be 1-byte aligned */ | |

extern unsigned _end; | |

extern unsigned _etext; | |

extern unsigned __text_start; | |

unsigned *lastrpsp = sp; | |

extern unsigned dotlabel; | |

unsigned *dot; | |

unsigned *our_entry; | |

unsigned *lim; | |

unsigned *btarget; | |

asm("\ndotlabel:"); | |

dot = &dotlabel; | |

printf("Initial rp: %p\tsp: %p\tetext: %p\t.: %p\n", rp, sp, &_etext, dot); | |

/* find where my RP is stored, hence my caller's stack frame */ | |

for ( ; *sp != rp; sp--) | |

{ | |

} | |

printf("Found my RP on stack at %p\n", sp); | |

/* stack frames are 16 words minimum, rp is stored in word -5 relative */ | |

/* to stack pointer on entry to a routine, so we can calculate the sp */ | |

/* on entry to the current routine */ | |

sp += 5; | |

printf("My entry SP was %p\n", sp); | |

/* by disassembling the target of the branch which brought us here, */ | |

/* we can learn our entry point address */ | |

our_entry = (unsigned *)(assemble_17(rp[-2]) + (unsigned)rp); | |

printf("Entry point to this routine is %p\n", our_entry); | |

/* can't be sure about args, but some may be on stack... */ | |

printf("%p: %p(%08x, %08x, %08x, %08x)\n", | |

rp - 2, | |

our_entry, | |

sp[-9], | |

sp[-10], | |

sp[-11], | |

sp[-12]); | |

/* become the previous procedure */ | |

dot = rp; | |

/* this is the minimum stack frame size -- skip over it */ | |

sp -= 16; | |

sp = find_rp(sp, dot, &btarget); | |

if (sp != 0) | |

{ | |

rp = (unsigned *)((unsigned)*sp & ~0x3); | |

printf("Found my RP (%p) on stack at %p\n", rp, sp); | |

/* stack frames are 16 words minimum, rp is stored in word -5 relative */ | |

/* to stack pointer on entry to a routine, so we can calculate the sp */ | |

/* on entry to the current routine */ | |

sp += 5; | |

printf("My entry SP was %p\n", sp); | |

/* by disassembling the target of the branch which brought us here, */ | |

/* we can learn our entry point address */ | |

if (0) our_entry = (unsigned *)(assemble_17(rp[-2]) + (unsigned)rp); | |

printf("Entry point to this routine is %p\n", btarget); | |

/* can't be sure about args, but some may be on stack... */ | |

printf("%p: %p()\n", rp - 2, our_entry); | |

/* become the previous procedure */ | |

dot = rp; | |

} | |

} | |

#if 0 | |

void ounwind() | |

{ | |

unsigned *sp = (unsigned *)GET_SP(); | |

unsigned *rp = (unsigned *)GET_RP(); | |

/* lie about these data types... _end could be 1-byte aligned */ | |

extern unsigned _end; | |

extern unsigned _etext; | |

extern unsigned __text_start; | |

unsigned *lim = sp - 1024*4; | |

unsigned *lastrpsp = sp; | |

extern unsigned dotlabel; | |

unsigned *dot; | |

asm("\ndotlabel:"); | |

dot = &dotlabel; | |

printf("Initial rp: %p\tsp: %p\tetext: %p\t.: %p\n", rp, sp, &_etext, dot); | |

/* walk backwards in stack looking for likely RPs */ | |

while(sp > lim) | |

{ | |

unsigned *maybe_rp = (unsigned *)*sp; | |

if (0) printf("%p: %p\n", sp, maybe_rp); | |

if (((unsigned)maybe_rp & 0x3) == 0x3 | |

&& maybe_rp < &_etext | |

&& maybe_rp >= &__text_start) | |

{ | |

unsigned instr; | |

unsigned *btarget; | |

maybe_rp = (unsigned *)((unsigned)maybe_rp & ~0x3); | |

/* check if instr - 2 is a bl ..., %r2 */ | |

instr = maybe_rp[-2]; | |

if (IS_BL2(instr)) | |

{ | |

btarget = (unsigned)maybe_rp + assemble_17(instr); | |

printf("%p: %p: bl %p, %%r2, stack delta 0x%x\n", | |

sp, | |

maybe_rp - 2, | |

btarget, | |

(unsigned)lastrpsp - (unsigned)sp | |

); | |

if (btarget > dot) | |

{ | |

printf("unlikely bl, .+0x%x\n", | |

(unsigned)btarget - (unsigned)maybe_rp); | |

} | |

lastrpsp = sp; | |

} | |

} | |

sp--; | |

} | |

exit(77); | |

} | |

#endif |