2019-08-22 21:26:53 +01:00
/* SPDX-License-Identifier: GPL-2.0-only */
/ *
* linux/ a r c h / a r m / l i b / b a c k t r a c e - c l a n g . S
*
* Copyright ( C ) 2 0 1 9 N a t h a n H u c k l e b e r r y
*
* /
# include < l i n u x / k e r n _ l e v e l s . h >
# include < l i n u x / l i n k a g e . h >
# include < a s m / a s s e m b l e r . h >
.text
/* fp is 0 or stack frame */
# define f r a m e r4
# define s v _ f p r5
# define s v _ p c r6
# define m a s k r7
# define s v _ l r r8
2020-06-08 21:30:07 -07:00
# define l o g l v l r9
2019-08-22 21:26:53 +01:00
ENTRY( c _ b a c k t r a c e )
# if ! d e f i n e d ( C O N F I G _ F R A M E _ P O I N T E R ) | | ! d e f i n e d ( C O N F I G _ P R I N T K )
ret l r
ENDPROC( c _ b a c k t r a c e )
# else
/ *
* Clang d o e s n o t s t o r e p c o r s p i n f u n c t i o n p r o l o g u e s s o w e d o n ' t k n o w e x a c t l y
* where t h e f u n c t i o n s t a r t s .
*
* We c a n t r e a t t h e c u r r e n t f r a m e ' s l r a s t h e s a v e d p c a n d t h e p r e c e d i n g
* frame' s l r a s t h e c u r r e n t f r a m e ' s l r , b u t w e c a n ' t t r a c e t h e m o s t r e c e n t
* call. I n s e r t i n g a f a l s e s t a c k f r a m e a l l o w s u s t o r e f e r e n c e t h e f u n c t i o n
* called l a s t i n t h e s t a c k t r a c e .
*
* If t h e c a l l i n s t r u c t i o n w a s a b l w e c a n l o o k a t t h e c a l l e r s b r a n c h
* instruction t o c a l c u l a t e t h e s a v e d p c . W e c a n r e c o v e r t h e p c i n m o s t c a s e s ,
* but i n c a s e s s u c h a s c a l l i n g f u n c t i o n p o i n t e r s w e c a n n o t . I n t h i s c a s e ,
* default t o u s i n g t h e l r . T h i s w i l l b e s o m e a d d r e s s i n t h e f u n c t i o n , b u t w i l l
* not b e t h e f u n c t i o n s t a r t .
*
* Unfortunately d u e t o t h e s t a c k f r a m e l a y o u t w e c a n ' t d u m p r0 - r3 , b u t t h e s e
* are l e s s f r e q u e n t l y s a v e d .
*
* Stack f r a m e l a y o u t :
* < larger a d d r e s s e s >
* saved l r
* frame= > s a v e d f p
* optionally s a v e d c a l l e r r e g i s t e r s ( r4 - r10 )
* optionally s a v e d a r g u m e n t s ( r0 - r3 )
* < top o f s t a c k f r a m e >
* < smaller a d d r e s s e s >
*
* Functions s t a r t w i t h t h e f o l l o w i n g c o d e s e q u e n c e :
* corrected p c = > s t m f d s p ! , { . . . , f p , l r }
* add f p , s p , #x
* stmfd s p ! , { r0 - r3 } ( o p t i o n a l )
*
*
*
*
*
*
* The d i a g r a m b e l o w s h o w s a n e x a m p l e s t a c k s e t u p f o r d u m p _ s t a c k .
*
* The f r a m e f o r c _ b a c k t r a c e h a s p o i n t e r s t o t h e c o d e o f d u m p _ s t a c k . T h i s i s
* why t h e f r a m e o f c _ b a c k t r a c e i s u s e d t o f o r t h e p c c a l c u l a t i o n o f
* dump_ s t a c k . T h i s i s w h y w e m u s t m o v e b a c k a f r a m e t o p r i n t d u m p _ s t a c k .
*
* The s t o r e d l o c a l s f o r d u m p _ s t a c k a r e i n d u m p _ s t a c k ' s f r a m e . T h i s m e a n s t h a t
* to f u l l y p r i n t d u m p _ s t a c k ' s f r a m e w e n e e d b o t h t h e f r a m e f o r d u m p _ s t a c k ( f o r
* locals) a n d t h e f r a m e t h a t w a s c a l l e d b y d u m p _ s t a c k ( f o r p c ) .
*
* To p r i n t l o c a l s w e m u s t k n o w w h e r e t h e f u n c t i o n s t a r t i s . I f w e r e a d t h e
* function p r o l o g u e o p c o d e s w e c a n d e t e r m i n e w h i c h v a r i a b l e s a r e s t o r e d i n t h e
* stack f r a m e .
*
* To f i n d t h e f u n c t i o n s t a r t o f d u m p _ s t a c k w e c a n l o o k a t t h e s t o r e d L R o f
* show_ s t a c k . I t p o i n t s a t t h e i n s t r u c t i o n d i r e c t l y a f t e r t h e b l d u m p _ s t a c k .
* We c a n t h e n r e a d t h e o f f s e t f r o m t h e b l o p c o d e t o d e t e r m i n e w h e r e t h e b r a n c h
* takes u s . T h e a d d r e s s c a l c u l a t e d m u s t b e t h e s t a r t o f d u m p _ s t a c k .
*
* c_ b a c k t r a c e f r a m e d u m p _ s t a c k :
* { [ LR] } = = = = = = = = = = = = | . . .
* { [ FP] } = = = = = = = | | b l c _ b a c k t r a c e
* | | = > . . .
* { [ R4 - R 1 0 ] } |
* { [ R0 - R 3 ] } | s h o w _ s t a c k :
* dump_ s t a c k f r a m e | . . .
* { [ LR] } = = = = = = = = = = = = = | b l d u m p _ s t a c k
* { [ FP] } < = = = = = = = | | = > . . .
* { [ R4 - R 1 0 ] }
* { [ R0 - R 3 ] }
* /
stmfd s p ! , { r4 - r9 , f p , l r } @ Save an extra register
@ to ensure 8 byte alignment
movs f r a m e , r0 @ if frame pointer is zero
beq n o _ f r a m e @ we have no stack frames
2020-06-08 21:30:07 -07:00
mov l o g l v l , r2
2019-08-22 21:26:53 +01:00
tst r1 , #0x10 @ 26 or 32-bit mode?
moveq m a s k , #0xfc000003
movne m a s k , #0 @ mask for 32-bit
/ *
* Switches t h e c u r r e n t f r a m e t o b e t h e f r a m e f o r d u m p _ s t a c k .
* /
add f r a m e , s p , #24 @ switch to false frame
for_each_frame : tst f r a m e , m a s k @ Check for address exceptions
bne n o _ f r a m e
/ *
* sv_ f p i s t h e s t a c k f r a m e w i t h t h e l o c a l s f o r t h e c u r r e n t c o n s i d e r e d
* function.
*
* sv_ p c i s t h e s a v e d l r f r a m e t h e f r a m e a b o v e . T h i s i s a p o i n t e r t o a c o d e
* address w i t h i n t h e c u r r e n t c o n s i d e r e d f u n c t i o n , b u t i t i s n o t t h e f u n c t i o n
* start. T h i s v a l u e g e t s u p d a t e d t o b e t h e f u n c t i o n s t a r t l a t e r i f i t i s
* possible.
* /
1001 : ldr s v _ p c , [ f r a m e , #4 ] @ get saved 'pc'
1002 : ldr s v _ f p , [ f r a m e , #0 ] @ get saved fp
teq s v _ f p , m a s k @ make sure next frame exists
beq n o _ f r a m e
/ *
* sv_ l r i s t h e l r f r o m t h e f u n c t i o n t h a t c a l l e d t h e c u r r e n t f u n c t i o n . T h i s i s
* a p o i n t e r t o a c o d e a d d r e s s i n t h e c u r r e n t f u n c t i o n ' s c a l l e r . s v _ l r - 4 i s
* the i n s t r u c t i o n u s e d t o c a l l t h e c u r r e n t f u n c t i o n .
*
* This s v _ l r c a n b e u s e d t o c a l c u l a t e t h e f u n c t i o n s t a r t i f t h e f u n c t i o n w a s
* called u s i n g a b l i n s t r u c t i o n . I f t h e f u n c t i o n s t a r t c a n b e r e c o v e r e d s v _ p c
* is o v e r w r i t t e n w i t h t h e f u n c t i o n s t a r t .
*
* If t h e c u r r e n t f u n c t i o n w a s c a l l e d u s i n g a f u n c t i o n p o i n t e r w e c a n n o t
* recover t h e f u n c t i o n s t a r t a n d i n s t e a d c o n t i n u e w i t h s v _ p c a s a n a r b i t r a r y
* value w i t h i n t h e c u r r e n t f u n c t i o n . I f t h i s i s t h e c a s e w e c a n n o t p r i n t
* registers f o r t h e c u r r e n t f u n c t i o n , b u t t h e s t a c k t r a c e i s s t i l l p r i n t e d
* properly.
* /
1003 : ldr s v _ l r , [ s v _ f p , #4 ] @ get saved lr from next frame
ldr r0 , [ s v _ l r , #- 4 ] @ get call instruction
ldr r3 , . L o p c o d e + 4
and r2 , r3 , r0 @ is this a bl call
teq r2 , r3
bne f i n i s h e d _ s e t u p @ give up if it's not
and r0 , #0xffffff @ get call offset 24-bit int
lsl r0 , r0 , #8 @ sign extend offset
asr r0 , r0 , #8
ldr s v _ p c , [ s v _ f p , #4 ] @ get lr address
add s v _ p c , s v _ p c , #- 4 @ get call instruction address
add s v _ p c , s v _ p c , #8 @ take care of prefetch
add s v _ p c , s v _ p c , r0 , l s l #2 @ find function start
finished_setup :
bic s v _ p c , s v _ p c , m a s k @ mask PC/LR for the mode
/ *
* Print t h e f u n c t i o n ( s v _ p c ) a n d w h e r e i t w a s c a l l e d f r o m ( s v _ l r ) .
* /
1004 : mov r0 , s v _ p c
mov r1 , s v _ l r
mov r2 , f r a m e
bic r1 , r1 , m a s k @ mask PC/LR for the mode
2020-06-08 21:30:07 -07:00
mov r3 , l o g l v l
2019-08-22 21:26:53 +01:00
bl d u m p _ b a c k t r a c e _ e n t r y
/ *
* Test i f t h e f u n c t i o n s t a r t i s a s t m f d i n s t r u c t i o n t o d e t e r m i n e w h i c h
* registers w e r e s t o r e d i n t h e f u n c t i o n p r o l o g u e .
*
* If w e c o u l d n o t r e c o v e r t h e s v _ p c b e c a u s e w e w e r e c a l l e d t h r o u g h a f u n c t i o n
* pointer t h e c o m p a r i s o n w i l l f a i l a n d n o r e g i s t e r s w i l l p r i n t . U n w i n d i n g w i l l
* continue a s i f t h e r e h a d b e e n n o r e g i s t e r s s t o r e d i n t h i s f r a m e .
* /
1005 : ldr r1 , [ s v _ p c , #0 ] @ if stmfd sp!, {..., fp, lr}
ldr r3 , . L o p c o d e @ instruction exists,
teq r3 , r1 , l s r #11
ldr r0 , [ f r a m e ] @ locals are stored in
@ the preceding frame
subeq r0 , r0 , #4
2020-06-08 21:30:07 -07:00
mov r2 , l o g l v l
2019-08-22 21:26:53 +01:00
bleq d u m p _ b a c k t r a c e _ s t m @ dump saved registers
/ *
* If w e a r e o u t o f f r a m e s o r i f t h e n e x t f r a m e i s i n v a l i d .
* /
teq s v _ f p , #0 @ zero saved fp means
beq n o _ f r a m e @ no further frames
cmp s v _ f p , f r a m e @ next frame must be
mov f r a m e , s v _ f p @ above the current frame
bhi f o r _ e a c h _ f r a m e
1006 : adr r0 , . L b a d
2020-06-08 21:30:07 -07:00
mov r1 , l o g l v l
mov r2 , f r a m e
2019-08-22 21:26:53 +01:00
bl p r i n t k
no_frame : ldmfd s p ! , { r4 - r9 , f p , p c }
ENDPROC( c _ b a c k t r a c e )
.pushsection _ _ ex_ t a b l e ," a "
.align 3
.long 1 0 0 1 b, 1 0 0 6 b
.long 1 0 0 2 b, 1 0 0 6 b
.long 1 0 0 3 b, 1 0 0 6 b
.long 1 0 0 4 b, 1 0 0 6 b
.long 1 0 0 5 b, 1 0 0 6 b
.popsection
2020-06-08 21:30:07 -07:00
.Lbad : .asciz " % sBacktrace a b o r t e d d u e t o b a d f r a m e p o i n t e r < % p > \ n "
2019-08-22 21:26:53 +01:00
.align
.Lopcode : .word 0xe92d4800 > > 1 1 @ stmfd sp!, {... fp, lr}
.word 0x0b000000 @ bl if these bits are set
# endif