summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/android.make2
-rw-r--r--firmware/SOURCES2
-rw-r--r--firmware/export/panic.h6
-rw-r--r--firmware/panic.c32
-rw-r--r--firmware/target/arm/system-arm.c19
-rw-r--r--firmware/target/hosted/ypr0/ypr0.make2
-rw-r--r--lib/unwarminder/SOURCES7
-rw-r--r--lib/unwarminder/backtrace.c124
-rw-r--r--lib/unwarminder/backtrace.h57
-rw-r--r--lib/unwarminder/get_sp.S34
-rw-r--r--lib/unwarminder/get_sp.h1
-rw-r--r--lib/unwarminder/types.h39
-rw-r--r--lib/unwarminder/unwarm.c183
-rw-r--r--lib/unwarminder/unwarm.h178
-rw-r--r--lib/unwarminder/unwarm_arm.c701
-rw-r--r--lib/unwarminder/unwarm_thumb.c740
-rw-r--r--lib/unwarminder/unwarminder.c78
-rw-r--r--lib/unwarminder/unwarminder.h160
-rw-r--r--lib/unwarminder/unwarminder.make21
-rw-r--r--lib/unwarminder/unwarmmem.c175
-rw-r--r--lib/unwarminder/unwarmmem.h57
-rw-r--r--tools/root.make28
-rw-r--r--uisimulator/uisimulator.make2
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)
114classes: $(R_OBJ) $(JAVA_OBJ) 114classes: $(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
433target/coldfire/ata-as-coldfire.S 433target/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)
29void panicf( const char *fmt, ... ) __attribute__ ((naked)) ATTRIBUTE_PRINTF(1, 2);
30#else
27void panicf( const char *fmt, ... ) ATTRIBUTE_PRINTF(1, 2); 31void 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
34static char panic_buf[128]; 39static 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)
43void panicf_f( const char *fmt, ...);
44
45/* we wrap panicf() here with naked function to catch SP value */
46void 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 */
57void 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
40void panicf( const char *fmt, ...) 67void 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
28static const char* const uiename[] = { 31static 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 */
39void NORETURN_ATTR UIE(unsigned int pc, unsigned int num) 42void 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 @@
1backtrace.c
2get_sp.S
3unwarm_arm.c
4unwarm.c
5unwarminder.c
6unwarmmem.c
7unwarm_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
31static Boolean CliReport(void *data, Int32 address);
32static Boolean CliReadW(Int32 a, Int32 *v);
33static Boolean CliReadH(Int32 a, Int16 *v);
34static Boolean CliReadB(Int32 a, Int8 *v);
35
36/***************************************************************************
37 * Variables
38 ***************************************************************************/
39
40/* Table of function pointers for passing to the unwinder */
41const 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 ***************************************************************************/
73static 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
87static Boolean CliReadW(const Int32 a, Int32 *v)
88{
89 *v = *(Int32 *)a;
90 return TRUE;
91}
92
93static Boolean CliReadH(const Int32 a, Int16 *v)
94{
95 *v = *(Int16 *)a;
96 return TRUE;
97}
98
99static Boolean CliReadB(const Int32 a, Int8 *v)
100{
101 *v = *(Int8 *)a;
102 return TRUE;
103}
104
105Boolean CliInvalidateW(const Int32 a)
106{
107 *(Int32 *)a = 0xdeadbeef;
108 return TRUE;
109}
110
111void 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 */
39typedef struct
40{
41 Int16 frameCount;
42 Int32 address[32];
43}
44CliStack;
45
46/***************************************************************************
47 * Variables
48 ***************************************************************************/
49
50extern const UnwindCallbacks cliCallbacks;
51
52void 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
23call_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 */
28get_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
21typedef unsigned char Int8;
22typedef unsigned short Int16;
23typedef unsigned int Int32;
24
25
26typedef signed char SignedInt8;
27typedef signed short SignedInt16;
28typedef signed int SignedInt32;
29
30
31typedef 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 */
61void 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 */
72void 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 */
88void 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 */
117Boolean 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 */
134Boolean 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 */
158Boolean 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, &reg->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, &reg->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
45typedef 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}
54RegValOrigin;
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 */
60typedef 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}
70RegData;
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 */
77typedef 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}
97MemData;
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 */
103typedef 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}
117UnwState;
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
150UnwResult UnwStartArm (UnwState * const state);
151
152UnwResult UnwStartThumb (UnwState * const state);
153
154void UnwInvalidateRegisterFile(RegData *regFile);
155
156void UnwInitState (UnwState * const state,
157 const UnwindCallbacks *cb,
158 void *rptData,
159 Int32 pcValue,
160 Int32 spValue);
161
162Boolean UnwReportRetAddr (UnwState * const state, Int32 addr);
163
164Boolean UnwMemWriteRegister (UnwState * const state,
165 const Int32 addr,
166 const RegData * const reg);
167
168Boolean UnwMemReadRegister (UnwState * const state,
169 const Int32 addr,
170 RegData * const reg);
171
172void 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 */
61static 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
86UnwResult 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 */
55static 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
71UnwResult 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
56UnwResult 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 */
40typedef 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}
87UnwResult;
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 */
101typedef Boolean (*UnwindReportFunc)(void *data,
102 Int32 address);
103
104/** Structure that holds memory callback function pointers.
105 */
106typedef 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}
139UnwindCallbacks;
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 */
153UnwResult 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
9UNWARM_DIR = $(ROOTDIR)/lib/unwarminder
10UNWARM_SRC = $(call preprocess, $(UNWARM_DIR)/SOURCES)
11UNWARM_OBJ := $(call c2obj, $(UNWARM_SRC))
12
13OTHER_SRC += $(UNWARM_SRC)
14
15UNWARMINDER = $(BUILDDIR)/lib/libunwarminder.a
16
17INCLUDES += -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 */
59static 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
101Boolean 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
121Boolean 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
157void 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
43Boolean UnwMemHashRead (MemData * const memData,
44 Int32 addr,
45 Int32 * const data,
46 Boolean * const tracked);
47
48Boolean UnwMemHashWrite (MemData * const memData,
49 Int32 addr,
50 Int32 val,
51 Boolean valValid);
52
53void 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
79endif 79endif
80 80
81ifeq (arm,$(ARCH))
82 include $(ROOTDIR)/lib/unwarminder/unwarminder.make
83endif
84
81ifneq (,$(findstring bootloader,$(APPSDIR))) 85ifneq (,$(findstring bootloader,$(APPSDIR)))
82 include $(APPSDIR)/bootloader.make 86 include $(APPSDIR)/bootloader.make
83else ifneq (,$(findstring bootbox,$(APPSDIR))) 87else ifneq (,$(findstring bootbox,$(APPSDIR)))
@@ -170,6 +174,12 @@ ifeq (,$(findstring bootloader,$(APPSDIR)))
170 174
171OBJ += $(LANG_O) 175OBJ += $(LANG_O)
172 176
177ifeq (arm,$(ARCH))
178 UNWARMINDER_LINK := -lunwarminder
179else
180 UNWARMINDER_LINK :=
181endif
182
173ifndef APP_TYPE 183ifndef APP_TYPE
174 184
175## target build 185## target build
@@ -185,8 +195,6 @@ else
185 LIBARMSUPPORT_LINK := 195 LIBARMSUPPORT_LINK :=
186endif 196endif
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