diff options
author | Marcin Bukat <marcin.bukat@gmail.com> | 2012-01-25 09:57:59 +0100 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2012-02-22 08:33:26 +0100 |
commit | b4eab599513324dcaffa4c5693345ae11f3f9725 (patch) | |
tree | 58d66298269d2cec58102724565b573f250b5153 | |
parent | 680c6fcde1eabb45dd12c59718d708b2cda61f6a (diff) | |
download | rockbox-b4eab599513324dcaffa4c5693345ae11f3f9725.tar.gz rockbox-b4eab599513324dcaffa4c5693345ae11f3f9725.zip |
Arm stack unwinder
Simplified stack unwinder for ARM. This is port of
http://www.mcternan.me.uk/ArmStackUnwinding/
backtrace() is called from UIE() on native targets
and from panicf() on both native and ARM RaaA.
Change-Id: I8e4b3c02490dd60b30aa372fe842d193b8929ce0
-rw-r--r-- | android/android.make | 2 | ||||
-rw-r--r-- | firmware/SOURCES | 2 | ||||
-rw-r--r-- | firmware/export/panic.h | 6 | ||||
-rw-r--r-- | firmware/panic.c | 32 | ||||
-rw-r--r-- | firmware/target/arm/system-arm.c | 19 | ||||
-rw-r--r-- | firmware/target/hosted/ypr0/ypr0.make | 2 | ||||
-rw-r--r-- | lib/unwarminder/SOURCES | 7 | ||||
-rw-r--r-- | lib/unwarminder/backtrace.c | 124 | ||||
-rw-r--r-- | lib/unwarminder/backtrace.h | 57 | ||||
-rw-r--r-- | lib/unwarminder/get_sp.S | 34 | ||||
-rw-r--r-- | lib/unwarminder/get_sp.h | 1 | ||||
-rw-r--r-- | lib/unwarminder/types.h | 39 | ||||
-rw-r--r-- | lib/unwarminder/unwarm.c | 183 | ||||
-rw-r--r-- | lib/unwarminder/unwarm.h | 178 | ||||
-rw-r--r-- | lib/unwarminder/unwarm_arm.c | 701 | ||||
-rw-r--r-- | lib/unwarminder/unwarm_thumb.c | 740 | ||||
-rw-r--r-- | lib/unwarminder/unwarminder.c | 78 | ||||
-rw-r--r-- | lib/unwarminder/unwarminder.h | 160 | ||||
-rw-r--r-- | lib/unwarminder/unwarminder.make | 21 | ||||
-rw-r--r-- | lib/unwarminder/unwarmmem.c | 175 | ||||
-rw-r--r-- | lib/unwarminder/unwarmmem.h | 57 | ||||
-rw-r--r-- | tools/root.make | 28 | ||||
-rw-r--r-- | uisimulator/uisimulator.make | 2 |
23 files changed, 2630 insertions, 18 deletions
diff --git a/android/android.make b/android/android.make index a62c47e188..ae8238e1de 100644 --- a/android/android.make +++ b/android/android.make | |||
@@ -114,7 +114,7 @@ dex: $(DEX) | |||
114 | classes: $(R_OBJ) $(JAVA_OBJ) | 114 | classes: $(R_OBJ) $(JAVA_OBJ) |
115 | 115 | ||
116 | 116 | ||
117 | $(BUILDDIR)/$(BINARY): $$(OBJ) $(VOICESPEEXLIB) $(FIRMLIB) $(SKINLIB) $(CPUFEAT_BUILD)/cpu-features.o | 117 | $(BUILDDIR)/$(BINARY): $$(OBJ) $(VOICESPEEXLIB) $(FIRMLIB) $(SKINLIB) $(UNWARMINDER) $(CPUFEAT_BUILD)/cpu-features.o |
118 | $(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(LDOPTS) $(GLOBAL_LDOPTS) -Wl,-Map,$(BUILDDIR)/rockbox.map | 118 | $(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(LDOPTS) $(GLOBAL_LDOPTS) -Wl,-Map,$(BUILDDIR)/rockbox.map |
119 | $(call PRINTS,OC $(@F))$(OC) -S -x $@ | 119 | $(call PRINTS,OC $(@F))$(OC) -S -x $@ |
120 | 120 | ||
diff --git a/firmware/SOURCES b/firmware/SOURCES index e5df779260..85705cb556 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES | |||
@@ -433,7 +433,7 @@ target/coldfire/i2c-coldfire.c | |||
433 | target/coldfire/ata-as-coldfire.S | 433 | target/coldfire/ata-as-coldfire.S |
434 | #endif | 434 | #endif |
435 | 435 | ||
436 | #elif defined(CPU_PP) || (defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE) | 436 | #elif defined(CPU_PP) || (defined(CPU_ARM) && (CONFIG_PLATFORM & PLATFORM_NATIVE)) |
437 | /* CPU_PP => CPU_ARM, CPU_ARM !=> CPU_PP */ | 437 | /* CPU_PP => CPU_ARM, CPU_ARM !=> CPU_PP */ |
438 | 438 | ||
439 | # if ARM_ARCH < 6 | 439 | # if ARM_ARCH < 6 |
diff --git a/firmware/export/panic.h b/firmware/export/panic.h index b0325aaeb7..7767c675cf 100644 --- a/firmware/export/panic.h +++ b/firmware/export/panic.h | |||
@@ -22,8 +22,12 @@ | |||
22 | #ifndef __PANIC_H__ | 22 | #ifndef __PANIC_H__ |
23 | #define __PANIC_H__ | 23 | #define __PANIC_H__ |
24 | 24 | ||
25 | #include "config.h" | ||
25 | #include "gcc_extensions.h" | 26 | #include "gcc_extensions.h" |
26 | 27 | ||
28 | #if defined(CPU_ARM) | ||
29 | void panicf( const char *fmt, ... ) __attribute__ ((naked)) ATTRIBUTE_PRINTF(1, 2); | ||
30 | #else | ||
27 | void panicf( const char *fmt, ... ) ATTRIBUTE_PRINTF(1, 2); | 31 | void panicf( const char *fmt, ... ) ATTRIBUTE_PRINTF(1, 2); |
28 | 32 | #endif | |
29 | #endif /* __PANIC_H__ */ | 33 | #endif /* __PANIC_H__ */ |
diff --git a/firmware/panic.c b/firmware/panic.c index bd2c719607..cdefc5a0b8 100644 --- a/firmware/panic.c +++ b/firmware/panic.c | |||
@@ -31,14 +31,42 @@ | |||
31 | #include "power.h" | 31 | #include "power.h" |
32 | #include "system.h" | 32 | #include "system.h" |
33 | 33 | ||
34 | #if defined(CPU_ARM) | ||
35 | #include "gcc_extensions.h" | ||
36 | #include <backtrace.h> | ||
37 | #endif | ||
38 | |||
34 | static char panic_buf[128]; | 39 | static char panic_buf[128]; |
35 | #define LINECHARS (LCD_WIDTH/SYSFONT_WIDTH) - 2 | 40 | #define LINECHARS (LCD_WIDTH/SYSFONT_WIDTH) - 2 |
36 | 41 | ||
42 | #if defined(CPU_ARM) | ||
43 | void panicf_f( const char *fmt, ...); | ||
44 | |||
45 | /* we wrap panicf() here with naked function to catch SP value */ | ||
46 | void panicf( const char *fmt, ...) | ||
47 | { | ||
48 | (void)fmt; | ||
49 | asm volatile ("mov r4, sp \n" | ||
50 | "b panicf_f \n" | ||
51 | ); | ||
52 | } | ||
53 | |||
37 | /* | 54 | /* |
38 | * "Dude. This is pretty fucked-up, right here." | 55 | * "Dude. This is pretty fucked-up, right here." |
39 | */ | 56 | */ |
57 | void panicf_f( const char *fmt, ...) | ||
58 | { | ||
59 | int sp; | ||
60 | |||
61 | asm volatile ("mov %[SP],r4 \n" | ||
62 | : [SP] "=r" (sp) | ||
63 | ); | ||
64 | |||
65 | int pc = (int)__builtin_return_address(0); | ||
66 | #else | ||
40 | void panicf( const char *fmt, ...) | 67 | void panicf( const char *fmt, ...) |
41 | { | 68 | { |
69 | #endif | ||
42 | va_list ap; | 70 | va_list ap; |
43 | 71 | ||
44 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 72 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
@@ -82,6 +110,10 @@ void panicf( const char *fmt, ...) | |||
82 | panic_buf[i+LINECHARS] = c; | 110 | panic_buf[i+LINECHARS] = c; |
83 | } | 111 | } |
84 | } | 112 | } |
113 | |||
114 | #if defined(CPU_ARM) | ||
115 | backtrace(pc, sp, &y); | ||
116 | #endif | ||
85 | #else | 117 | #else |
86 | /* no LCD */ | 118 | /* no LCD */ |
87 | #endif | 119 | #endif |
diff --git a/firmware/target/arm/system-arm.c b/firmware/target/arm/system-arm.c index 23ccfd1f14..c4692bbf29 100644 --- a/firmware/target/arm/system-arm.c +++ b/firmware/target/arm/system-arm.c | |||
@@ -25,6 +25,9 @@ | |||
25 | #include "font.h" | 25 | #include "font.h" |
26 | #include "gcc_extensions.h" | 26 | #include "gcc_extensions.h" |
27 | 27 | ||
28 | #include <get_sp.h> | ||
29 | #include <backtrace.h> | ||
30 | |||
28 | static const char* const uiename[] = { | 31 | static const char* const uiename[] = { |
29 | "Undefined instruction", | 32 | "Undefined instruction", |
30 | "Prefetch abort", | 33 | "Prefetch abort", |
@@ -38,6 +41,12 @@ static const char* const uiename[] = { | |||
38 | */ | 41 | */ |
39 | void NORETURN_ATTR UIE(unsigned int pc, unsigned int num) | 42 | void NORETURN_ATTR UIE(unsigned int pc, unsigned int num) |
40 | { | 43 | { |
44 | /* safe guard variable - we call backtrace() only on first | ||
45 | * UIE call. This prevent endless loop if backtrace() touches | ||
46 | * memory regions which cause abort | ||
47 | */ | ||
48 | static bool triggered = false; | ||
49 | |||
41 | #if LCD_DEPTH > 1 | 50 | #if LCD_DEPTH > 1 |
42 | lcd_set_backdrop(NULL); | 51 | lcd_set_backdrop(NULL); |
43 | lcd_set_drawmode(DRMODE_SOLID); | 52 | lcd_set_drawmode(DRMODE_SOLID); |
@@ -49,9 +58,7 @@ void NORETURN_ATTR UIE(unsigned int pc, unsigned int num) | |||
49 | lcd_setfont(FONT_SYSFIXED); | 58 | lcd_setfont(FONT_SYSFIXED); |
50 | lcd_set_viewport(NULL); | 59 | lcd_set_viewport(NULL); |
51 | lcd_clear_display(); | 60 | lcd_clear_display(); |
52 | lcd_puts(0, line++, uiename[num]); | 61 | lcd_putsf(0, line++, "%s at %08x" IF_COP(" (%d)"), uiename[num], pc IF_COP(, CURRENT_CORE)); |
53 | lcd_putsf(0, line++, "at %08x" IF_COP(" (%d)"), pc | ||
54 | IF_COP(, CURRENT_CORE)); | ||
55 | 62 | ||
56 | #if !defined(CPU_ARM7TDMI) && (CONFIG_CPU != RK27XX) /* arm7tdmi has no MPU/MMU */ | 63 | #if !defined(CPU_ARM7TDMI) && (CONFIG_CPU != RK27XX) /* arm7tdmi has no MPU/MMU */ |
57 | if(num == 1 || num == 2) /* prefetch / data abort */ | 64 | if(num == 1 || num == 2) /* prefetch / data abort */ |
@@ -88,6 +95,12 @@ void NORETURN_ATTR UIE(unsigned int pc, unsigned int num) | |||
88 | } /* num == 1 || num == 2 // prefetch/data abort */ | 95 | } /* num == 1 || num == 2 // prefetch/data abort */ |
89 | #endif /* !defined(CPU_ARM7TDMI */ | 96 | #endif /* !defined(CPU_ARM7TDMI */ |
90 | 97 | ||
98 | if (!triggered) | ||
99 | { | ||
100 | triggered = true; | ||
101 | backtrace(pc, __get_sp(), &line); | ||
102 | } | ||
103 | |||
91 | lcd_update(); | 104 | lcd_update(); |
92 | 105 | ||
93 | disable_interrupt(IRQ_FIQ_STATUS); | 106 | disable_interrupt(IRQ_FIQ_STATUS); |
diff --git a/firmware/target/hosted/ypr0/ypr0.make b/firmware/target/hosted/ypr0/ypr0.make index c2114878db..1b67054183 100644 --- a/firmware/target/hosted/ypr0/ypr0.make +++ b/firmware/target/hosted/ypr0/ypr0.make | |||
@@ -14,7 +14,7 @@ SIMFLAGS += $(INCLUDES) $(DEFINES) -DHAVE_CONFIG_H $(GCCOPTS) | |||
14 | .SECONDEXPANSION: # $$(OBJ) is not populated until after this | 14 | .SECONDEXPANSION: # $$(OBJ) is not populated until after this |
15 | 15 | ||
16 | 16 | ||
17 | $(BUILDDIR)/rockbox.elf : $$(OBJ) $$(FIRMLIB) $$(VOICESPEEXLIB) $$(SKINLIB) | 17 | $(BUILDDIR)/rockbox.elf : $$(OBJ) $$(FIRMLIB) $$(VOICESPEEXLIB) $$(SKINLIB) $$(UNWARMINDER) |
18 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -o $@ $(OBJ) \ | 18 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -o $@ $(OBJ) \ |
19 | -L$(BUILDDIR)/firmware -lfirmware \ | 19 | -L$(BUILDDIR)/firmware -lfirmware \ |
20 | -L$(BUILDDIR)/apps/codecs $(VOICESPEEXLIB:lib%.a=-l%) \ | 20 | -L$(BUILDDIR)/apps/codecs $(VOICESPEEXLIB:lib%.a=-l%) \ |
diff --git a/lib/unwarminder/SOURCES b/lib/unwarminder/SOURCES new file mode 100644 index 0000000000..055e6d0ab3 --- /dev/null +++ b/lib/unwarminder/SOURCES | |||
@@ -0,0 +1,7 @@ | |||
1 | backtrace.c | ||
2 | get_sp.S | ||
3 | unwarm_arm.c | ||
4 | unwarm.c | ||
5 | unwarminder.c | ||
6 | unwarmmem.c | ||
7 | unwarm_thumb.c | ||
diff --git a/lib/unwarminder/backtrace.c b/lib/unwarminder/backtrace.c new file mode 100644 index 0000000000..4e1609137c --- /dev/null +++ b/lib/unwarminder/backtrace.c | |||
@@ -0,0 +1,124 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commercially or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liability for it's use or misuse - this software is without warranty. | ||
10 | *************************************************************************** | ||
11 | * File Description: Unwinder client that reads local memory. | ||
12 | * This client reads from local memory and is designed to run on target | ||
13 | * along with the unwinder. Memory read requests are implemented by | ||
14 | * casting a point to read the memory directly, although checks for | ||
15 | * alignment should probably also be made if this is to be used in | ||
16 | * production code, as otherwise the ARM may return the memory in a | ||
17 | * rotated/rolled format, or the MMU may generate an alignment exception | ||
18 | * if present and so configured. | ||
19 | **************************************************************************/ | ||
20 | |||
21 | /*************************************************************************** | ||
22 | * Includes | ||
23 | ***************************************************************************/ | ||
24 | |||
25 | #include "backtrace.h" | ||
26 | |||
27 | /*************************************************************************** | ||
28 | * Prototypes | ||
29 | ***************************************************************************/ | ||
30 | |||
31 | static Boolean CliReport(void *data, Int32 address); | ||
32 | static Boolean CliReadW(Int32 a, Int32 *v); | ||
33 | static Boolean CliReadH(Int32 a, Int16 *v); | ||
34 | static Boolean CliReadB(Int32 a, Int8 *v); | ||
35 | |||
36 | /*************************************************************************** | ||
37 | * Variables | ||
38 | ***************************************************************************/ | ||
39 | |||
40 | /* Table of function pointers for passing to the unwinder */ | ||
41 | const UnwindCallbacks cliCallbacks = | ||
42 | { | ||
43 | CliReport, | ||
44 | CliReadW, | ||
45 | CliReadH, | ||
46 | CliReadB | ||
47 | #if defined(UNW_DEBUG) | ||
48 | ,printf | ||
49 | #endif | ||
50 | }; | ||
51 | |||
52 | |||
53 | /*************************************************************************** | ||
54 | * Callbacks | ||
55 | ***************************************************************************/ | ||
56 | |||
57 | /*************************************************************************** | ||
58 | * | ||
59 | * Function: CliReport | ||
60 | * | ||
61 | * Parameters: data - Pointer to data passed to UnwindStart() | ||
62 | * address - The return address of a stack frame. | ||
63 | * | ||
64 | * Returns: TRUE if unwinding should continue, otherwise FALSE to | ||
65 | * indicate that unwinding should stop. | ||
66 | * | ||
67 | * Description: This function is called from the unwinder each time a stack | ||
68 | * frame has been unwound. The LSB of address indicates if | ||
69 | * the processor is in ARM mode (LSB clear) or Thumb (LSB | ||
70 | * set). | ||
71 | * | ||
72 | ***************************************************************************/ | ||
73 | static Boolean CliReport(void *data, Int32 address) | ||
74 | { | ||
75 | /* CliStack *s = (CliStack *)data; */ | ||
76 | unsigned *line = (unsigned *)data; | ||
77 | |||
78 | |||
79 | lcd_putsf(0, (*line)++, " %c: 0x%08x", | ||
80 | (address & 0x1) ? 'T' : 'A', | ||
81 | address & (~0x1)); | ||
82 | lcd_update(); | ||
83 | |||
84 | return TRUE; | ||
85 | } | ||
86 | |||
87 | static Boolean CliReadW(const Int32 a, Int32 *v) | ||
88 | { | ||
89 | *v = *(Int32 *)a; | ||
90 | return TRUE; | ||
91 | } | ||
92 | |||
93 | static Boolean CliReadH(const Int32 a, Int16 *v) | ||
94 | { | ||
95 | *v = *(Int16 *)a; | ||
96 | return TRUE; | ||
97 | } | ||
98 | |||
99 | static Boolean CliReadB(const Int32 a, Int8 *v) | ||
100 | { | ||
101 | *v = *(Int8 *)a; | ||
102 | return TRUE; | ||
103 | } | ||
104 | |||
105 | Boolean CliInvalidateW(const Int32 a) | ||
106 | { | ||
107 | *(Int32 *)a = 0xdeadbeef; | ||
108 | return TRUE; | ||
109 | } | ||
110 | |||
111 | void backtrace(int pcAddr, int spAddr, unsigned *line) | ||
112 | { | ||
113 | UnwResult r; | ||
114 | |||
115 | lcd_putsf(0, (*line)++, "bt pc: 0x%08x, sp: 0x%08x", pcAddr, spAddr); | ||
116 | lcd_update(); | ||
117 | |||
118 | r = UnwindStart(pcAddr, spAddr, &cliCallbacks, (void *)line); | ||
119 | |||
120 | lcd_puts(0, (*line)++, "bt end"); | ||
121 | lcd_update(); | ||
122 | } | ||
123 | |||
124 | /* END OF FILE */ | ||
diff --git a/lib/unwarminder/backtrace.h b/lib/unwarminder/backtrace.h new file mode 100644 index 0000000000..3bf3eb5aac --- /dev/null +++ b/lib/unwarminder/backtrace.h | |||
@@ -0,0 +1,57 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commercially or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liability for it's use or misuse - this software is without warranty. | ||
10 | *************************************************************************** | ||
11 | * File Description: Unwinder client that reads local memory. | ||
12 | **************************************************************************/ | ||
13 | |||
14 | #ifndef CLIENT_H | ||
15 | #define CLIENT_H | ||
16 | |||
17 | /*************************************************************************** | ||
18 | * Nested Includes | ||
19 | ***************************************************************************/ | ||
20 | #include "config.h" | ||
21 | #include "system.h" | ||
22 | #include "lcd.h" | ||
23 | |||
24 | #include <stdio.h> | ||
25 | #include "unwarminder.h" | ||
26 | #include "get_sp.h" | ||
27 | #include "gcc_extensions.h" | ||
28 | |||
29 | #if defined(SIM_CLIENT) | ||
30 | #error This file is not for the simulated unwinder client | ||
31 | #endif | ||
32 | |||
33 | /*************************************************************************** | ||
34 | * Typedefs | ||
35 | ***************************************************************************/ | ||
36 | |||
37 | /** Example structure for holding unwind results. | ||
38 | */ | ||
39 | typedef struct | ||
40 | { | ||
41 | Int16 frameCount; | ||
42 | Int32 address[32]; | ||
43 | } | ||
44 | CliStack; | ||
45 | |||
46 | /*************************************************************************** | ||
47 | * Variables | ||
48 | ***************************************************************************/ | ||
49 | |||
50 | extern const UnwindCallbacks cliCallbacks; | ||
51 | |||
52 | void backtrace(int pcAddr, int spAddr, unsigned *line); | ||
53 | |||
54 | #endif | ||
55 | |||
56 | |||
57 | /* END OF FILE */ | ||
diff --git a/lib/unwarminder/get_sp.S b/lib/unwarminder/get_sp.S new file mode 100644 index 0000000000..29356b3ec3 --- /dev/null +++ b/lib/unwarminder/get_sp.S | |||
@@ -0,0 +1,34 @@ | |||
1 | #include "config.h" | ||
2 | /* On native platform we protect ourself by disabling interrupts | ||
3 | * then we check current processor mode. If we are called | ||
4 | * from exception we need to save state and switch to SYS and | ||
5 | * after obtaining SP we restore everything from saved state. | ||
6 | * | ||
7 | * On RaaA we are called in USER mode most probably and | ||
8 | * cpsr mangling is restricted. We simply copy SP value | ||
9 | * in this situation | ||
10 | */ | ||
11 | .section .text | ||
12 | .type __get_sp,%function | ||
13 | .global __get_sp | ||
14 | |||
15 | __get_sp: | ||
16 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | ||
17 | mrs r1, cpsr /* save current state */ | ||
18 | orr r0, r1, #0xc0 | ||
19 | msr cpsr, r0 /* disable IRQ and FIQ */ | ||
20 | and r0, r1, #0x1f /* get current mode */ | ||
21 | cmp r0, #0x1f /* are we in sys mode? */ | ||
22 | beq get_sp | ||
23 | call_from_exception: | ||
24 | mrs r0, spsr /* get saved state */ | ||
25 | and r0, r0, #0x1f /* get mode bits */ | ||
26 | orr r0, r0, #0xc0 /* no FIQ no IRQ */ | ||
27 | msr cpsr, r0 /* change mode */ | ||
28 | get_sp: | ||
29 | #endif | ||
30 | mov r0, sp /* get SP */ | ||
31 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | ||
32 | msr cpsr, r1 /* restore mode */ | ||
33 | #endif | ||
34 | .size __get_sp, . - __get_sp | ||
diff --git a/lib/unwarminder/get_sp.h b/lib/unwarminder/get_sp.h new file mode 100644 index 0000000000..a8c965f063 --- /dev/null +++ b/lib/unwarminder/get_sp.h | |||
@@ -0,0 +1 @@ | |||
int __get_sp(void); | |||
diff --git a/lib/unwarminder/types.h b/lib/unwarminder/types.h new file mode 100644 index 0000000000..2e902f34d2 --- /dev/null +++ b/lib/unwarminder/types.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commercially or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liability for it's use or misuse - this software is without warranty. | ||
10 | **************************************************************************/ | ||
11 | /** \file | ||
12 | * Types common across the whole system. | ||
13 | **************************************************************************/ | ||
14 | |||
15 | #ifndef TYPES_H | ||
16 | #define TYPES_H | ||
17 | |||
18 | #define UPGRADE_ARM_STACK_UNWIND | ||
19 | #undef UNW_DEBUG | ||
20 | |||
21 | typedef unsigned char Int8; | ||
22 | typedef unsigned short Int16; | ||
23 | typedef unsigned int Int32; | ||
24 | |||
25 | |||
26 | typedef signed char SignedInt8; | ||
27 | typedef signed short SignedInt16; | ||
28 | typedef signed int SignedInt32; | ||
29 | |||
30 | |||
31 | typedef enum | ||
32 | { | ||
33 | FALSE, | ||
34 | TRUE | ||
35 | } Boolean; | ||
36 | |||
37 | #endif | ||
38 | |||
39 | /* END OF FILE */ | ||
diff --git a/lib/unwarminder/unwarm.c b/lib/unwarminder/unwarm.c new file mode 100644 index 0000000000..99f6a12ccb --- /dev/null +++ b/lib/unwarminder/unwarm.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commercially or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liability for it's use or misuse - this software is without warranty. | ||
10 | *************************************************************************** | ||
11 | * File Description: Utility functions and glue for ARM unwinding sub-modules. | ||
12 | **************************************************************************/ | ||
13 | |||
14 | #define MODULE_NAME "UNWARM" | ||
15 | |||
16 | /*************************************************************************** | ||
17 | * Include Files | ||
18 | **************************************************************************/ | ||
19 | |||
20 | #include "types.h" | ||
21 | #include <stdio.h> | ||
22 | #include <stdarg.h> | ||
23 | #include <string.h> | ||
24 | #include "unwarm.h" | ||
25 | #include "unwarmmem.h" | ||
26 | |||
27 | /*************************************************************************** | ||
28 | * Manifest Constants | ||
29 | **************************************************************************/ | ||
30 | |||
31 | |||
32 | /*************************************************************************** | ||
33 | * Type Definitions | ||
34 | **************************************************************************/ | ||
35 | |||
36 | |||
37 | /*************************************************************************** | ||
38 | * Variables | ||
39 | **************************************************************************/ | ||
40 | |||
41 | |||
42 | /*************************************************************************** | ||
43 | * Macros | ||
44 | **************************************************************************/ | ||
45 | |||
46 | |||
47 | /*************************************************************************** | ||
48 | * Local Functions | ||
49 | **************************************************************************/ | ||
50 | |||
51 | |||
52 | /*************************************************************************** | ||
53 | * Global Functions | ||
54 | **************************************************************************/ | ||
55 | |||
56 | #if defined(UNW_DEBUG) | ||
57 | /** Printf wrapper. | ||
58 | * This is used such that alternative outputs for any output can be selected | ||
59 | * by modification of this wrapper function. | ||
60 | */ | ||
61 | void UnwPrintf(const char *format, ...) | ||
62 | { | ||
63 | va_list args; | ||
64 | |||
65 | va_start( args, format ); | ||
66 | vprintf(format, args ); | ||
67 | } | ||
68 | #endif | ||
69 | |||
70 | /** Invalidate all general purpose registers. | ||
71 | */ | ||
72 | void UnwInvalidateRegisterFile(RegData *regFile) | ||
73 | { | ||
74 | Int8 t = 0; | ||
75 | |||
76 | do | ||
77 | { | ||
78 | regFile[t].o = REG_VAL_INVALID; | ||
79 | t++; | ||
80 | } | ||
81 | while(t < 13); | ||
82 | |||
83 | } | ||
84 | |||
85 | |||
86 | /** Initialise the data used for unwinding. | ||
87 | */ | ||
88 | void UnwInitState(UnwState * const state, /**< Pointer to structure to fill. */ | ||
89 | const UnwindCallbacks *cb, /**< Callbacks. */ | ||
90 | void *rptData, /**< Data to pass to report function. */ | ||
91 | Int32 pcValue, /**< PC at which to start unwinding. */ | ||
92 | Int32 spValue) /**< SP at which to start unwinding. */ | ||
93 | { | ||
94 | UnwInvalidateRegisterFile(state->regData); | ||
95 | |||
96 | /* Store the pointer to the callbacks */ | ||
97 | state->cb = cb; | ||
98 | state->reportData = rptData; | ||
99 | |||
100 | /* Setup the SP and PC */ | ||
101 | state->regData[13].v = spValue; | ||
102 | state->regData[13].o = REG_VAL_FROM_CONST; | ||
103 | state->regData[15].v = pcValue; | ||
104 | state->regData[15].o = REG_VAL_FROM_CONST; | ||
105 | |||
106 | UnwPrintd3("\nInitial: PC=0x%08x SP=0x%08x\n", pcValue, spValue); | ||
107 | |||
108 | /* Invalidate all memory addresses */ | ||
109 | memset(state->memData.used, 0, sizeof(state->memData.used)); | ||
110 | } | ||
111 | |||
112 | |||
113 | /** Call the report function to indicate some return address. | ||
114 | * This returns the value of the report function, which if TRUE | ||
115 | * indicates that unwinding may continue. | ||
116 | */ | ||
117 | Boolean UnwReportRetAddr(UnwState * const state, Int32 addr) | ||
118 | { | ||
119 | /* Cast away const from reportData. | ||
120 | * The const is only to prevent the unw module modifying the data. | ||
121 | */ | ||
122 | return state->cb->report((void *)state->reportData, addr); | ||
123 | } | ||
124 | |||
125 | |||
126 | /** Write some register to memory. | ||
127 | * This will store some register and meta data onto the virtual stack. | ||
128 | * The address for the write | ||
129 | * \param state [in/out] The unwinding state. | ||
130 | * \param wAddr [in] The address at which to write the data. | ||
131 | * \param reg [in] The register to store. | ||
132 | * \return TRUE if the write was successful, FALSE otherwise. | ||
133 | */ | ||
134 | Boolean UnwMemWriteRegister(UnwState * const state, | ||
135 | const Int32 addr, | ||
136 | const RegData * const reg) | ||
137 | { | ||
138 | return UnwMemHashWrite(&state->memData, | ||
139 | addr, | ||
140 | reg->v, | ||
141 | M_IsOriginValid(reg->o)); | ||
142 | } | ||
143 | |||
144 | /** Read a register from memory. | ||
145 | * This will read a register from memory, and setup the meta data. | ||
146 | * If the register has been previously written to memory using | ||
147 | * UnwMemWriteRegister, the local hash will be used to return the | ||
148 | * value while respecting whether the data was valid or not. If the | ||
149 | * register was previously written and was invalid at that point, | ||
150 | * REG_VAL_INVALID will be returned in *reg. | ||
151 | * \param state [in] The unwinding state. | ||
152 | * \param addr [in] The address to read. | ||
153 | * \param reg [out] The result, containing the data value and the origin | ||
154 | * which will be REG_VAL_FROM_MEMORY, or REG_VAL_INVALID. | ||
155 | * \return TRUE if the address could be read and *reg has been filled in. | ||
156 | * FALSE is the data could not be read. | ||
157 | */ | ||
158 | Boolean UnwMemReadRegister(UnwState * const state, | ||
159 | const Int32 addr, | ||
160 | RegData * const reg) | ||
161 | { | ||
162 | Boolean tracked; | ||
163 | |||
164 | /* Check if the value can be found in the hash */ | ||
165 | if(UnwMemHashRead(&state->memData, addr, ®->v, &tracked)) | ||
166 | { | ||
167 | reg->o = tracked ? REG_VAL_FROM_MEMORY : REG_VAL_INVALID; | ||
168 | return TRUE; | ||
169 | } | ||
170 | /* Not in the hash, so read from real memory */ | ||
171 | else if(state->cb->readW(addr, ®->v)) | ||
172 | { | ||
173 | reg->o = REG_VAL_FROM_MEMORY; | ||
174 | return TRUE; | ||
175 | } | ||
176 | /* Not in the hash, and failed to read from memory */ | ||
177 | else | ||
178 | { | ||
179 | return FALSE; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | /* END OF FILE */ | ||
diff --git a/lib/unwarminder/unwarm.h b/lib/unwarminder/unwarm.h new file mode 100644 index 0000000000..45508bd39f --- /dev/null +++ b/lib/unwarminder/unwarm.h | |||
@@ -0,0 +1,178 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commerically or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liablity for it's use or misuse - this software is without warranty. | ||
10 | *************************************************************************** | ||
11 | * File Description: Internal interface between the ARM unwinding sub-modules. | ||
12 | **************************************************************************/ | ||
13 | |||
14 | #ifndef UNWARM_H | ||
15 | #define UNWARM_H | ||
16 | |||
17 | /*************************************************************************** | ||
18 | * Nested Include Files | ||
19 | **************************************************************************/ | ||
20 | |||
21 | #include "types.h" | ||
22 | #include "unwarminder.h" | ||
23 | |||
24 | /*************************************************************************** | ||
25 | * Manifest Constants | ||
26 | **************************************************************************/ | ||
27 | |||
28 | /** The maximum number of instructions to interpet in a function. | ||
29 | * Unwinding will be unconditionally stopped and UNWIND_EXHAUSTED returned | ||
30 | * if more than this number of instructions are interpreted in a single | ||
31 | * function without unwinding a stack frame. This prevents infinite loops | ||
32 | * or corrupted program memory from preventing unwinding from progressing. | ||
33 | */ | ||
34 | #define UNW_MAX_INSTR_COUNT 1000 /* originaly it was 100 */ | ||
35 | |||
36 | /** The size of the hash used to track reads and writes to memory. | ||
37 | * This should be a prime value for efficiency. | ||
38 | */ | ||
39 | #define MEM_HASH_SIZE 61 /* originaly it was 31 */ | ||
40 | |||
41 | /*************************************************************************** | ||
42 | * Type Definitions | ||
43 | **************************************************************************/ | ||
44 | |||
45 | typedef enum | ||
46 | { | ||
47 | /** Invalid value. */ | ||
48 | REG_VAL_INVALID = 0x00, | ||
49 | REG_VAL_FROM_STACK = 0x01, | ||
50 | REG_VAL_FROM_MEMORY = 0x02, | ||
51 | REG_VAL_FROM_CONST = 0x04, | ||
52 | REG_VAL_ARITHMETIC = 0x80 | ||
53 | } | ||
54 | RegValOrigin; | ||
55 | |||
56 | |||
57 | /** Type for tracking information about a register. | ||
58 | * This stores the register value, as well as other data that helps unwinding. | ||
59 | */ | ||
60 | typedef struct | ||
61 | { | ||
62 | /** The value held in the register. */ | ||
63 | Int32 v; | ||
64 | |||
65 | /** The origin of the register value. | ||
66 | * This is used to track how the value in the register was loaded. | ||
67 | */ | ||
68 | RegValOrigin o; | ||
69 | } | ||
70 | RegData; | ||
71 | |||
72 | |||
73 | /** Structure used to track reads and writes to memory. | ||
74 | * This structure is used as a hash to store a small number of writes | ||
75 | * to memory. | ||
76 | */ | ||
77 | typedef struct | ||
78 | { | ||
79 | /** Memory contents. */ | ||
80 | Int32 v[MEM_HASH_SIZE]; | ||
81 | |||
82 | /** Address at which v[n] represents. */ | ||
83 | Int32 a[MEM_HASH_SIZE]; | ||
84 | |||
85 | /** Indicates whether the data in v[n] and a[n] is occupied. | ||
86 | * Each bit represents one hash value. | ||
87 | */ | ||
88 | Int8 used[(MEM_HASH_SIZE + 7) / 8]; | ||
89 | |||
90 | /** Indicates whether the data in v[n] is valid. | ||
91 | * This allows a[n] to be set, but for v[n] to be marked as invalid. | ||
92 | * Specifically this is needed for when an untracked register value | ||
93 | * is written to memory. | ||
94 | */ | ||
95 | Int8 tracked[(MEM_HASH_SIZE + 7) / 8]; | ||
96 | } | ||
97 | MemData; | ||
98 | |||
99 | |||
100 | /** Structure that is used to keep track of unwinding meta-data. | ||
101 | * This data is passed between all the unwinding functions. | ||
102 | */ | ||
103 | typedef struct | ||
104 | { | ||
105 | /** The register values and meta-data. */ | ||
106 | RegData regData[16]; | ||
107 | |||
108 | /** Memory tracking data. */ | ||
109 | MemData memData; | ||
110 | |||
111 | /** Pointer to the callback functions */ | ||
112 | const UnwindCallbacks *cb; | ||
113 | |||
114 | /** Pointer to pass to the report function. */ | ||
115 | const void *reportData; | ||
116 | } | ||
117 | UnwState; | ||
118 | |||
119 | /*************************************************************************** | ||
120 | * Macros | ||
121 | **************************************************************************/ | ||
122 | |||
123 | #define M_IsOriginValid(v) (((v) & 0x7f) ? TRUE : FALSE) | ||
124 | #define M_Origin2Str(v) ((v) ? "VALID" : "INVALID") | ||
125 | |||
126 | #if defined(UNW_DEBUG) | ||
127 | #define UnwPrintd1(a) state->cb->printf(a) | ||
128 | #define UnwPrintd2(a,b) state->cb->printf(a,b) | ||
129 | #define UnwPrintd3(a,b,c) state->cb->printf(a,b,c) | ||
130 | #define UnwPrintd4(a,b,c,d) state->cb->printf(a,b,c,d) | ||
131 | #define UnwPrintd5(a,b,c,d,e) state->cb->printf(a,b,c,d,e) | ||
132 | #define UnwPrintd6(a,b,c,d,e,f) state->cb->printf(a,b,c,d,e,f) | ||
133 | #define UnwPrintd7(a,b,c,d,e,f,g) state->cb->printf(a,b,c,d,e,f,g) | ||
134 | #define UnwPrintd8(a,b,c,d,e,f,g,h) state->cb->printf(a,b,c,d,e,f,g,h) | ||
135 | #else | ||
136 | #define UnwPrintd1(a) | ||
137 | #define UnwPrintd2(a,b) | ||
138 | #define UnwPrintd3(a,b,c) | ||
139 | #define UnwPrintd4(a,b,c,d) | ||
140 | #define UnwPrintd5(a,b,c,d,e) | ||
141 | #define UnwPrintd6(a,b,c,d,e,f) | ||
142 | #define UnwPrintd7(a,b,c,d,e,f,g) | ||
143 | #define UnwPrintd8(a,b,c,d,e,f,g,h) | ||
144 | #endif | ||
145 | |||
146 | /*************************************************************************** | ||
147 | * Function Prototypes | ||
148 | **************************************************************************/ | ||
149 | |||
150 | UnwResult UnwStartArm (UnwState * const state); | ||
151 | |||
152 | UnwResult UnwStartThumb (UnwState * const state); | ||
153 | |||
154 | void UnwInvalidateRegisterFile(RegData *regFile); | ||
155 | |||
156 | void UnwInitState (UnwState * const state, | ||
157 | const UnwindCallbacks *cb, | ||
158 | void *rptData, | ||
159 | Int32 pcValue, | ||
160 | Int32 spValue); | ||
161 | |||
162 | Boolean UnwReportRetAddr (UnwState * const state, Int32 addr); | ||
163 | |||
164 | Boolean UnwMemWriteRegister (UnwState * const state, | ||
165 | const Int32 addr, | ||
166 | const RegData * const reg); | ||
167 | |||
168 | Boolean UnwMemReadRegister (UnwState * const state, | ||
169 | const Int32 addr, | ||
170 | RegData * const reg); | ||
171 | |||
172 | void UnwMemHashGC (UnwState * const state); | ||
173 | |||
174 | #endif /* UNWARM_H */ | ||
175 | |||
176 | /* END OF FILE */ | ||
177 | |||
178 | |||
diff --git a/lib/unwarminder/unwarm_arm.c b/lib/unwarminder/unwarm_arm.c new file mode 100644 index 0000000000..0c41c05919 --- /dev/null +++ b/lib/unwarminder/unwarm_arm.c | |||
@@ -0,0 +1,701 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commercially or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liability for it's use or misuse - this software is without warranty. | ||
10 | *************************************************************************** | ||
11 | * File Description: Abstract interpreter for ARM mode. | ||
12 | **************************************************************************/ | ||
13 | |||
14 | #define MODULE_NAME "UNWARM_ARM" | ||
15 | |||
16 | /*************************************************************************** | ||
17 | * Include Files | ||
18 | **************************************************************************/ | ||
19 | |||
20 | #include "types.h" | ||
21 | #if defined(UPGRADE_ARM_STACK_UNWIND) | ||
22 | #include <stdio.h> | ||
23 | #include "unwarm.h" | ||
24 | |||
25 | /*************************************************************************** | ||
26 | * Manifest Constants | ||
27 | **************************************************************************/ | ||
28 | |||
29 | |||
30 | /*************************************************************************** | ||
31 | * Type Definitions | ||
32 | **************************************************************************/ | ||
33 | |||
34 | |||
35 | /*************************************************************************** | ||
36 | * Variables | ||
37 | **************************************************************************/ | ||
38 | |||
39 | |||
40 | /*************************************************************************** | ||
41 | * Macros | ||
42 | **************************************************************************/ | ||
43 | |||
44 | |||
45 | /*************************************************************************** | ||
46 | * Local Functions | ||
47 | **************************************************************************/ | ||
48 | |||
49 | /** Check if some instruction is a data-processing instruction. | ||
50 | * Decodes the passed instruction, checks if it is a data-processing and | ||
51 | * verifies that the parameters and operation really indicate a data- | ||
52 | * processing instruction. This is needed because some parts of the | ||
53 | * instruction space under this instruction can be extended or represent | ||
54 | * other operations such as MRS, MSR. | ||
55 | * | ||
56 | * \param[in] inst The instruction word. | ||
57 | * \retval TRUE Further decoding of the instruction indicates that this is | ||
58 | * a valid data-processing instruction. | ||
59 | * \retval FALSE This is not a data-processing instruction, | ||
60 | */ | ||
61 | static Boolean isDataProc(Int32 instr) | ||
62 | { | ||
63 | Int8 opcode = (instr & 0x01e00000) >> 21; | ||
64 | Boolean S = (instr & 0x00100000) ? TRUE : FALSE; | ||
65 | |||
66 | if((instr & 0xfc000000) != 0xe0000000) | ||
67 | { | ||
68 | return FALSE; | ||
69 | } | ||
70 | else if(!S && opcode >= 8 && opcode <= 11) | ||
71 | { | ||
72 | /* TST, TEQ, CMP and CMN all require S to be set */ | ||
73 | return FALSE; | ||
74 | } | ||
75 | else | ||
76 | { | ||
77 | return TRUE; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | /*************************************************************************** | ||
82 | * Global Functions | ||
83 | **************************************************************************/ | ||
84 | |||
85 | |||
86 | UnwResult UnwStartArm(UnwState * const state) | ||
87 | { | ||
88 | Boolean found = FALSE; | ||
89 | Int16 t = UNW_MAX_INSTR_COUNT; | ||
90 | |||
91 | do | ||
92 | { | ||
93 | Int32 instr; | ||
94 | |||
95 | /* Attempt to read the instruction */ | ||
96 | if(!state->cb->readW(state->regData[15].v, &instr)) | ||
97 | { | ||
98 | return UNWIND_IREAD_W_FAIL; | ||
99 | } | ||
100 | |||
101 | UnwPrintd4("A %x %x %08x:", | ||
102 | state->regData[13].v, state->regData[15].v, instr); | ||
103 | |||
104 | /* Check that the PC is still on Arm alignment */ | ||
105 | if(state->regData[15].v & 0x3) | ||
106 | { | ||
107 | UnwPrintd1("\nError: PC misalignment\n"); | ||
108 | return UNWIND_INCONSISTENT; | ||
109 | } | ||
110 | |||
111 | /* Check that the SP and PC have not been invalidated */ | ||
112 | if(!M_IsOriginValid(state->regData[13].o) || !M_IsOriginValid(state->regData[15].o)) | ||
113 | { | ||
114 | UnwPrintd1("\nError: PC or SP invalidated\n"); | ||
115 | return UNWIND_INCONSISTENT; | ||
116 | } | ||
117 | |||
118 | /* Branch and Exchange (BX) | ||
119 | * This is tested prior to data processing to prevent | ||
120 | * mis-interpretation as an invalid TEQ instruction. | ||
121 | */ | ||
122 | if((instr & 0xfffffff0) == 0xe12fff10) | ||
123 | { | ||
124 | Int8 rn = instr & 0xf; | ||
125 | |||
126 | UnwPrintd4("BX r%d\t ; r%d %s\n", rn, rn, M_Origin2Str(state->regData[rn].o)); | ||
127 | |||
128 | if(!M_IsOriginValid(state->regData[rn].o)) | ||
129 | { | ||
130 | UnwPrintd1("\nUnwind failure: BX to untracked register\n"); | ||
131 | return UNWIND_FAILURE; | ||
132 | } | ||
133 | |||
134 | /* Set the new PC value */ | ||
135 | state->regData[15].v = state->regData[rn].v; | ||
136 | |||
137 | /* Check if the return value is from the stack */ | ||
138 | if(state->regData[rn].o == REG_VAL_FROM_STACK) | ||
139 | { | ||
140 | /* Now have the return address */ | ||
141 | UnwPrintd2(" Return PC=%x\n", state->regData[15].v & (~0x1)); | ||
142 | |||
143 | /* Report the return address */ | ||
144 | if(!UnwReportRetAddr(state, state->regData[rn].v)) | ||
145 | { | ||
146 | return UNWIND_TRUNCATED; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | /* Determine the return mode */ | ||
151 | if(state->regData[rn].v & 0x1) | ||
152 | { | ||
153 | /* Branching to THUMB */ | ||
154 | return UnwStartThumb(state); | ||
155 | } | ||
156 | else | ||
157 | { | ||
158 | /* Branch to ARM */ | ||
159 | |||
160 | /* Account for the auto-increment which isn't needed */ | ||
161 | state->regData[15].v -= 4; | ||
162 | } | ||
163 | } | ||
164 | /* Branch */ | ||
165 | else if((instr & 0xff000000) == 0xea000000) | ||
166 | { | ||
167 | SignedInt32 offset = (instr & 0x00ffffff); | ||
168 | |||
169 | /* Shift value */ | ||
170 | offset = offset << 2; | ||
171 | |||
172 | /* Sign extend if needed */ | ||
173 | if(offset & 0x02000000) | ||
174 | { | ||
175 | offset |= 0xfc000000; | ||
176 | } | ||
177 | |||
178 | UnwPrintd2("B %d\n", offset); | ||
179 | |||
180 | /* Adjust PC */ | ||
181 | state->regData[15].v += offset; | ||
182 | |||
183 | /* Account for pre-fetch, where normally the PC is 8 bytes | ||
184 | * ahead of the instruction just executed. | ||
185 | */ | ||
186 | state->regData[15].v += 4; | ||
187 | |||
188 | } | ||
189 | /* MRS */ | ||
190 | else if((instr & 0xffbf0fff) == 0xe10f0000) | ||
191 | { | ||
192 | #if defined(UNW_DEBUG) | ||
193 | Boolean R = (instr & 0x00400000) ? TRUE : FALSE; | ||
194 | #endif | ||
195 | Int8 rd = (instr & 0x0000f000) >> 12; | ||
196 | |||
197 | UnwPrintd4("MRS r%d,%s\t; r%d invalidated", rd, R ? "SPSR" : "CPSR", rd); | ||
198 | |||
199 | /* Status registers untracked */ | ||
200 | state->regData[rd].o = REG_VAL_INVALID; | ||
201 | } | ||
202 | /* MSR */ | ||
203 | else if((instr & 0xffb0f000) == 0xe120f000) | ||
204 | { | ||
205 | #if defined(UNW_DEBUG) | ||
206 | Boolean R = (instr & 0x00400000) ? TRUE : FALSE; | ||
207 | |||
208 | UnwPrintd2("MSR %s_?, ???", R ? "SPSR" : "CPSR"); | ||
209 | #endif | ||
210 | /* Status registers untracked. | ||
211 | * Potentially this could change processor mode and switch | ||
212 | * banked registers r8-r14. Most likely is that r13 (sp) will | ||
213 | * be banked. However, invalidating r13 will stop unwinding | ||
214 | * when potentially this write is being used to disable/enable | ||
215 | * interrupts (a common case). Therefore no invalidation is | ||
216 | * performed. | ||
217 | */ | ||
218 | } | ||
219 | /* Data processing */ | ||
220 | else if(isDataProc(instr)) | ||
221 | { | ||
222 | Boolean I = (instr & 0x02000000) ? TRUE : FALSE; | ||
223 | Int8 opcode = (instr & 0x01e00000) >> 21; | ||
224 | #if defined(UNW_DEBUG) | ||
225 | Boolean S = (instr & 0x00100000) ? TRUE : FALSE; | ||
226 | #endif | ||
227 | Int8 rn = (instr & 0x000f0000) >> 16; | ||
228 | Int8 rd = (instr & 0x0000f000) >> 12; | ||
229 | Int16 operand2 = (instr & 0x00000fff); | ||
230 | Int32 op2val; | ||
231 | RegValOrigin op2origin; | ||
232 | |||
233 | switch(opcode) | ||
234 | { | ||
235 | case 0: UnwPrintd4("AND%s r%d,r%d,", S ? "S" : "", rd, rn); break; | ||
236 | case 1: UnwPrintd4("EOR%s r%d,r%d,", S ? "S" : "", rd, rn); break; | ||
237 | case 2: UnwPrintd4("SUB%s r%d,r%d,", S ? "S" : "", rd, rn); break; | ||
238 | case 3: UnwPrintd4("RSB%s r%d,r%d,", S ? "S" : "", rd, rn); break; | ||
239 | case 4: UnwPrintd4("ADD%s r%d,r%d,", S ? "S" : "", rd, rn); break; | ||
240 | case 5: UnwPrintd4("ADC%s r%d,r%d,", S ? "S" : "", rd, rn); break; | ||
241 | case 6: UnwPrintd4("SBC%s r%d,r%d,", S ? "S" : "", rd, rn); break; | ||
242 | case 7: UnwPrintd4("RSC%s r%d,r%d,", S ? "S" : "", rd, rn); break; | ||
243 | case 8: UnwPrintd3("TST%s r%d,", S ? "S" : "", rn); break; | ||
244 | case 9: UnwPrintd3("TEQ%s r%d,", S ? "S" : "", rn); break; | ||
245 | case 10: UnwPrintd3("CMP%s r%d,", S ? "S" : "", rn); break; | ||
246 | case 11: UnwPrintd3("CMN%s r%d,", S ? "S" : "", rn); break; | ||
247 | case 12: UnwPrintd3("ORR%s r%d,", S ? "S" : "", rn); break; | ||
248 | case 13: UnwPrintd3("MOV%s r%d,", S ? "S" : "", rd); break; | ||
249 | case 14: UnwPrintd4("BIC%s r%d,r%d", S ? "S" : "", rd, rn); break; | ||
250 | case 15: UnwPrintd3("MVN%s r%d,", S ? "S" : "", rd); break; | ||
251 | } | ||
252 | |||
253 | /* Decode operand 2 */ | ||
254 | if(I) | ||
255 | { | ||
256 | Int8 shiftDist = (operand2 & 0x0f00) >> 8; | ||
257 | Int8 shiftConst = (operand2 & 0x00ff); | ||
258 | |||
259 | /* rotate const right by 2 * shiftDist */ | ||
260 | shiftDist *= 2; | ||
261 | op2val = (shiftConst >> shiftDist) | | ||
262 | (shiftConst << (32 - shiftDist)); | ||
263 | op2origin = REG_VAL_FROM_CONST; | ||
264 | |||
265 | UnwPrintd2("#0x%x", op2val); | ||
266 | } | ||
267 | else | ||
268 | { | ||
269 | /* Register and shift */ | ||
270 | Int8 rm = (operand2 & 0x000f); | ||
271 | Int8 regShift = (operand2 & 0x0010) ? TRUE : FALSE; | ||
272 | Int8 shiftType = (operand2 & 0x0060) >> 5; | ||
273 | Int32 shiftDist; | ||
274 | #if defined(UNW_DEBUG) | ||
275 | const char * const shiftMnu[4] = { "LSL", "LSR", "ASR", "ROR" }; | ||
276 | #endif | ||
277 | UnwPrintd2("r%d ", rm); | ||
278 | |||
279 | /* Get the shift distance */ | ||
280 | if(regShift) | ||
281 | { | ||
282 | Int8 rs = (operand2 & 0x0f00) >> 8; | ||
283 | |||
284 | if(operand2 & 0x00800) | ||
285 | { | ||
286 | UnwPrintd1("\nError: Bit should be zero\n"); | ||
287 | return UNWIND_ILLEGAL_INSTR; | ||
288 | } | ||
289 | else if(rs == 15) | ||
290 | { | ||
291 | UnwPrintd1("\nError: Cannot use R15 with register shift\n"); | ||
292 | return UNWIND_ILLEGAL_INSTR; | ||
293 | } | ||
294 | |||
295 | /* Get shift distance */ | ||
296 | shiftDist = state->regData[rs].v; | ||
297 | op2origin = state->regData[rs].o; | ||
298 | |||
299 | UnwPrintd7("%s r%d\t; r%d %s r%d %s", | ||
300 | shiftMnu[shiftType], rs, | ||
301 | rm, M_Origin2Str(state->regData[rm].o), | ||
302 | rs, M_Origin2Str(state->regData[rs].o)); | ||
303 | } | ||
304 | else | ||
305 | { | ||
306 | shiftDist = (operand2 & 0x0f80) >> 7; | ||
307 | op2origin = REG_VAL_FROM_CONST; | ||
308 | |||
309 | if(shiftDist) | ||
310 | { | ||
311 | UnwPrintd3("%s #%d", | ||
312 | shiftMnu[shiftType], shiftDist); | ||
313 | } | ||
314 | UnwPrintd3("\t; r%d %s", rm, M_Origin2Str(state->regData[rm].o)); | ||
315 | |||
316 | } | ||
317 | |||
318 | /* Apply the shift type to the source register */ | ||
319 | switch(shiftType) | ||
320 | { | ||
321 | case 0: /* logical left */ | ||
322 | op2val = state->regData[rm].v << shiftDist; | ||
323 | break; | ||
324 | case 1: /* logical right */ | ||
325 | |||
326 | if(!regShift && shiftDist == 0) | ||
327 | { | ||
328 | shiftDist = 32; | ||
329 | } | ||
330 | |||
331 | op2val = state->regData[rm].v >> shiftDist; | ||
332 | break; | ||
333 | case 2: /* arithmetic right */ | ||
334 | |||
335 | if(!regShift && shiftDist == 0) | ||
336 | { | ||
337 | shiftDist = 32; | ||
338 | } | ||
339 | |||
340 | if(state->regData[rm].v & 0x80000000) | ||
341 | { | ||
342 | /* Register shifts maybe greater than 32 */ | ||
343 | if(shiftDist >= 32) | ||
344 | { | ||
345 | op2val = 0xffffffff; | ||
346 | } | ||
347 | else | ||
348 | { | ||
349 | op2val = state->regData[rm].v >> shiftDist; | ||
350 | op2val |= 0xffffffff << (32 - shiftDist); | ||
351 | } | ||
352 | } | ||
353 | else | ||
354 | { | ||
355 | op2val = state->regData[rm].v >> shiftDist; | ||
356 | } | ||
357 | break; | ||
358 | case 3: /* rotate right */ | ||
359 | |||
360 | if(!regShift && shiftDist == 0) | ||
361 | { | ||
362 | /* Rotate right with extend. | ||
363 | * This uses the carry bit and so always has an | ||
364 | * untracked result. | ||
365 | */ | ||
366 | op2origin = REG_VAL_INVALID; | ||
367 | op2val = 0; | ||
368 | } | ||
369 | else | ||
370 | { | ||
371 | /* Limit shift distance to 0-31 incase of register shift */ | ||
372 | shiftDist &= 0x1f; | ||
373 | |||
374 | op2val = (state->regData[rm].v >> shiftDist) | | ||
375 | (state->regData[rm].v << (32 - shiftDist)); | ||
376 | } | ||
377 | break; | ||
378 | |||
379 | default: | ||
380 | UnwPrintd2("\nError: Invalid shift type: %d\n", shiftType); | ||
381 | return UNWIND_FAILURE; | ||
382 | } | ||
383 | |||
384 | /* Decide the data origin */ | ||
385 | if(M_IsOriginValid(op2origin) && | ||
386 | M_IsOriginValid(state->regData[rm].o)) | ||
387 | { | ||
388 | op2origin = state->regData[rm].o; | ||
389 | op2origin |= REG_VAL_ARITHMETIC; | ||
390 | } | ||
391 | else | ||
392 | { | ||
393 | op2origin = REG_VAL_INVALID; | ||
394 | } | ||
395 | |||
396 | } | ||
397 | |||
398 | /* Propagate register validity */ | ||
399 | switch(opcode) | ||
400 | { | ||
401 | case 0: /* AND: Rd := Op1 AND Op2 */ | ||
402 | case 1: /* EOR: Rd := Op1 EOR Op2 */ | ||
403 | case 2: /* SUB: Rd:= Op1 - Op2 */ | ||
404 | case 3: /* RSB: Rd:= Op2 - Op1 */ | ||
405 | case 4: /* ADD: Rd:= Op1 + Op2 */ | ||
406 | case 12: /* ORR: Rd:= Op1 OR Op2 */ | ||
407 | case 14: /* BIC: Rd:= Op1 AND NOT Op2 */ | ||
408 | if(!M_IsOriginValid(state->regData[rn].o) || | ||
409 | !M_IsOriginValid(op2origin)) | ||
410 | { | ||
411 | state->regData[rd].o = REG_VAL_INVALID; | ||
412 | } | ||
413 | else | ||
414 | { | ||
415 | state->regData[rd].o = state->regData[rn].o; | ||
416 | state->regData[rd].o |= op2origin; | ||
417 | } | ||
418 | break; | ||
419 | case 5: /* ADC: Rd:= Op1 + Op2 + C */ | ||
420 | case 6: /* SBC: Rd:= Op1 - Op2 + C */ | ||
421 | case 7: /* RSC: Rd:= Op2 - Op1 + C */ | ||
422 | /* CPSR is not tracked */ | ||
423 | state->regData[rd].o = REG_VAL_INVALID; | ||
424 | break; | ||
425 | |||
426 | case 8: /* TST: set condition codes on Op1 AND Op2 */ | ||
427 | case 9: /* TEQ: set condition codes on Op1 EOR Op2 */ | ||
428 | case 10: /* CMP: set condition codes on Op1 - Op2 */ | ||
429 | case 11: /* CMN: set condition codes on Op1 + Op2 */ | ||
430 | break; | ||
431 | |||
432 | |||
433 | case 13: /* MOV: Rd:= Op2 */ | ||
434 | case 15: /* MVN: Rd:= NOT Op2 */ | ||
435 | state->regData[rd].o = op2origin; | ||
436 | break; | ||
437 | } | ||
438 | |||
439 | /* Account for pre-fetch by temporarily adjusting PC */ | ||
440 | if(rn == 15) | ||
441 | { | ||
442 | /* If the shift amount is specified in the instruction, | ||
443 | * the PC will be 8 bytes ahead. If a register is used | ||
444 | * to specify the shift amount the PC will be 12 bytes | ||
445 | * ahead. | ||
446 | */ | ||
447 | if(!I && (operand2 & 0x0010)) | ||
448 | state->regData[rn].v += 12; | ||
449 | else | ||
450 | state->regData[rn].v += 8; | ||
451 | } | ||
452 | |||
453 | /* Compute values */ | ||
454 | switch(opcode) | ||
455 | { | ||
456 | case 0: /* AND: Rd := Op1 AND Op2 */ | ||
457 | state->regData[rd].v = state->regData[rn].v & op2val; | ||
458 | break; | ||
459 | |||
460 | case 1: /* EOR: Rd := Op1 EOR Op2 */ | ||
461 | state->regData[rd].v = state->regData[rn].v ^ op2val; | ||
462 | break; | ||
463 | |||
464 | case 2: /* SUB: Rd:= Op1 - Op2 */ | ||
465 | state->regData[rd].v = state->regData[rn].v - op2val; | ||
466 | break; | ||
467 | case 3: /* RSB: Rd:= Op2 - Op1 */ | ||
468 | state->regData[rd].v = op2val - state->regData[rn].v; | ||
469 | break; | ||
470 | |||
471 | case 4: /* ADD: Rd:= Op1 + Op2 */ | ||
472 | state->regData[rd].v = state->regData[rn].v + op2val; | ||
473 | break; | ||
474 | |||
475 | case 5: /* ADC: Rd:= Op1 + Op2 + C */ | ||
476 | case 6: /* SBC: Rd:= Op1 - Op2 + C */ | ||
477 | case 7: /* RSC: Rd:= Op2 - Op1 + C */ | ||
478 | case 8: /* TST: set condition codes on Op1 AND Op2 */ | ||
479 | case 9: /* TEQ: set condition codes on Op1 EOR Op2 */ | ||
480 | case 10: /* CMP: set condition codes on Op1 - Op2 */ | ||
481 | case 11: /* CMN: set condition codes on Op1 + Op2 */ | ||
482 | UnwPrintd1("\t; ????"); | ||
483 | break; | ||
484 | |||
485 | case 12: /* ORR: Rd:= Op1 OR Op2 */ | ||
486 | state->regData[rd].v = state->regData[rn].v | op2val; | ||
487 | break; | ||
488 | |||
489 | case 13: /* MOV: Rd:= Op2 */ | ||
490 | state->regData[rd].v = op2val; | ||
491 | break; | ||
492 | |||
493 | case 14: /* BIC: Rd:= Op1 AND NOT Op2 */ | ||
494 | state->regData[rd].v = state->regData[rn].v & (~op2val); | ||
495 | break; | ||
496 | |||
497 | case 15: /* MVN: Rd:= NOT Op2 */ | ||
498 | state->regData[rd].v = ~op2val; | ||
499 | break; | ||
500 | } | ||
501 | |||
502 | /* Remove the prefetch offset from the PC */ | ||
503 | if(rd != 15 && rn == 15) | ||
504 | { | ||
505 | if(!I && (operand2 & 0x0010)) | ||
506 | state->regData[rn].v -= 12; | ||
507 | else | ||
508 | state->regData[rn].v -= 8; | ||
509 | } | ||
510 | |||
511 | } | ||
512 | /* Block Data Transfer | ||
513 | * LDM, STM | ||
514 | */ | ||
515 | else if((instr & 0xfe000000) == 0xe8000000) | ||
516 | { | ||
517 | Boolean P = (instr & 0x01000000) ? TRUE : FALSE; | ||
518 | Boolean U = (instr & 0x00800000) ? TRUE : FALSE; | ||
519 | Boolean S = (instr & 0x00400000) ? TRUE : FALSE; | ||
520 | Boolean W = (instr & 0x00200000) ? TRUE : FALSE; | ||
521 | Boolean L = (instr & 0x00100000) ? TRUE : FALSE; | ||
522 | Int16 baseReg = (instr & 0x000f0000) >> 16; | ||
523 | Int16 regList = (instr & 0x0000ffff); | ||
524 | Int32 addr = state->regData[baseReg].v; | ||
525 | Boolean addrValid = M_IsOriginValid(state->regData[baseReg].o); | ||
526 | SignedInt8 r; | ||
527 | |||
528 | #if defined(UNW_DEBUG) | ||
529 | /* Display the instruction */ | ||
530 | if(L) | ||
531 | { | ||
532 | UnwPrintd6("LDM%c%c r%d%s, {reglist}%s\n", | ||
533 | P ? 'E' : 'F', | ||
534 | U ? 'D' : 'A', | ||
535 | baseReg, | ||
536 | W ? "!" : "", | ||
537 | S ? "^" : ""); | ||
538 | } | ||
539 | else | ||
540 | { | ||
541 | UnwPrintd6("STM%c%c r%d%s, {reglist}%s\n", | ||
542 | !P ? 'E' : 'F', | ||
543 | !U ? 'D' : 'A', | ||
544 | baseReg, | ||
545 | W ? "!" : "", | ||
546 | S ? "^" : ""); | ||
547 | } | ||
548 | #endif | ||
549 | /* S indicates that banked registers (untracked) are used, unless | ||
550 | * this is a load including the PC when the S-bit indicates that | ||
551 | * that CPSR is loaded from SPSR (also untracked, but ignored). | ||
552 | */ | ||
553 | if(S && (!L || (regList & (0x01 << 15)) == 0)) | ||
554 | { | ||
555 | UnwPrintd1("\nError:S-bit set requiring banked registers\n"); | ||
556 | return UNWIND_FAILURE; | ||
557 | } | ||
558 | else if(baseReg == 15) | ||
559 | { | ||
560 | UnwPrintd1("\nError: r15 used as base register\n"); | ||
561 | return UNWIND_FAILURE; | ||
562 | } | ||
563 | else if(regList == 0) | ||
564 | { | ||
565 | UnwPrintd1("\nError: Register list empty\n"); | ||
566 | return UNWIND_FAILURE; | ||
567 | } | ||
568 | |||
569 | /* Check if ascending or descending. | ||
570 | * Registers are loaded/stored in order of address. | ||
571 | * i.e. r0 is at the lowest address, r15 at the highest. | ||
572 | */ | ||
573 | r = U ? 0 : 15; | ||
574 | |||
575 | do | ||
576 | { | ||
577 | /* Check if the register is to be transferred */ | ||
578 | if(regList & (0x01 << r)) | ||
579 | { | ||
580 | if(P) addr += U ? 4 : -4; | ||
581 | |||
582 | if(L) | ||
583 | { | ||
584 | if(addrValid) | ||
585 | { | ||
586 | if(!UnwMemReadRegister(state, addr, &state->regData[r])) | ||
587 | { | ||
588 | return UNWIND_DREAD_W_FAIL; | ||
589 | } | ||
590 | |||
591 | /* Update the origin if read via the stack pointer */ | ||
592 | if(M_IsOriginValid(state->regData[r].o) && baseReg == 13) | ||
593 | { | ||
594 | state->regData[r].o = REG_VAL_FROM_STACK; | ||
595 | } | ||
596 | |||
597 | UnwPrintd5(" R%d = 0x%08x\t; r%d %s\n", | ||
598 | r, | ||
599 | state->regData[r].v, | ||
600 | r, | ||
601 | M_Origin2Str(state->regData[r].o)); | ||
602 | } | ||
603 | else | ||
604 | { | ||
605 | /* Invalidate the register as the base reg was invalid */ | ||
606 | state->regData[r].o = REG_VAL_INVALID; | ||
607 | |||
608 | UnwPrintd2(" R%d = ???\n", r); | ||
609 | } | ||
610 | } | ||
611 | else | ||
612 | { | ||
613 | if(addrValid) | ||
614 | { | ||
615 | if(!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r])) | ||
616 | { | ||
617 | return UNWIND_DWRITE_W_FAIL; | ||
618 | } | ||
619 | } | ||
620 | |||
621 | UnwPrintd2(" R%d = 0x%08x\n", r); | ||
622 | } | ||
623 | |||
624 | if(!P) addr += U ? 4 : -4; | ||
625 | } | ||
626 | |||
627 | /* Check the next register */ | ||
628 | r += U ? 1 : -1; | ||
629 | } | ||
630 | while(r >= 0 && r <= 15); | ||
631 | |||
632 | /* Check the writeback bit */ | ||
633 | if(W) state->regData[baseReg].v = addr; | ||
634 | |||
635 | /* Check if the PC was loaded */ | ||
636 | if(L && (regList & (0x01 << 15))) | ||
637 | { | ||
638 | if(!M_IsOriginValid(state->regData[15].o)) | ||
639 | { | ||
640 | /* Return address is not valid */ | ||
641 | UnwPrintd1("PC popped with invalid address\n"); | ||
642 | return UNWIND_FAILURE; | ||
643 | } | ||
644 | else | ||
645 | { | ||
646 | /* Store the return address */ | ||
647 | if(!UnwReportRetAddr(state, state->regData[15].v)) | ||
648 | { | ||
649 | return UNWIND_TRUNCATED; | ||
650 | } | ||
651 | |||
652 | UnwPrintd2(" Return PC=0x%x", state->regData[15].v); | ||
653 | |||
654 | /* Determine the return mode */ | ||
655 | if(state->regData[15].v & 0x1) | ||
656 | { | ||
657 | /* Branching to THUMB */ | ||
658 | return UnwStartThumb(state); | ||
659 | } | ||
660 | else | ||
661 | { | ||
662 | /* Branch to ARM */ | ||
663 | |||
664 | /* Account for the auto-increment which isn't needed */ | ||
665 | state->regData[15].v -= 4; | ||
666 | } | ||
667 | } | ||
668 | } | ||
669 | } | ||
670 | else | ||
671 | { | ||
672 | UnwPrintd1("????"); | ||
673 | |||
674 | /* Unknown/undecoded. May alter some register, so invalidate file */ | ||
675 | UnwInvalidateRegisterFile(state->regData); | ||
676 | } | ||
677 | |||
678 | UnwPrintd1("\n"); | ||
679 | |||
680 | /* Should never hit the reset vector */ | ||
681 | if(state->regData[15].v == 0) return UNWIND_RESET; | ||
682 | |||
683 | /* Check next address */ | ||
684 | state->regData[15].v += 4; | ||
685 | |||
686 | /* Garbage collect the memory hash (used only for the stack) */ | ||
687 | UnwMemHashGC(state); | ||
688 | |||
689 | t--; | ||
690 | if(t == 0) return UNWIND_EXHAUSTED; | ||
691 | |||
692 | } | ||
693 | while(!found); | ||
694 | |||
695 | return UNWIND_UNSUPPORTED; | ||
696 | } | ||
697 | |||
698 | #endif /* UPGRADE_ARM_STACK_UNWIND */ | ||
699 | |||
700 | /* END OF FILE */ | ||
701 | |||
diff --git a/lib/unwarminder/unwarm_thumb.c b/lib/unwarminder/unwarm_thumb.c new file mode 100644 index 0000000000..09b3c9e54b --- /dev/null +++ b/lib/unwarminder/unwarm_thumb.c | |||
@@ -0,0 +1,740 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commercially or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liability for it's use or misuse - this software is without warranty. | ||
10 | *************************************************************************** | ||
11 | * File Description: Abstract interpretation for Thumb mode. | ||
12 | **************************************************************************/ | ||
13 | |||
14 | #define MODULE_NAME "UNWARM_THUMB" | ||
15 | |||
16 | /*************************************************************************** | ||
17 | * Include Files | ||
18 | **************************************************************************/ | ||
19 | |||
20 | #include "types.h" | ||
21 | #if defined(UPGRADE_ARM_STACK_UNWIND) | ||
22 | #include <stdio.h> | ||
23 | #include "unwarm.h" | ||
24 | |||
25 | /*************************************************************************** | ||
26 | * Manifest Constants | ||
27 | **************************************************************************/ | ||
28 | |||
29 | |||
30 | /*************************************************************************** | ||
31 | * Type Definitions | ||
32 | **************************************************************************/ | ||
33 | |||
34 | |||
35 | /*************************************************************************** | ||
36 | * Variables | ||
37 | **************************************************************************/ | ||
38 | |||
39 | |||
40 | /*************************************************************************** | ||
41 | * Macros | ||
42 | **************************************************************************/ | ||
43 | |||
44 | |||
45 | /*************************************************************************** | ||
46 | * Local Functions | ||
47 | **************************************************************************/ | ||
48 | |||
49 | /** Sign extend an 11 bit value. | ||
50 | * This function simply inspects bit 11 of the input \a value, and if | ||
51 | * set, the top 5 bits are set to give a 2's compliment signed value. | ||
52 | * \param value The value to sign extend. | ||
53 | * \return The signed-11 bit value stored in a 16bit data type. | ||
54 | */ | ||
55 | static SignedInt16 signExtend11(Int16 value) | ||
56 | { | ||
57 | if(value & 0x400) | ||
58 | { | ||
59 | value |= 0xf800; | ||
60 | } | ||
61 | |||
62 | return value; | ||
63 | } | ||
64 | |||
65 | |||
66 | /*************************************************************************** | ||
67 | * Global Functions | ||
68 | **************************************************************************/ | ||
69 | |||
70 | |||
71 | UnwResult UnwStartThumb(UnwState * const state) | ||
72 | { | ||
73 | Boolean found = FALSE; | ||
74 | Int16 t = UNW_MAX_INSTR_COUNT; | ||
75 | |||
76 | do | ||
77 | { | ||
78 | Int16 instr; | ||
79 | |||
80 | /* Attempt to read the instruction */ | ||
81 | if(!state->cb->readH(state->regData[15].v & (~0x1), &instr)) | ||
82 | { | ||
83 | return UNWIND_IREAD_H_FAIL; | ||
84 | } | ||
85 | |||
86 | UnwPrintd4("T %x %x %04x:", | ||
87 | state->regData[13].v, state->regData[15].v, instr); | ||
88 | |||
89 | /* Check that the PC is still on Thumb alignment */ | ||
90 | if(!(state->regData[15].v & 0x1)) | ||
91 | { | ||
92 | UnwPrintd1("\nError: PC misalignment\n"); | ||
93 | return UNWIND_INCONSISTENT; | ||
94 | } | ||
95 | |||
96 | /* Check that the SP and PC have not been invalidated */ | ||
97 | if(!M_IsOriginValid(state->regData[13].o) || !M_IsOriginValid(state->regData[15].o)) | ||
98 | { | ||
99 | UnwPrintd1("\nError: PC or SP invalidated\n"); | ||
100 | return UNWIND_INCONSISTENT; | ||
101 | } | ||
102 | |||
103 | /* Format 1: Move shifted register | ||
104 | * LSL Rd, Rs, #Offset5 | ||
105 | * LSR Rd, Rs, #Offset5 | ||
106 | * ASR Rd, Rs, #Offset5 | ||
107 | */ | ||
108 | if((instr & 0xe000) == 0x0000 && (instr & 0x1800) != 0x1800) | ||
109 | { | ||
110 | Boolean signExtend; | ||
111 | Int8 op = (instr & 0x1800) >> 11; | ||
112 | Int8 offset5 = (instr & 0x07c0) >> 6; | ||
113 | Int8 rs = (instr & 0x0038) >> 3; | ||
114 | Int8 rd = (instr & 0x0007); | ||
115 | |||
116 | switch(op) | ||
117 | { | ||
118 | case 0: /* LSL */ | ||
119 | UnwPrintd6("LSL r%d, r%d, #%d\t; r%d %s", rd, rs, offset5, rs, M_Origin2Str(state->regData[rs].o)); | ||
120 | state->regData[rd].v = state->regData[rs].v << offset5; | ||
121 | state->regData[rd].o = state->regData[rs].o; | ||
122 | state->regData[rd].o |= REG_VAL_ARITHMETIC; | ||
123 | break; | ||
124 | |||
125 | case 1: /* LSR */ | ||
126 | UnwPrintd6("LSR r%d, r%d, #%d\t; r%d %s", rd, rs, offset5, rs, M_Origin2Str(state->regData[rs].o)); | ||
127 | state->regData[rd].v = state->regData[rs].v >> offset5; | ||
128 | state->regData[rd].o = state->regData[rs].o; | ||
129 | state->regData[rd].o |= REG_VAL_ARITHMETIC; | ||
130 | break; | ||
131 | |||
132 | case 2: /* ASR */ | ||
133 | UnwPrintd6("ASL r%d, r%d, #%d\t; r%d %s", rd, rs, offset5, rs, M_Origin2Str(state->regData[rs].o)); | ||
134 | |||
135 | signExtend = (state->regData[rs].v & 0x8000) ? TRUE : FALSE; | ||
136 | state->regData[rd].v = state->regData[rs].v >> offset5; | ||
137 | if(signExtend) | ||
138 | { | ||
139 | state->regData[rd].v |= 0xffffffff << (32 - offset5); | ||
140 | } | ||
141 | state->regData[rd].o = state->regData[rs].o; | ||
142 | state->regData[rd].o |= REG_VAL_ARITHMETIC; | ||
143 | break; | ||
144 | } | ||
145 | } | ||
146 | /* Format 2: add/subtract | ||
147 | * ADD Rd, Rs, Rn | ||
148 | * ADD Rd, Rs, #Offset3 | ||
149 | * SUB Rd, Rs, Rn | ||
150 | * SUB Rd, Rs, #Offset3 | ||
151 | */ | ||
152 | else if((instr & 0xf800) == 0x1800) | ||
153 | { | ||
154 | Boolean I = (instr & 0x0400) ? TRUE : FALSE; | ||
155 | Boolean op = (instr & 0x0200) ? TRUE : FALSE; | ||
156 | Int8 rn = (instr & 0x01c0) >> 6; | ||
157 | Int8 rs = (instr & 0x0038) >> 3; | ||
158 | Int8 rd = (instr & 0x0007); | ||
159 | |||
160 | /* Print decoding */ | ||
161 | UnwPrintd6("%s r%d, r%d, %c%d\t;", | ||
162 | op ? "SUB" : "ADD", | ||
163 | rd, rs, | ||
164 | I ? '#' : 'r', | ||
165 | rn); | ||
166 | UnwPrintd5("r%d %s, r%d %s", | ||
167 | rd, M_Origin2Str(state->regData[rd].o), | ||
168 | rs, M_Origin2Str(state->regData[rs].o)); | ||
169 | if(!I) | ||
170 | { | ||
171 | UnwPrintd3(", r%d %s", rn, M_Origin2Str(state->regData[rn].o)); | ||
172 | |||
173 | /* Perform calculation */ | ||
174 | if(op) | ||
175 | { | ||
176 | state->regData[rd].v = state->regData[rs].v - state->regData[rn].v; | ||
177 | } | ||
178 | else | ||
179 | { | ||
180 | state->regData[rd].v = state->regData[rs].v + state->regData[rn].v; | ||
181 | } | ||
182 | |||
183 | /* Propagate the origin */ | ||
184 | if(M_IsOriginValid(state->regData[rs].v) && | ||
185 | M_IsOriginValid(state->regData[rn].v)) | ||
186 | { | ||
187 | state->regData[rd].o = state->regData[rs].o; | ||
188 | state->regData[rd].o |= REG_VAL_ARITHMETIC; | ||
189 | } | ||
190 | else | ||
191 | { | ||
192 | state->regData[rd].o = REG_VAL_INVALID; | ||
193 | } | ||
194 | } | ||
195 | else | ||
196 | { | ||
197 | /* Perform calculation */ | ||
198 | if(op) | ||
199 | { | ||
200 | state->regData[rd].v = state->regData[rs].v - rn; | ||
201 | } | ||
202 | else | ||
203 | { | ||
204 | state->regData[rd].v = state->regData[rs].v + rn; | ||
205 | } | ||
206 | |||
207 | /* Propagate the origin */ | ||
208 | state->regData[rd].o = state->regData[rs].o; | ||
209 | state->regData[rd].o |= REG_VAL_ARITHMETIC; | ||
210 | } | ||
211 | } | ||
212 | /* Format 3: move/compare/add/subtract immediate | ||
213 | * MOV Rd, #Offset8 | ||
214 | * CMP Rd, #Offset8 | ||
215 | * ADD Rd, #Offset8 | ||
216 | * SUB Rd, #Offset8 | ||
217 | */ | ||
218 | else if((instr & 0xe000) == 0x2000) | ||
219 | { | ||
220 | Int8 op = (instr & 0x1800) >> 11; | ||
221 | Int8 rd = (instr & 0x0700) >> 8; | ||
222 | Int8 offset8 = (instr & 0x00ff); | ||
223 | |||
224 | switch(op) | ||
225 | { | ||
226 | case 0: /* MOV */ | ||
227 | UnwPrintd3("MOV r%d, #0x%x", rd, offset8); | ||
228 | state->regData[rd].v = offset8; | ||
229 | state->regData[rd].o = REG_VAL_FROM_CONST; | ||
230 | break; | ||
231 | |||
232 | case 1: /* CMP */ | ||
233 | /* Irrelevant to unwinding */ | ||
234 | UnwPrintd1("CMP ???"); | ||
235 | break; | ||
236 | |||
237 | case 2: /* ADD */ | ||
238 | UnwPrintd5("ADD r%d, #0x%x\t; r%d %s", | ||
239 | rd, offset8, rd, M_Origin2Str(state->regData[rd].o)); | ||
240 | state->regData[rd].v += offset8; | ||
241 | state->regData[rd].o |= REG_VAL_ARITHMETIC; | ||
242 | break; | ||
243 | |||
244 | case 3: /* SUB */ | ||
245 | UnwPrintd5("SUB r%d, #0x%d\t; r%d %s", | ||
246 | rd, offset8, rd, M_Origin2Str(state->regData[rd].o)); | ||
247 | state->regData[rd].v += offset8; | ||
248 | state->regData[rd].o |= REG_VAL_ARITHMETIC; | ||
249 | break; | ||
250 | } | ||
251 | } | ||
252 | /* Format 4: ALU operations | ||
253 | * AND Rd, Rs | ||
254 | * EOR Rd, Rs | ||
255 | * LSL Rd, Rs | ||
256 | * LSR Rd, Rs | ||
257 | * ASR Rd, Rs | ||
258 | * ADC Rd, Rs | ||
259 | * SBC Rd, Rs | ||
260 | * ROR Rd, Rs | ||
261 | * TST Rd, Rs | ||
262 | * NEG Rd, Rs | ||
263 | * CMP Rd, Rs | ||
264 | * CMN Rd, Rs | ||
265 | * ORR Rd, Rs | ||
266 | * MUL Rd, Rs | ||
267 | * BIC Rd, Rs | ||
268 | * MVN Rd, Rs | ||
269 | */ | ||
270 | else if((instr & 0xfc00) == 0x4000) | ||
271 | { | ||
272 | Int8 op = (instr & 0x03c0) >> 6; | ||
273 | Int8 rs = (instr & 0x0038) >> 3; | ||
274 | Int8 rd = (instr & 0x0007); | ||
275 | #if defined(UNW_DEBUG) | ||
276 | static const char * const mnu[16] = | ||
277 | { "AND", "EOR", "LSL", "LSR", | ||
278 | "ASR", "ADC", "SBC", "ROR", | ||
279 | "TST", "NEG", "CMP", "CMN", | ||
280 | "ORR", "MUL", "BIC", "MVN" }; | ||
281 | #endif | ||
282 | /* Print the mnemonic and registers */ | ||
283 | switch(op) | ||
284 | { | ||
285 | case 0: /* AND */ | ||
286 | case 1: /* EOR */ | ||
287 | case 2: /* LSL */ | ||
288 | case 3: /* LSR */ | ||
289 | case 4: /* ASR */ | ||
290 | case 7: /* ROR */ | ||
291 | case 9: /* NEG */ | ||
292 | case 12: /* ORR */ | ||
293 | case 13: /* MUL */ | ||
294 | case 15: /* MVN */ | ||
295 | UnwPrintd8("%s r%d ,r%d\t; r%d %s, r%d %s", | ||
296 | mnu[op], | ||
297 | rd, rs, | ||
298 | rd, M_Origin2Str(state->regData[rd].o), | ||
299 | rs, M_Origin2Str(state->regData[rs].o)); | ||
300 | break; | ||
301 | |||
302 | case 5: /* ADC */ | ||
303 | case 6: /* SBC */ | ||
304 | UnwPrintd4("%s r%d, r%d", mnu[op], rd, rs); | ||
305 | break; | ||
306 | |||
307 | case 8: /* TST */ | ||
308 | case 10: /* CMP */ | ||
309 | case 11: /* CMN */ | ||
310 | /* Irrelevant to unwinding */ | ||
311 | UnwPrintd2("%s ???", mnu[op]); | ||
312 | break; | ||
313 | |||
314 | case 14: /* BIC */ | ||
315 | UnwPrintd5("r%d ,r%d\t; r%d %s", | ||
316 | rd, rs, | ||
317 | rs, M_Origin2Str(state->regData[rs].o)); | ||
318 | state->regData[rd].v &= !state->regData[rs].v; | ||
319 | break; | ||
320 | } | ||
321 | |||
322 | |||
323 | /* Perform operation */ | ||
324 | switch(op) | ||
325 | { | ||
326 | case 0: /* AND */ | ||
327 | state->regData[rd].v &= state->regData[rs].v; | ||
328 | break; | ||
329 | |||
330 | case 1: /* EOR */ | ||
331 | state->regData[rd].v ^= state->regData[rs].v; | ||
332 | break; | ||
333 | |||
334 | case 2: /* LSL */ | ||
335 | state->regData[rd].v <<= state->regData[rs].v; | ||
336 | break; | ||
337 | |||
338 | case 3: /* LSR */ | ||
339 | state->regData[rd].v >>= state->regData[rs].v; | ||
340 | break; | ||
341 | |||
342 | case 4: /* ASR */ | ||
343 | if(state->regData[rd].v & 0x80000000) | ||
344 | { | ||
345 | state->regData[rd].v >>= state->regData[rs].v; | ||
346 | state->regData[rd].v |= 0xffffffff << (32 - state->regData[rs].v); | ||
347 | } | ||
348 | else | ||
349 | { | ||
350 | state->regData[rd].v >>= state->regData[rs].v; | ||
351 | } | ||
352 | |||
353 | break; | ||
354 | |||
355 | case 5: /* ADC */ | ||
356 | case 6: /* SBC */ | ||
357 | case 8: /* TST */ | ||
358 | case 10: /* CMP */ | ||
359 | case 11: /* CMN */ | ||
360 | break; | ||
361 | case 7: /* ROR */ | ||
362 | state->regData[rd].v = (state->regData[rd].v >> state->regData[rs].v) | | ||
363 | (state->regData[rd].v << (32 - state->regData[rs].v)); | ||
364 | break; | ||
365 | |||
366 | case 9: /* NEG */ | ||
367 | state->regData[rd].v = -state->regData[rs].v; | ||
368 | break; | ||
369 | |||
370 | case 12: /* ORR */ | ||
371 | state->regData[rd].v |= state->regData[rs].v; | ||
372 | break; | ||
373 | |||
374 | case 13: /* MUL */ | ||
375 | state->regData[rd].v *= state->regData[rs].v; | ||
376 | break; | ||
377 | |||
378 | case 14: /* BIC */ | ||
379 | state->regData[rd].v &= !state->regData[rs].v; | ||
380 | break; | ||
381 | |||
382 | case 15: /* MVN */ | ||
383 | state->regData[rd].v = !state->regData[rs].v; | ||
384 | break; | ||
385 | } | ||
386 | |||
387 | /* Propagate data origins */ | ||
388 | switch(op) | ||
389 | { | ||
390 | case 0: /* AND */ | ||
391 | case 1: /* EOR */ | ||
392 | case 2: /* LSL */ | ||
393 | case 3: /* LSR */ | ||
394 | case 4: /* ASR */ | ||
395 | case 7: /* ROR */ | ||
396 | case 12: /* ORR */ | ||
397 | case 13: /* MUL */ | ||
398 | case 14: /* BIC */ | ||
399 | if(M_IsOriginValid(state->regData[rs].o) && M_IsOriginValid(state->regData[rs].o)) | ||
400 | { | ||
401 | state->regData[rd].o = state->regData[rs].o; | ||
402 | state->regData[rd].o |= REG_VAL_ARITHMETIC; | ||
403 | } | ||
404 | else | ||
405 | { | ||
406 | state->regData[rd].o = REG_VAL_INVALID; | ||
407 | } | ||
408 | break; | ||
409 | |||
410 | case 5: /* ADC */ | ||
411 | case 6: /* SBC */ | ||
412 | /* C-bit not tracked */ | ||
413 | state->regData[rd].o = REG_VAL_INVALID; | ||
414 | break; | ||
415 | |||
416 | case 8: /* TST */ | ||
417 | case 10: /* CMP */ | ||
418 | case 11: /* CMN */ | ||
419 | /* Nothing propagated */ | ||
420 | break; | ||
421 | |||
422 | case 9: /* NEG */ | ||
423 | case 15: /* MVN */ | ||
424 | state->regData[rd].o = state->regData[rs].o; | ||
425 | state->regData[rd].o |= REG_VAL_ARITHMETIC; | ||
426 | break; | ||
427 | |||
428 | } | ||
429 | |||
430 | } | ||
431 | /* Format 5: Hi register operations/branch exchange | ||
432 | * ADD Rd, Hs | ||
433 | * ADD Hd, Rs | ||
434 | * ADD Hd, Hs | ||
435 | */ | ||
436 | else if((instr & 0xfc00) == 0x4400) | ||
437 | { | ||
438 | Int8 op = (instr & 0x0300) >> 8; | ||
439 | Boolean h1 = (instr & 0x0080) ? TRUE: FALSE; | ||
440 | Boolean h2 = (instr & 0x0040) ? TRUE: FALSE; | ||
441 | Int8 rhs = (instr & 0x0038) >> 3; | ||
442 | Int8 rhd = (instr & 0x0007); | ||
443 | |||
444 | /* Adjust the register numbers */ | ||
445 | if(h2) rhs += 8; | ||
446 | if(h1) rhd += 8; | ||
447 | |||
448 | if(op != 3 && !h1 && !h2) | ||
449 | { | ||
450 | UnwPrintd1("\nError: h1 or h2 must be set for ADD, CMP or MOV\n"); | ||
451 | return UNWIND_ILLEGAL_INSTR; | ||
452 | } | ||
453 | |||
454 | switch(op) | ||
455 | { | ||
456 | case 0: /* ADD */ | ||
457 | UnwPrintd5("ADD r%d, r%d\t; r%d %s", | ||
458 | rhd, rhs, rhs, M_Origin2Str(state->regData[rhs].o)); | ||
459 | state->regData[rhd].v += state->regData[rhs].v; | ||
460 | state->regData[rhd].o = state->regData[rhs].o; | ||
461 | state->regData[rhd].o |= REG_VAL_ARITHMETIC; | ||
462 | break; | ||
463 | |||
464 | case 1: /* CMP */ | ||
465 | /* Irrelevant to unwinding */ | ||
466 | UnwPrintd1("CMP ???"); | ||
467 | break; | ||
468 | |||
469 | case 2: /* MOV */ | ||
470 | UnwPrintd5("MOV r%d, r%d\t; r%d %s", | ||
471 | rhd, rhs, rhd, M_Origin2Str(state->regData[rhs].o)); | ||
472 | state->regData[rhd].v += state->regData[rhs].v; | ||
473 | state->regData[rhd].o = state->regData[rhd].o; | ||
474 | break; | ||
475 | |||
476 | case 3: /* BX */ | ||
477 | UnwPrintd4("BX r%d\t; r%d %s\n", | ||
478 | rhs, rhs, M_Origin2Str(state->regData[rhs].o)); | ||
479 | |||
480 | /* Only follow BX if the data was from the stack */ | ||
481 | if(state->regData[rhs].o == REG_VAL_FROM_STACK) | ||
482 | { | ||
483 | UnwPrintd2(" Return PC=0x%x\n", state->regData[rhs].v & (~0x1)); | ||
484 | |||
485 | /* Report the return address, including mode bit */ | ||
486 | if(!UnwReportRetAddr(state, state->regData[rhs].v)) | ||
487 | { | ||
488 | return UNWIND_TRUNCATED; | ||
489 | } | ||
490 | |||
491 | /* Update the PC */ | ||
492 | state->regData[15].v = state->regData[rhs].v; | ||
493 | |||
494 | /* Determine the new mode */ | ||
495 | if(state->regData[rhs].v & 0x1) | ||
496 | { | ||
497 | /* Branching to THUMB */ | ||
498 | |||
499 | /* Account for the auto-increment which isn't needed */ | ||
500 | state->regData[15].v -= 2; | ||
501 | } | ||
502 | else | ||
503 | { | ||
504 | /* Branch to ARM */ | ||
505 | return UnwStartArm(state); | ||
506 | } | ||
507 | } | ||
508 | else | ||
509 | { | ||
510 | UnwPrintd4("\nError: BX to invalid register: r%d = 0x%x (%s)\n", | ||
511 | rhs, state->regData[rhs].o, M_Origin2Str(state->regData[rhs].o)); | ||
512 | return UNWIND_FAILURE; | ||
513 | } | ||
514 | } | ||
515 | } | ||
516 | /* Format 9: PC-relative load | ||
517 | * LDR Rd,[PC, #imm] | ||
518 | */ | ||
519 | else if((instr & 0xf800) == 0x4800) | ||
520 | { | ||
521 | Int8 rd = (instr & 0x0700) >> 8; | ||
522 | Int8 word8 = (instr & 0x00ff); | ||
523 | Int32 address; | ||
524 | |||
525 | /* Compute load address, adding a word to account for prefetch */ | ||
526 | address = (state->regData[15].v & (~0x3)) + 4 + (word8 << 2); | ||
527 | |||
528 | UnwPrintd3("LDR r%d, 0x%08x", rd, address); | ||
529 | |||
530 | if(!UnwMemReadRegister(state, address, &state->regData[rd])) | ||
531 | { | ||
532 | return UNWIND_DREAD_W_FAIL; | ||
533 | } | ||
534 | } | ||
535 | /* Format 13: add offset to Stack Pointer | ||
536 | * ADD sp,#+imm | ||
537 | * ADD sp,#-imm | ||
538 | */ | ||
539 | else if((instr & 0xff00) == 0xB000) | ||
540 | { | ||
541 | Int8 value = (instr & 0x7f) * 4; | ||
542 | |||
543 | /* Check the negative bit */ | ||
544 | if((instr & 0x80) != 0) | ||
545 | { | ||
546 | UnwPrintd2("SUB sp,#0x%x", value); | ||
547 | state->regData[13].v -= value; | ||
548 | } | ||
549 | else | ||
550 | { | ||
551 | UnwPrintd2("ADD sp,#0x%x", value); | ||
552 | state->regData[13].v += value; | ||
553 | } | ||
554 | } | ||
555 | /* Format 14: push/pop registers | ||
556 | * PUSH {Rlist} | ||
557 | * PUSH {Rlist, LR} | ||
558 | * POP {Rlist} | ||
559 | * POP {Rlist, PC} | ||
560 | */ | ||
561 | else if((instr & 0xf600) == 0xb400) | ||
562 | { | ||
563 | Boolean L = (instr & 0x0800) ? TRUE : FALSE; | ||
564 | Boolean R = (instr & 0x0100) ? TRUE : FALSE; | ||
565 | Int8 rList = (instr & 0x00ff); | ||
566 | |||
567 | if(L) | ||
568 | { | ||
569 | Int8 r; | ||
570 | |||
571 | /* Load from memory: POP */ | ||
572 | UnwPrintd2("POP {Rlist%s}\n", R ? ", PC" : ""); | ||
573 | |||
574 | for(r = 0; r < 8; r++) | ||
575 | { | ||
576 | if(rList & (0x1 << r)) | ||
577 | { | ||
578 | /* Read the word */ | ||
579 | if(!UnwMemReadRegister(state, state->regData[13].v, &state->regData[r])) | ||
580 | { | ||
581 | return UNWIND_DREAD_W_FAIL; | ||
582 | } | ||
583 | |||
584 | /* Alter the origin to be from the stack if it was valid */ | ||
585 | if(M_IsOriginValid(state->regData[r].o)) | ||
586 | { | ||
587 | state->regData[r].o = REG_VAL_FROM_STACK; | ||
588 | } | ||
589 | |||
590 | state->regData[13].v += 4; | ||
591 | |||
592 | UnwPrintd3(" r%d = 0x%08x\n", r, state->regData[r].v); | ||
593 | } | ||
594 | } | ||
595 | |||
596 | /* Check if the PC is to be popped */ | ||
597 | if(R) | ||
598 | { | ||
599 | /* Get the return address */ | ||
600 | if(!UnwMemReadRegister(state, state->regData[13].v, &state->regData[15])) | ||
601 | { | ||
602 | return UNWIND_DREAD_W_FAIL; | ||
603 | } | ||
604 | |||
605 | /* Alter the origin to be from the stack if it was valid */ | ||
606 | if(!M_IsOriginValid(state->regData[15].o)) | ||
607 | { | ||
608 | /* Return address is not valid */ | ||
609 | UnwPrintd1("PC popped with invalid address\n"); | ||
610 | return UNWIND_FAILURE; | ||
611 | } | ||
612 | else | ||
613 | { | ||
614 | /* The bottom bit should have been set to indicate that | ||
615 | * the caller was from Thumb. This would allow return | ||
616 | * by BX for interworking APCS. | ||
617 | */ | ||
618 | if((state->regData[15].v & 0x1) == 0) | ||
619 | { | ||
620 | UnwPrintd2("Warning: Return address not to Thumb: 0x%08x\n", | ||
621 | state->regData[15].v); | ||
622 | |||
623 | /* Pop into the PC will not switch mode */ | ||
624 | return UNWIND_INCONSISTENT; | ||
625 | } | ||
626 | |||
627 | /* Store the return address */ | ||
628 | if(!UnwReportRetAddr(state, state->regData[15].v)) | ||
629 | { | ||
630 | return UNWIND_TRUNCATED; | ||
631 | } | ||
632 | |||
633 | /* Now have the return address */ | ||
634 | UnwPrintd2(" Return PC=%x\n", state->regData[15].v); | ||
635 | |||
636 | /* Update the pc */ | ||
637 | state->regData[13].v += 4; | ||
638 | |||
639 | /* Compensate for the auto-increment, which isn't needed here */ | ||
640 | state->regData[15].v -= 2; | ||
641 | } | ||
642 | } | ||
643 | |||
644 | } | ||
645 | else | ||
646 | { | ||
647 | SignedInt8 r; | ||
648 | |||
649 | /* Store to memory: PUSH */ | ||
650 | UnwPrintd2("PUSH {Rlist%s}", R ? ", LR" : ""); | ||
651 | |||
652 | /* Check if the LR is to be pushed */ | ||
653 | if(R) | ||
654 | { | ||
655 | UnwPrintd3("\n lr = 0x%08x\t; %s", | ||
656 | state->regData[14].v, M_Origin2Str(state->regData[14].o)); | ||
657 | |||
658 | state->regData[13].v -= 4; | ||
659 | |||
660 | /* Write the register value to memory */ | ||
661 | if(!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[14])) | ||
662 | { | ||
663 | return UNWIND_DWRITE_W_FAIL; | ||
664 | } | ||
665 | } | ||
666 | |||
667 | for(r = 7; r >= 0; r--) | ||
668 | { | ||
669 | if(rList & (0x1 << r)) | ||
670 | { | ||
671 | UnwPrintd4("\n r%d = 0x%08x\t; %s", | ||
672 | r, state->regData[r].v, M_Origin2Str(state->regData[r].o)); | ||
673 | |||
674 | state->regData[13].v -= 4; | ||
675 | |||
676 | if(!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r])) | ||
677 | { | ||
678 | return UNWIND_DWRITE_W_FAIL; | ||
679 | } | ||
680 | } | ||
681 | } | ||
682 | } | ||
683 | } | ||
684 | /* Format 18: unconditional branch | ||
685 | * B label | ||
686 | */ | ||
687 | else if((instr & 0xf800) == 0xe000) | ||
688 | { | ||
689 | SignedInt16 branchValue = signExtend11(instr & 0x07ff); | ||
690 | |||
691 | /* Branch distance is twice that specified in the instruction. */ | ||
692 | branchValue *= 2; | ||
693 | |||
694 | UnwPrintd2("B %d \n", branchValue); | ||
695 | |||
696 | /* Update PC */ | ||
697 | state->regData[15].v += branchValue; | ||
698 | |||
699 | /* Need to advance by a word to account for pre-fetch. | ||
700 | * Advance by a half word here, allowing the normal address | ||
701 | * advance to account for the other half word. | ||
702 | */ | ||
703 | state->regData[15].v += 2; | ||
704 | |||
705 | /* Display PC of next instruction */ | ||
706 | UnwPrintd2(" New PC=%x", state->regData[15].v + 2); | ||
707 | |||
708 | } | ||
709 | else | ||
710 | { | ||
711 | UnwPrintd1("????"); | ||
712 | |||
713 | /* Unknown/undecoded. May alter some register, so invalidate file */ | ||
714 | UnwInvalidateRegisterFile(state->regData); | ||
715 | } | ||
716 | |||
717 | UnwPrintd1("\n"); | ||
718 | |||
719 | /* Should never hit the reset vector */ | ||
720 | if(state->regData[15].v == 0) return UNWIND_RESET; | ||
721 | |||
722 | /* Check next address */ | ||
723 | state->regData[15].v += 2; | ||
724 | |||
725 | /* Garbage collect the memory hash (used only for the stack) */ | ||
726 | UnwMemHashGC(state); | ||
727 | |||
728 | t--; | ||
729 | if(t == 0) return UNWIND_EXHAUSTED; | ||
730 | |||
731 | } | ||
732 | while(!found); | ||
733 | |||
734 | return UNWIND_SUCCESS; | ||
735 | } | ||
736 | |||
737 | #endif /* UPGRADE_ARM_STACK_UNWIND */ | ||
738 | |||
739 | /* END OF FILE */ | ||
740 | |||
diff --git a/lib/unwarminder/unwarminder.c b/lib/unwarminder/unwarminder.c new file mode 100644 index 0000000000..68bd9f3986 --- /dev/null +++ b/lib/unwarminder/unwarminder.c | |||
@@ -0,0 +1,78 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commercially or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liability for it's use or misuse - this software is without warranty. | ||
10 | *************************************************************************** | ||
11 | * File Description: Implementation of the interface into the ARM unwinder. | ||
12 | **************************************************************************/ | ||
13 | |||
14 | #define MODULE_NAME "UNWARMINDER" | ||
15 | |||
16 | /*************************************************************************** | ||
17 | * Include Files | ||
18 | **************************************************************************/ | ||
19 | |||
20 | #include "types.h" | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include "unwarminder.h" | ||
24 | #include "unwarm.h" | ||
25 | |||
26 | |||
27 | /*************************************************************************** | ||
28 | * Manifest Constants | ||
29 | **************************************************************************/ | ||
30 | |||
31 | |||
32 | /*************************************************************************** | ||
33 | * Type Definitions | ||
34 | **************************************************************************/ | ||
35 | |||
36 | |||
37 | /*************************************************************************** | ||
38 | * Variables | ||
39 | **************************************************************************/ | ||
40 | |||
41 | |||
42 | /*************************************************************************** | ||
43 | * Macros | ||
44 | **************************************************************************/ | ||
45 | |||
46 | |||
47 | /*************************************************************************** | ||
48 | * Local Functions | ||
49 | **************************************************************************/ | ||
50 | |||
51 | |||
52 | /*************************************************************************** | ||
53 | * Global Functions | ||
54 | **************************************************************************/ | ||
55 | |||
56 | UnwResult UnwindStart(Int32 pcValue, | ||
57 | Int32 spValue, | ||
58 | const UnwindCallbacks *cb, | ||
59 | void *data) | ||
60 | { | ||
61 | UnwState state; | ||
62 | |||
63 | /* Initialise the unwinding state */ | ||
64 | UnwInitState(&state, cb, data, pcValue, spValue); | ||
65 | |||
66 | /* Check the Thumb bit */ | ||
67 | if(pcValue & 0x1) | ||
68 | { | ||
69 | return UnwStartThumb(&state); | ||
70 | } | ||
71 | else | ||
72 | { | ||
73 | return UnwStartArm(&state); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | /* END OF FILE */ | ||
78 | |||
diff --git a/lib/unwarminder/unwarminder.h b/lib/unwarminder/unwarminder.h new file mode 100644 index 0000000000..1c5adbf693 --- /dev/null +++ b/lib/unwarminder/unwarminder.h | |||
@@ -0,0 +1,160 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commerically or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liablity for it's use or misuse - this software is without warranty. | ||
10 | **************************************************************************/ | ||
11 | /** \file | ||
12 | * Interface to the ARM stack unwinding module. | ||
13 | **************************************************************************/ | ||
14 | |||
15 | #ifndef UNWARMINDER_H | ||
16 | #define UNWARMINDER_H | ||
17 | |||
18 | /*************************************************************************** | ||
19 | * Nested Include Files | ||
20 | **************************************************************************/ | ||
21 | |||
22 | #include "types.h" | ||
23 | |||
24 | /*************************************************************************** | ||
25 | * Manifest Constants | ||
26 | **************************************************************************/ | ||
27 | |||
28 | /** \def UNW_DEBUG | ||
29 | * If this define is set, additional information will be produced while | ||
30 | * unwinding the stack to allow debug of the unwind module itself. | ||
31 | */ | ||
32 | /* #define UNW_DEBUG 1 */ | ||
33 | |||
34 | /*************************************************************************** | ||
35 | * Type Definitions | ||
36 | **************************************************************************/ | ||
37 | |||
38 | /** Possible results for UnwindStart to return. | ||
39 | */ | ||
40 | typedef enum UnwResultTag | ||
41 | { | ||
42 | /** Unwinding was successful and complete. */ | ||
43 | UNWIND_SUCCESS = 0, | ||
44 | |||
45 | /** More than UNW_MAX_INSTR_COUNT instructions were interpreted. */ | ||
46 | UNWIND_EXHAUSTED, | ||
47 | |||
48 | /** Unwinding stopped because the reporting func returned FALSE. */ | ||
49 | UNWIND_TRUNCATED, | ||
50 | |||
51 | /** Read data was found to be inconsistent. */ | ||
52 | UNWIND_INCONSISTENT, | ||
53 | |||
54 | /** Unsupported instruction or data found. */ | ||
55 | UNWIND_UNSUPPORTED, | ||
56 | |||
57 | /** General failure. */ | ||
58 | UNWIND_FAILURE, | ||
59 | |||
60 | /** Illegal instruction. */ | ||
61 | UNWIND_ILLEGAL_INSTR, | ||
62 | |||
63 | /** Unwinding hit the reset vector. */ | ||
64 | UNWIND_RESET, | ||
65 | |||
66 | /** Failed read for an instruction word. */ | ||
67 | UNWIND_IREAD_W_FAIL, | ||
68 | |||
69 | /** Failed read for an instruction half-word. */ | ||
70 | UNWIND_IREAD_H_FAIL, | ||
71 | |||
72 | /** Failed read for an instruction byte. */ | ||
73 | UNWIND_IREAD_B_FAIL, | ||
74 | |||
75 | /** Failed read for a data word. */ | ||
76 | UNWIND_DREAD_W_FAIL, | ||
77 | |||
78 | /** Failed read for a data half-word. */ | ||
79 | UNWIND_DREAD_H_FAIL, | ||
80 | |||
81 | /** Failed read for a data byte. */ | ||
82 | UNWIND_DREAD_B_FAIL, | ||
83 | |||
84 | /** Failed write for a data word. */ | ||
85 | UNWIND_DWRITE_W_FAIL | ||
86 | } | ||
87 | UnwResult; | ||
88 | |||
89 | /** Type for function pointer for result callback. | ||
90 | * The function is passed two parameters, the first is a void * pointer, | ||
91 | * and the second is the return address of the function. The bottom bit | ||
92 | * of the passed address indicates the execution mode; if it is set, | ||
93 | * the execution mode at the return address is Thumb, otherwise it is | ||
94 | * ARM. | ||
95 | * | ||
96 | * The return value of this function determines whether unwinding should | ||
97 | * continue or not. If TRUE is returned, unwinding will continue and the | ||
98 | * report function maybe called again in future. If FALSE is returned, | ||
99 | * unwinding will stop with UnwindStart() returning UNWIND_TRUNCATED. | ||
100 | */ | ||
101 | typedef Boolean (*UnwindReportFunc)(void *data, | ||
102 | Int32 address); | ||
103 | |||
104 | /** Structure that holds memory callback function pointers. | ||
105 | */ | ||
106 | typedef struct UnwindCallbacksTag | ||
107 | { | ||
108 | /** Report an unwind result. */ | ||
109 | UnwindReportFunc report; | ||
110 | |||
111 | /** Read a 32 bit word from memory. | ||
112 | * The memory address to be read is passed as \a address, and | ||
113 | * \a *val is expected to be populated with the read value. | ||
114 | * If the address cannot or should not be read, FALSE can be | ||
115 | * returned to indicate that unwinding should stop. If TRUE | ||
116 | * is returned, \a *val is assumed to be valid and unwinding | ||
117 | * will continue. | ||
118 | */ | ||
119 | Boolean (*readW)(const Int32 address, Int32 *val); | ||
120 | |||
121 | /** Read a 16 bit half-word from memory. | ||
122 | * This function has the same usage as for readW, but is expected | ||
123 | * to read only a 16 bit value. | ||
124 | */ | ||
125 | Boolean (*readH)(const Int32 address, Int16 *val); | ||
126 | |||
127 | /** Read a byte from memory. | ||
128 | * This function has the same usage as for readW, but is expected | ||
129 | * to read only an 8 bit value. | ||
130 | */ | ||
131 | Boolean (*readB)(const Int32 address, Int8 *val); | ||
132 | |||
133 | #if defined(UNW_DEBUG) | ||
134 | /** Print a formatted line for debug. */ | ||
135 | int (*printf)(const char *format, ...); | ||
136 | #endif | ||
137 | |||
138 | } | ||
139 | UnwindCallbacks; | ||
140 | |||
141 | /*************************************************************************** | ||
142 | * Macros | ||
143 | **************************************************************************/ | ||
144 | |||
145 | /*************************************************************************** | ||
146 | * Function Prototypes | ||
147 | **************************************************************************/ | ||
148 | |||
149 | /** Start unwinding the current stack. | ||
150 | * This will unwind the stack starting at the PC value supplied and | ||
151 | * the stack pointer value supplied. | ||
152 | */ | ||
153 | UnwResult UnwindStart(Int32 pcValue, | ||
154 | Int32 spValue, | ||
155 | const UnwindCallbacks *cb, | ||
156 | void *data); | ||
157 | |||
158 | #endif /* UNWARMINDER_H */ | ||
159 | |||
160 | /* END OF FILE */ | ||
diff --git a/lib/unwarminder/unwarminder.make b/lib/unwarminder/unwarminder.make new file mode 100644 index 0000000000..f570d49640 --- /dev/null +++ b/lib/unwarminder/unwarminder.make | |||
@@ -0,0 +1,21 @@ | |||
1 | # __________ __ ___. | ||
2 | # Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
3 | # Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
4 | # Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
5 | # Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
6 | # \/ \/ \/ \/ \/ | ||
7 | # | ||
8 | |||
9 | UNWARM_DIR = $(ROOTDIR)/lib/unwarminder | ||
10 | UNWARM_SRC = $(call preprocess, $(UNWARM_DIR)/SOURCES) | ||
11 | UNWARM_OBJ := $(call c2obj, $(UNWARM_SRC)) | ||
12 | |||
13 | OTHER_SRC += $(UNWARM_SRC) | ||
14 | |||
15 | UNWARMINDER = $(BUILDDIR)/lib/libunwarminder.a | ||
16 | |||
17 | INCLUDES += -I$(UNWARM_DIR) | ||
18 | |||
19 | $(UNWARMINDER): $(UNWARM_OBJ) | ||
20 | $(SILENT)$(shell rm -f $@) | ||
21 | $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null | ||
diff --git a/lib/unwarminder/unwarmmem.c b/lib/unwarminder/unwarmmem.c new file mode 100644 index 0000000000..5991720412 --- /dev/null +++ b/lib/unwarminder/unwarmmem.c | |||
@@ -0,0 +1,175 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commerically or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liablity for it's use or misuse - this software is without warranty. | ||
10 | *************************************************************************** | ||
11 | * File Description: Implementation of the memory tracking sub-system. | ||
12 | **************************************************************************/ | ||
13 | |||
14 | #define MODULE_NAME "UNWARMMEM" | ||
15 | |||
16 | /*************************************************************************** | ||
17 | * Include Files | ||
18 | **************************************************************************/ | ||
19 | |||
20 | #include "types.h" | ||
21 | #include <stdio.h> | ||
22 | #include "unwarmmem.h" | ||
23 | #include "unwarm.h" | ||
24 | |||
25 | /*************************************************************************** | ||
26 | * Manifest Constants | ||
27 | **************************************************************************/ | ||
28 | |||
29 | |||
30 | /*************************************************************************** | ||
31 | * Type Definitions | ||
32 | **************************************************************************/ | ||
33 | |||
34 | |||
35 | /*************************************************************************** | ||
36 | * Variables | ||
37 | **************************************************************************/ | ||
38 | |||
39 | |||
40 | /*************************************************************************** | ||
41 | * Macros | ||
42 | **************************************************************************/ | ||
43 | |||
44 | |||
45 | #define M_IsIdxUsed(a, v) (((a)[v >> 3] & (1 << (v & 0x7))) ? TRUE : FALSE) | ||
46 | |||
47 | #define M_SetIdxUsed(a, v) ((a)[v >> 3] |= (1 << (v & 0x7))) | ||
48 | |||
49 | #define M_ClrIdxUsed(a, v) ((a)[v >> 3] &= ~(1 << (v & 0x7))) | ||
50 | |||
51 | /*************************************************************************** | ||
52 | * Local Functions | ||
53 | **************************************************************************/ | ||
54 | |||
55 | /** Search the memory hash to see if an entry is stored in the hash already. | ||
56 | * This will search the hash and either return the index where the item is | ||
57 | * stored, or -1 if the item was not found. | ||
58 | */ | ||
59 | static SignedInt16 memHashIndex(MemData * const memData, | ||
60 | const Int32 addr) | ||
61 | { | ||
62 | const Int16 v = addr % MEM_HASH_SIZE; | ||
63 | Int16 s = v; | ||
64 | |||
65 | do | ||
66 | { | ||
67 | /* Check if the element is occupied */ | ||
68 | if(M_IsIdxUsed(memData->used, s)) | ||
69 | { | ||
70 | /* Check if it is occupied with the sought data */ | ||
71 | if(memData->a[s] == addr) | ||
72 | { | ||
73 | return s; | ||
74 | } | ||
75 | } | ||
76 | else | ||
77 | { | ||
78 | /* Item is free, this is where the item should be stored */ | ||
79 | return s; | ||
80 | } | ||
81 | |||
82 | /* Search the next entry */ | ||
83 | s++; | ||
84 | if(s > MEM_HASH_SIZE) | ||
85 | { | ||
86 | s = 0; | ||
87 | } | ||
88 | } | ||
89 | while(s != v); | ||
90 | |||
91 | /* Search failed, hash is full and the address not stored */ | ||
92 | return -1; | ||
93 | } | ||
94 | |||
95 | |||
96 | |||
97 | /*************************************************************************** | ||
98 | * Global Functions | ||
99 | **************************************************************************/ | ||
100 | |||
101 | Boolean UnwMemHashRead(MemData * const memData, | ||
102 | Int32 addr, | ||
103 | Int32 * const data, | ||
104 | Boolean * const tracked) | ||
105 | { | ||
106 | SignedInt16 i = memHashIndex(memData, addr); | ||
107 | |||
108 | if(i >= 0 && M_IsIdxUsed(memData->used, i) && memData->a[i] == addr) | ||
109 | { | ||
110 | *data = memData->v[i]; | ||
111 | *tracked = M_IsIdxUsed(memData->tracked, i); | ||
112 | return TRUE; | ||
113 | } | ||
114 | else | ||
115 | { | ||
116 | /* Address not found in the hash */ | ||
117 | return FALSE; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | Boolean UnwMemHashWrite(MemData * const memData, | ||
122 | Int32 addr, | ||
123 | Int32 val, | ||
124 | Boolean valValid) | ||
125 | { | ||
126 | SignedInt16 i = memHashIndex(memData, addr); | ||
127 | |||
128 | if(i < 0) | ||
129 | { | ||
130 | /* Hash full */ | ||
131 | return FALSE; | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | /* Store the item */ | ||
136 | memData->a[i] = addr; | ||
137 | M_SetIdxUsed(memData->used, i); | ||
138 | |||
139 | if(valValid) | ||
140 | { | ||
141 | memData->v[i] = val; | ||
142 | M_SetIdxUsed(memData->tracked, i); | ||
143 | } | ||
144 | else | ||
145 | { | ||
146 | #if defined(UNW_DEBUG) | ||
147 | memData->v[i] = 0xdeadbeef; | ||
148 | #endif | ||
149 | M_ClrIdxUsed(memData->tracked, i); | ||
150 | } | ||
151 | |||
152 | return TRUE; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | |||
157 | void UnwMemHashGC(UnwState * const state) | ||
158 | { | ||
159 | const Int32 minValidAddr = state->regData[13].v; | ||
160 | MemData * const memData = &state->memData; | ||
161 | Int16 t; | ||
162 | |||
163 | for(t = 0; t < MEM_HASH_SIZE; t++) | ||
164 | { | ||
165 | if(M_IsIdxUsed(memData->used, t) && (memData->a[t] < minValidAddr)) | ||
166 | { | ||
167 | UnwPrintd3("MemHashGC: Free elem %d, addr 0x%08x\n", | ||
168 | t, memData->a[t]); | ||
169 | |||
170 | M_ClrIdxUsed(memData->used, t); | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | |||
175 | /* END OF FILE */ | ||
diff --git a/lib/unwarminder/unwarmmem.h b/lib/unwarminder/unwarmmem.h new file mode 100644 index 0000000000..4c02d284d7 --- /dev/null +++ b/lib/unwarminder/unwarmmem.h | |||
@@ -0,0 +1,57 @@ | |||
1 | /*************************************************************************** | ||
2 | * ARM Stack Unwinder, Michael.McTernan.2001@cs.bris.ac.uk | ||
3 | * | ||
4 | * This program is PUBLIC DOMAIN. | ||
5 | * This means that there is no copyright and anyone is able to take a copy | ||
6 | * for free and use it as they wish, with or without modifications, and in | ||
7 | * any context, commerically or otherwise. The only limitation is that I | ||
8 | * don't guarantee that the software is fit for any purpose or accept any | ||
9 | * liablity for it's use or misuse - this software is without warranty. | ||
10 | *************************************************************************** | ||
11 | * File Description: Interface to the memory tracking sub-system. | ||
12 | **************************************************************************/ | ||
13 | |||
14 | #ifndef UNWARMMEM_H | ||
15 | #define UNWARMMEM_H | ||
16 | |||
17 | /*************************************************************************** | ||
18 | * Nested Include Files | ||
19 | **************************************************************************/ | ||
20 | |||
21 | #include "types.h" | ||
22 | #include "unwarm.h" | ||
23 | |||
24 | /*************************************************************************** | ||
25 | * Manifest Constants | ||
26 | **************************************************************************/ | ||
27 | |||
28 | |||
29 | /*************************************************************************** | ||
30 | * Type Definitions | ||
31 | **************************************************************************/ | ||
32 | |||
33 | |||
34 | /*************************************************************************** | ||
35 | * Macros | ||
36 | **************************************************************************/ | ||
37 | |||
38 | |||
39 | /*************************************************************************** | ||
40 | * Function Prototypes | ||
41 | **************************************************************************/ | ||
42 | |||
43 | Boolean UnwMemHashRead (MemData * const memData, | ||
44 | Int32 addr, | ||
45 | Int32 * const data, | ||
46 | Boolean * const tracked); | ||
47 | |||
48 | Boolean UnwMemHashWrite (MemData * const memData, | ||
49 | Int32 addr, | ||
50 | Int32 val, | ||
51 | Boolean valValid); | ||
52 | |||
53 | void UnwMemHashGC (UnwState * const state); | ||
54 | |||
55 | #endif | ||
56 | |||
57 | /* END OF FILE */ | ||
diff --git a/tools/root.make b/tools/root.make index 3cdee2caf7..74279064bf 100644 --- a/tools/root.make +++ b/tools/root.make | |||
@@ -78,6 +78,10 @@ ifndef APP_TYPE | |||
78 | endif | 78 | endif |
79 | endif | 79 | endif |
80 | 80 | ||
81 | ifeq (arm,$(ARCH)) | ||
82 | include $(ROOTDIR)/lib/unwarminder/unwarminder.make | ||
83 | endif | ||
84 | |||
81 | ifneq (,$(findstring bootloader,$(APPSDIR))) | 85 | ifneq (,$(findstring bootloader,$(APPSDIR))) |
82 | include $(APPSDIR)/bootloader.make | 86 | include $(APPSDIR)/bootloader.make |
83 | else ifneq (,$(findstring bootbox,$(APPSDIR))) | 87 | else ifneq (,$(findstring bootbox,$(APPSDIR))) |
@@ -170,6 +174,12 @@ ifeq (,$(findstring bootloader,$(APPSDIR))) | |||
170 | 174 | ||
171 | OBJ += $(LANG_O) | 175 | OBJ += $(LANG_O) |
172 | 176 | ||
177 | ifeq (arm,$(ARCH)) | ||
178 | UNWARMINDER_LINK := -lunwarminder | ||
179 | else | ||
180 | UNWARMINDER_LINK := | ||
181 | endif | ||
182 | |||
173 | ifndef APP_TYPE | 183 | ifndef APP_TYPE |
174 | 184 | ||
175 | ## target build | 185 | ## target build |
@@ -185,8 +195,6 @@ else | |||
185 | LIBARMSUPPORT_LINK := | 195 | LIBARMSUPPORT_LINK := |
186 | endif | 196 | endif |
187 | 197 | ||
188 | |||
189 | |||
190 | $(LINKRAM): $(RAMLDS) $(CONFIGFILE) | 198 | $(LINKRAM): $(RAMLDS) $(CONFIGFILE) |
191 | $(call PRINTS,PP $(@F)) | 199 | $(call PRINTS,PP $(@F)) |
192 | $(call preprocess2file,$<,$@,-DLOADADDRESS=$(LOADADDRESS)) | 200 | $(call preprocess2file,$<,$@,-DLOADADDRESS=$(LOADADDRESS)) |
@@ -195,21 +203,21 @@ $(LINKROM): $(ROMLDS) | |||
195 | $(call PRINTS,PP $(@F)) | 203 | $(call PRINTS,PP $(@F)) |
196 | $(call preprocess2file,$<,$@,-DLOADADDRESS=$(LOADADDRESS)) | 204 | $(call preprocess2file,$<,$@,-DLOADADDRESS=$(LOADADDRESS)) |
197 | 205 | ||
198 | $(BUILDDIR)/rockbox.elf : $$(OBJ) $$(FIRMLIB) $$(VOICESPEEXLIB) $$(SKINLIB) $$(LIBARMSUPPORT) $$(LINKRAM) | 206 | $(BUILDDIR)/rockbox.elf : $$(OBJ) $$(FIRMLIB) $$(VOICESPEEXLIB) $$(SKINLIB) $$(LIBARMSUPPORT) $$(UNWARMINDER) $$(LINKRAM) |
199 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \ | 207 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \ |
200 | -L$(BUILDDIR)/firmware -lfirmware \ | 208 | -L$(BUILDDIR)/firmware -lfirmware \ |
201 | -L$(BUILDDIR)/lib -lskin_parser $(LIBARMSUPPORT_LINK) \ | 209 | -L$(BUILDDIR)/lib -lskin_parser $(LIBARMSUPPORT_LINK) \ |
202 | -L$(BUILDDIR)/apps/codecs $(VOICESPEEXLIB:lib%.a=-l%) \ | 210 | $(UNWARMINDER_LINK) -L$(BUILDDIR)/apps/codecs \ |
203 | -lgcc $(BOOTBOXLDOPTS) $(GLOBAL_LDOPTS) \ | 211 | $(VOICESPEEXLIB:lib%.a=-l%) -lgcc $(BOOTBOXLDOPTS) \ |
204 | -T$(LINKRAM) -Wl,-Map,$(BUILDDIR)/rockbox.map | 212 | $(GLOBAL_LDOPTS) -T$(LINKRAM) -Wl,-Map,$(BUILDDIR)/rockbox.map |
205 | 213 | ||
206 | $(BUILDDIR)/rombox.elf : $$(OBJ) $$(FIRMLIB) $$(VOICESPEEXLIB) $$(SKINLIB) $$(LIBARMSUPPORT) $$(LINKROM) | 214 | $(BUILDDIR)/rombox.elf : $$(OBJ) $$(FIRMLIB) $$(VOICESPEEXLIB) $$(SKINLIB) $$(LIBARMSUPPORT) $$(UNWARMINDER) $$(LINKROM) |
207 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \ | 215 | $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -Os -nostdlib -o $@ $(OBJ) \ |
208 | -L$(BUILDDIR)/firmware -lfirmware \ | 216 | -L$(BUILDDIR)/firmware -lfirmware \ |
209 | -L$(BUILDDIR)/lib -lskin_parser $(LIBARMSUPPORT_LINK) \ | 217 | -L$(BUILDDIR)/lib -lskin_parser $(LIBARMSUPPORT_LINK) \ |
210 | -L$(BUILDDIR)/apps/codecs $(VOICESPEEXLIB:lib%.a=-l%) \ | 218 | $(UNWARMINDER_LINK) -L$(BUILDDIR)/apps/codecs \ |
211 | -lgcc $(GLOBAL_LDOPTS) \ | 219 | $(VOICESPEEXLIB:lib%.a=-l%) -lgcc $(GLOBAL_LDOPTS) \ |
212 | -T$(LINKROM) -Wl,-Map,$(BUILDDIR)/rombox.map | 220 | -T$(LINKROM) -Wl,-Map,$(BUILDDIR)/rombox.map |
213 | 221 | ||
214 | $(BUILDDIR)/rockbox.bin : $(BUILDDIR)/rockbox.elf | 222 | $(BUILDDIR)/rockbox.bin : $(BUILDDIR)/rockbox.elf |
215 | $(call PRINTS,OC $(@F))$(OC) $(if $(filter yes, $(USE_ELF)), -S -x, -O binary) $< $@ | 223 | $(call PRINTS,OC $(@F))$(OC) $(if $(filter yes, $(USE_ELF)), -S -x, -O binary) $< $@ |
diff --git a/uisimulator/uisimulator.make b/uisimulator/uisimulator.make index 03bf9da2bd..fc58f3a6c1 100644 --- a/uisimulator/uisimulator.make +++ b/uisimulator/uisimulator.make | |||
@@ -30,7 +30,7 @@ $(SIMLIB): $$(SIMOBJ) $(UIBMP) | |||
30 | $(SILENT)$(shell rm -f $@) | 30 | $(SILENT)$(shell rm -f $@) |
31 | $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null | 31 | $(call PRINTS,AR $(@F))$(AR) rcs $@ $^ >/dev/null |
32 | 32 | ||
33 | $(BUILDDIR)/$(BINARY): $$(OBJ) $(SIMLIB) $(VOICESPEEXLIB) $(FIRMLIB) $(SKINLIB) | 33 | $(BUILDDIR)/$(BINARY): $$(OBJ) $(SIMLIB) $(VOICESPEEXLIB) $(FIRMLIB) $(SKINLIB) $(UNWARMINDER) |
34 | $(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(SIMLIB) $(LDOPTS) $(GLOBAL_LDOPTS) \ | 34 | $(call PRINTS,LD $(BINARY))$(CC) -o $@ $^ $(SIMLIB) $(LDOPTS) $(GLOBAL_LDOPTS) \ |
35 | -Wl,-Map,$(BUILDDIR)/rockbox.map | 35 | -Wl,-Map,$(BUILDDIR)/rockbox.map |
36 | 36 | ||