Commit 874d59c3 authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Linus Torvalds

[PATCH] PA-RISC unwind updates

 - Update stale url to documentation

From: Kyle McMartin <kyle@parisc-linux.org>

 - Make unwinding from modules work, mostly
 - Fix unwinding from millicode
Signed-off-by: default avatarRandolph Chung <tausq@debian.org>
Signed-off-by: default avatarMatthew Wilcox <willy@parisc-linux.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent cb194696
......@@ -2,7 +2,7 @@
*
* The best reference for this stuff is probably the Processor-
* Specific ELF Supplement for PA-RISC:
* http://ftp.parisc-linux.org/docs/elf-pa-hp.pdf
* http://ftp.parisc-linux.org/docs/arch/elf-pa-hp.pdf
*
* Linux/PA-RISC Project (http://www.parisc-linux.org/)
* Copyright (C) 2003 Randolph Chung <tausq at debian . org>
......@@ -21,6 +21,23 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* Notes:
* - SEGREL32 handling
* We are not doing SEGREL32 handling correctly. According to the ABI, we
* should do a value offset, like this:
* if (is_init(me, (void *)val))
* val -= (uint32_t)me->module_init;
* else
* val -= (uint32_t)me->module_core;
* However, SEGREL32 is used only for PARISC unwind entries, and we want
* those entries to have an absolute address, and not just an offset.
*
* The unwind table mechanism has the ability to specify an offset for
* the unwind table; however, because we split off the init functions into
* a different piece of memory, it is not possible to do this using a
* single offset. Instead, we use the above hack for now.
*/
#include <linux/moduleloader.h>
......@@ -30,6 +47,8 @@
#include <linux/string.h>
#include <linux/kernel.h>
#include <asm/unwind.h>
#if 0
#define DEBUGP printk
#else
......@@ -248,6 +267,10 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset;
unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels);
if (strncmp(secstrings + sechdrs[i].sh_name,
".PARISC.unwind", 14) == 0)
me->arch.unwind_section = i;
if (sechdrs[i].sh_type != SHT_RELA)
continue;
......@@ -499,7 +522,9 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
break;
case R_PARISC_SEGREL32:
/* 32-bit segment relative address */
val -= (uint32_t)me->module_core;
/* See note about special handling of SEGREL32 at
* the beginning of this file.
*/
*loc = fsel(val, addend);
break;
case R_PARISC_DPREL21L:
......@@ -651,7 +676,9 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
break;
case R_PARISC_SEGREL32:
/* 32-bit segment relative address */
val -= (uint64_t)me->module_core;
/* See note about special handling of SEGREL32 at
* the beginning of this file.
*/
*loc = fsel(val, addend);
break;
case R_PARISC_FPTR64:
......@@ -682,6 +709,32 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
}
#endif
static void
register_unwind_table(struct module *me,
const Elf_Shdr *sechdrs)
{
unsigned char *table, *end;
unsigned long gp;
if (!me->arch.unwind_section)
return;
table = (unsigned char *)sechdrs[me->arch.unwind_section].sh_addr;
end = table + sechdrs[me->arch.unwind_section].sh_size;
gp = (Elf_Addr)me->module_core + me->arch.got_offset;
DEBUGP("register_unwind_table(), sect = %d at 0x%p - 0x%p (gp=0x%lx)\n",
me->arch.unwind_section, table, end, gp);
me->arch.unwind = unwind_table_add(me->name, 0, gp, table, end);
}
static void
deregister_unwind_table(struct module *me)
{
if (me->arch.unwind)
unwind_table_remove(me->arch.unwind);
}
int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs,
struct module *me)
......@@ -711,6 +764,8 @@ int module_finalize(const Elf_Ehdr *hdr,
me->arch.fdesc_count, me->arch.fdesc_max);
#endif
register_unwind_table(me, sechdrs);
/* haven't filled in me->symtab yet, so have to find it
* ourselves */
for (i = 1; i < hdr->e_shnum; i++) {
......@@ -763,4 +818,5 @@ int module_finalize(const Elf_Ehdr *hdr,
void module_arch_cleanup(struct module *mod)
{
deregister_unwind_table(mod);
}
......@@ -36,8 +36,7 @@ static spinlock_t unwind_lock;
* possible (before the slab allocator is initialized)
*/
static struct unwind_table kernel_unwind_table;
static struct unwind_table *unwind_tables, *unwind_tables_end;
static LIST_HEAD(unwind_tables);
static inline const struct unwind_table_entry *
find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
......@@ -65,14 +64,14 @@ find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr)
static const struct unwind_table_entry *
find_unwind_entry(unsigned long addr)
{
struct unwind_table *table = unwind_tables;
struct unwind_table *table;
const struct unwind_table_entry *e = NULL;
if (addr >= kernel_unwind_table.start &&
addr <= kernel_unwind_table.end)
e = find_unwind_entry_in_table(&kernel_unwind_table, addr);
else
for (; table; table = table->next) {
else
list_for_each_entry(table, &unwind_tables, list) {
if (addr >= table->start &&
addr <= table->end)
e = find_unwind_entry_in_table(table, addr);
......@@ -99,7 +98,7 @@ unwind_table_init(struct unwind_table *table, const char *name,
table->end = base_addr + end->region_end;
table->table = (struct unwind_table_entry *)table_start;
table->length = end - start + 1;
table->next = NULL;
INIT_LIST_HEAD(&table->list);
for (; start <= end; start++) {
if (start < end &&
......@@ -112,33 +111,60 @@ unwind_table_init(struct unwind_table *table, const char *name,
}
}
void *
static void
unwind_table_sort(struct unwind_table_entry *start,
struct unwind_table_entry *finish)
{
struct unwind_table_entry el, *p, *q;
for (p = start + 1; p < finish; ++p) {
if (p[0].region_start < p[-1].region_start) {
el = *p;
q = p;
do {
q[0] = q[-1];
--q;
} while (q > start &&
el.region_start < q[-1].region_start);
*q = el;
}
}
}
struct unwind_table *
unwind_table_add(const char *name, unsigned long base_addr,
unsigned long gp,
void *start, void *end)
{
struct unwind_table *table;
unsigned long flags;
struct unwind_table_entry *s = (struct unwind_table_entry *)start;
struct unwind_table_entry *e = (struct unwind_table_entry *)end;
unwind_table_sort(s, e);
table = kmalloc(sizeof(struct unwind_table), GFP_USER);
if (table == NULL)
return NULL;
unwind_table_init(table, name, base_addr, gp, start, end);
spin_lock_irqsave(&unwind_lock, flags);
if (unwind_tables)
{
unwind_tables_end->next = table;
unwind_tables_end = table;
}
else
{
unwind_tables = unwind_tables_end = table;
}
list_add_tail(&table->list, &unwind_tables);
spin_unlock_irqrestore(&unwind_lock, flags);
return table;
}
void unwind_table_remove(struct unwind_table *table)
{
unsigned long flags;
spin_lock_irqsave(&unwind_lock, flags);
list_del(&table->list);
spin_unlock_irqrestore(&unwind_lock, flags);
kfree(table);
}
/* Called from setup_arch to import the kernel unwind info */
static int unwind_init(void)
{
......@@ -148,6 +174,8 @@ static int unwind_init(void)
start = (long)&__start___unwind[0];
stop = (long)&__stop___unwind[0];
spin_lock_init(&unwind_lock);
printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n",
start, stop,
(stop - start) / sizeof(struct unwind_table_entry));
......@@ -239,9 +267,9 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
info->prev_sp, info->prev_ip);
} else {
dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, "
"Save_RP = %d size = %u\n", e->region_start,
e->region_end, e->Save_SP, e->Save_RP,
e->Total_frame_size);
"Save_RP = %d, Millicode = %d size = %u\n",
e->region_start, e->region_end, e->Save_SP, e->Save_RP,
e->Millicode, e->Total_frame_size);
looking_for_rp = e->Save_RP;
......@@ -284,7 +312,9 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
}
info->prev_sp = info->sp - frame_size;
if (rpoffset)
if (e->Millicode)
info->rp = info->r31;
else if (rpoffset)
info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
info->prev_ip = info->rp;
info->rp = 0;
......@@ -296,13 +326,14 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
}
void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
unsigned long sp, unsigned long ip, unsigned long rp)
struct pt_regs *regs)
{
memset(info, 0, sizeof(struct unwind_frame_info));
info->t = t;
info->sp = sp;
info->ip = ip;
info->rp = rp;
info->sp = regs->gr[30];
info->ip = regs->iaoq[0];
info->rp = regs->gr[2];
info->r31 = regs->gr[31];
dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n",
t ? (int)t->pid : -1, info->sp, info->ip);
......@@ -310,14 +341,22 @@ void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)
{
struct pt_regs *regs = &t->thread.regs;
unwind_frame_init(info, t, regs->ksp, regs->kpc, 0);
struct pt_regs *r = &t->thread.regs;
struct pt_regs *r2;
r2 = (struct pt_regs *)kmalloc(sizeof(struct pt_regs), GFP_KERNEL);
if (!r2)
return;
*r2 = *r;
r2->gr[30] = r->ksp;
r2->iaoq[0] = r->kpc;
unwind_frame_init(info, t, r2);
kfree(r2);
}
void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs)
{
unwind_frame_init(info, current, regs->gr[30], regs->iaoq[0],
regs->gr[2]);
unwind_frame_init(info, current, regs);
}
int unwind_once(struct unwind_frame_info *next_frame)
......
......@@ -17,12 +17,16 @@
#define Elf_Rela Elf32_Rela
#endif
struct unwind_table;
struct mod_arch_specific
{
unsigned long got_offset, got_count, got_max;
unsigned long fdesc_offset, fdesc_count, fdesc_max;
unsigned long stub_offset, stub_count, stub_max;
unsigned long init_stub_offset, init_stub_count, init_stub_max;
int unwind_section;
struct unwind_table *unwind;
};
#endif /* _ASM_PARISC_MODULE_H */
#ifndef _UNWIND_H_
#define _UNWIND_H_
#include <linux/list.h>
/* From ABI specifications */
struct unwind_table_entry {
unsigned int region_start;
......@@ -39,7 +41,7 @@ struct unwind_table_entry {
};
struct unwind_table {
struct unwind_table *next;
struct list_head list;
const char *name;
unsigned long gp;
unsigned long base_addr;
......@@ -55,15 +57,18 @@ struct unwind_frame_info {
available; but for now we only try to get the sp and ip for each
frame */
/* struct pt_regs regs; */
unsigned long sp, ip, rp;
unsigned long sp, ip, rp, r31;
unsigned long prev_sp, prev_ip;
};
void * unwind_table_add(const char *name, unsigned long base_addr,
unsigned long gp,
void *start, void *end);
struct unwind_table *
unwind_table_add(const char *name, unsigned long base_addr,
unsigned long gp, void *start, void *end);
void
unwind_table_remove(struct unwind_table *table);
void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t,
unsigned long sp, unsigned long ip, unsigned long rp);
struct pt_regs *regs);
void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t);
void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs);
int unwind_once(struct unwind_frame_info *info);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment