2014年3月9日 星期日

setjmp/longjmp 實作 (1) - cm3

在看過 x86 的版本之後, 來看看 cortex m3 的 setjmp/longjmp 如何實作。

一樣參考 newlib: arm-eabi-toolchain/newlib-2013.05/newlib/libc/machine/arm/setjmp.S (L173, L174)
有了 x86 的經驗之後還蠻有信心的, 結果開頭兩行就難倒我。

mov ip, sp
stmea a1!, { v1-v7, fp, ip, lr }

完全看不懂的暫存器名稱,花了點時間查詢。

r10 sl Stack limit
r11 fp Argument pointer
r12 ip Temporary workspace
r13 sp Stack pointer
r14 lr Link register Workspace

Table 2. Predeclared core registers
Register names
Meaning
r0-r15 and R0-R15
General purpose registers.
a1-a4
Argument, result or scratch registers. These are synonyms for R0 to R3
v1-v8
Variable registers. These are synonyms for R4 to R11.
sb and SB
Static base register. This is a synonym for R9.
ip and IP
Intra procedure call scratch register. This is a synonym for R12.
sp and SP
Stack pointer. This is a synonym for R13.
lr and LR
Link register. This is a synonym for R14.
pc and PC
Program counter. This is a synonym for R15.

ref:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0473c/CJAJBFHC.html
http://forums.leaflabs.com/topic.php?id=1298

ip (r12):
http://blog.csdn.net/gooogleman/article/details/3529413
http://forums.arm.com/index.php?/topic/12986-about-r12/

我還是習慣直接的名稱, 搞這些有的沒的化名實在累人人累。

arm-eabi-toolchain/newlib-2013.05/newlib/libc/machine/arm/setjmp.S
 1 /* This is a simple version of setjmp and longjmp.
 2
 3 Nick Clifton, Cygnus Solutions, 13 June 1997. */
 4
 5 /* ANSI concatenation macros. */
 6 #define CONCAT(a, b) CONCAT2(a, b)
 7 #define CONCAT2(a, b) a##b
 8

107 #ifdef __APCS_26__
108 #define RET movs pc, lr
109 #elif defined(__thumb2__)
110 #define RET bx lr
111 #else
112 #define RET tst lr, #1; \
113 moveq pc, lr ; \
114 .word 0xe12fff1e /* bx lr */
115 #endif
116
149
150 .macro FUNC_START name
151 .text
152 .align 2
153 MODE
154 .globl SYM (\name)
155 TYPE (\name)
156 SYM (\name):
157 PROLOGUE \name
158 .endm
159
160 .macro FUNC_END name
161 RET
162 SIZE (\name)
163 .endm
164
165 /* --------------------------------------------------------------------
166 int setjmp (jmp_buf);
167 -------------------------------------------------------------------- */
168
169 FUNC_START setjmp
170
171 /* Save all the callee-preserved registers into the jump buffer. */
172 #ifdef __thumb2__
173 mov ip, sp
174 stmea a1!, { v1-v7, fp, ip, lr }
175 #else
176 stmea a1!, { v1-v7, fp, ip, sp, lr }
177 #endif
178
179 #if 0 /* Simulator does not cope with FP instructions yet. */
180 #ifndef __SOFTFP__
181 /* Save the floating point registers. */
182 sfmea f4, 4, [a1]
183 #endif
184 #endif
185 /* When setting up the jump buffer return 0. */
186 mov a1, #0
187
188 FUNC_END setjmp
189
190 /* --------------------------------------------------------------------
191 volatile void longjmp (jmp_buf, int);
192 -------------------------------------------------------------------- */
193
194 FUNC_START longjmp
195
196 /* If we have stack extension code it ought to be handled here. */
197
198 /* Restore the registers, retrieving the state when setjmp() was called. */
199 #ifdef __thumb2__
200 ldmfd a1!, { v1-v7, fp, ip, lr }
201 mov sp, ip
202 #else
203 ldmfd a1!, { v1-v7, fp, ip, sp, lr }
204 #endif
205
206 #if 0 /* Simulator does not cope with FP instructions yet. */
207 #ifndef __SOFTFP__
208 /* Restore floating point registers as well. */
209 lfmfd f4, 4, [a1]
210 #endif
211 #endif
212 /* Put the return value into the integer result register.
213 But if it is zero then return 1 instead. */
214 movs a1, a2
215 #ifdef __thumb2__
216 it eq
217 #endif
218 moveq a1, #1
219
220 FUNC_END longjmp
221 #endif

arm-eabi-toolchain/newlib-2013.05/newlib/libc/machine/arm/setjmp.S L174
stmea a1!, { v1-v7, fp, ip, lr }

相當於

stmea r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}

沒有保存 r0, r1, r2, r3, r13 (sp), r15 (pc), 而 r0 就是 jmp_buf 所指到的一塊記憶體位址, 把
r4, r5, r6, r7, r8, r9, r10, r11, r12, lr 都存起來就對了。
186 mov a1, #0
a1 就是 r0, 把 r0 的值設為 0 也代表 setjmp 傳回值是 0, cm3 用 r0 來當作 function 的傳回值。

下表的 gdb 反組譯結果也說明了同樣的結果。

gdb_stmf4discovery 正確的版本
 1 >│0x8000130 <setjmp> mov r12, sp
 2 │0x8000132 <setjmp+2> stmia.w r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}
 3 │0x8000136 <setjmp+6> mov.w r0, #0
 4 │0x800013a <setjmp+10> bx lr
 
 
 5 │0x800013c <longjmp> ldmia.w r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}
 6 │0x8000140 <longjmp+4> mov sp, r12
 7 │0x8000142 <longjmp+6> movs r0, r1
 8 │0x8000144 <longjmp+8> it eq
 9 │0x8000146 <longjmp+10> moveq r0, #1
10 │0x8000148 <longjmp+12> bx lr

longjmp 從上表的反組譯也很容易得知,  把 jmp_buf 的值倒回給 r4, r5, r6, r7, r8, r9, r10, r11, r12, lr

gdb_stmf4discovery L7 ~ L9 我看不是很明白, movs 會影響 apsr, it qe 是根據 Z 的結果來執行 L9 嗎? moveq 也是根據 Z 的結果來判斷要不要執行這行嗎?

總之結果就是 r0 不是 0 的話, 就把 1 填入 r0。

提外話

newlib 有 4 個 libc.a
./arm-none-eabi/lib/libc.a
./arm-none-eabi/lib/armv6-m/libc.a
./arm-none-eabi/lib/thumb/libc.a
./arm-none-eabi/lib/thumb2/libc.a

我使用 libc.a 的 setjmp/longjmp 結果一直有錯誤, 後來啟動 gdb 觀察:


gdb 的觀察結果 (newlib setjmp/longjmp) link 到錯誤的 libc.a

 1 0x1b0 <setjmp> stmia r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr}
 2 0x1b4 <setjmp> mov r0, #0
 3 0x1b8 <setjmp> tst lr, #1
 4 0x1bc <setjmp> moveq pc, lr
 5 0x1c0 <setjmp> bx lr
 6
 7 0x1c4 <longjmp> ldm r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr}
 8 0x1c8 <longjmp> movs r0, r1
 9 0x1cc <longjmp> moveq r0, #1
10 0x1d0 <longjmp> tst lr, #1
11 0x1d4 <longjmp> moveq pc, lr
12 0x1d8 <longjmp> bx lr

想說這個組合語言怎麼和我看的有所不同, 最後才知道:

編譯/liker 參數沒加上 -mcpu=cortex-m3 -mthumb 會 link 到錯誤的 libc.a

arm-none-eabi-gcc -Wl,-T./stm32.ld -nostartfiles -fno-common -mcpu=cortex-m3 -mthumb -o t0.elf t0.o

我花了好大一番功夫才找到這個錯誤。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。