1+ use crate :: code:: AssembleError ;
2+
3+
4+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
5+ pub ( crate ) enum RelocationKind {
6+ None = 0 ,
7+ Jump24 = 29 ,
8+ Movw ,
9+ Movt ,
10+ }
11+
12+ impl RelocationKind {
13+ /// Addend has already been applied to value
14+ pub ( crate ) fn apply_relative ( self , data : & mut [ u8 ] , location : usize , value : usize ) -> Result < ( ) , AssembleError > {
15+ use RelocationKind :: * ;
16+ let value: isize = if location < value {
17+ ( value - location) . try_into ( ) . ok ( ) . ok_or ( AssembleError :: InvalidRelocation ( "Relative relocation difference too large" ) ) ?
18+ } else {
19+ -( location - value) . try_into ( ) . ok ( ) . ok_or ( AssembleError :: InvalidRelocation ( "Relative relocation difference too large" ) ) ?
20+ } ;
21+ match self {
22+ None => Ok ( ( ) ) ,
23+ Jump24 => {
24+ // Low 24 bits of (big-endian) word contain low 26 bits of pc-relative address (00 on the end) (sign-extended)
25+ let instruction: & mut [ u8 ; 4 ] = data. get_mut ( location..location+4 )
26+ . ok_or ( AssembleError :: InvalidRelocation ( "Attempted to apply relocation past end of section" ) ) ?
27+ . try_into ( ) . unwrap ( ) ;
28+ if value & 3 != 0 {
29+ return Err ( AssembleError :: InvalidRelocation ( "Relative relocation cut off low bits" ) ) ;
30+ }
31+ let actual_value = value >> 2 ;
32+ let actual_value: i32 = actual_value. try_into ( ) . ok ( ) . ok_or ( AssembleError :: InvalidRelocation ( "Relative relocation difference too large" ) ) ?;
33+ let bytes = i32:: to_le_bytes ( actual_value) ;
34+ if !(
35+ ( bytes[ 3 ] == 0x00 && bytes[ 2 ] & 0x80 == 0x00 ) ||
36+ ( bytes[ 3 ] == 0xFF && bytes[ 2 ] & 0x80 == 0x80 )
37+ ) {
38+ return Err ( AssembleError :: InvalidRelocation ( "Relative relocation difference too large" ) ) ;
39+ }
40+ instruction[ ..3 ] . copy_from_slice ( & bytes[ ..3 ] ) ;
41+ Ok ( ( ) )
42+ } ,
43+ Movw | Movt => Err ( AssembleError :: InvalidRelocation ( "Cannot apply direct relocation for relative symbol" ) ) ,
44+ }
45+ }
46+ pub ( crate ) fn apply_absolute ( self , data : & mut [ u8 ] , location : usize , value : isize ) -> Result < ( ) , AssembleError > {
47+ use RelocationKind :: * ;
48+ match self {
49+ None => Ok ( ( ) ) ,
50+ Jump24 => Err ( AssembleError :: InvalidRelocation ( "Cannot apply relative relocation for absolute symbol" ) ) ,
51+ Movw => {
52+ // Low 16 bits of value encoded in bits 19-16 and 11-0 of the 4-byte big-endian instruction
53+ let instruction: & mut [ u8 ; 4 ] = data. get_mut ( location..location+4 )
54+ . ok_or ( AssembleError :: InvalidRelocation ( "Attempted to apply relocation past end of section" ) ) ?
55+ . try_into ( ) . unwrap ( ) ;
56+ let actual_value: i32 = value. try_into ( ) . ok ( ) . ok_or ( AssembleError :: InvalidRelocation ( "Relative relocation difference too large" ) ) ?;
57+ let low_half: u16 = actual_value as u32 as u16 ;
58+ let imm4: u8 = ( low_half >> 12 ) as u8 & 0xF ;
59+ let imm12: u16 = low_half & 0xFFF ;
60+ let imm12_bytes = imm12. to_be_bytes ( ) ;
61+ instruction[ 2 ] &= 0xF0 ;
62+ instruction[ 2 ] |= imm4;
63+ instruction[ 1 ] &= 0xF0 ;
64+ instruction[ 1 ] |= imm12_bytes[ 0 ] ;
65+ instruction[ 0 ] = imm12_bytes[ 1 ] ;
66+ Ok ( ( ) )
67+ } ,
68+ Movt => {
69+ // Low 16 bits of value encoded in bits 19-16 and 11-0 of the 4-byte big-endian instruction
70+ let instruction: & mut [ u8 ; 4 ] = data. get_mut ( location..location+4 )
71+ . ok_or ( AssembleError :: InvalidRelocation ( "Attempted to apply relocation past end of section" ) ) ?
72+ . try_into ( ) . unwrap ( ) ;
73+ let actual_value: i32 = value. try_into ( ) . ok ( ) . ok_or ( AssembleError :: InvalidRelocation ( "Relative relocation difference too large" ) ) ?;
74+ let high_half: u16 = ( actual_value as u32 >> 16 ) as u16 ;
75+ let imm4: u8 = ( high_half >> 12 ) as u8 & 0xF ;
76+ let imm12: u16 = high_half & 0xFFF ;
77+ let imm12_bytes = imm12. to_be_bytes ( ) ;
78+ instruction[ 2 ] &= 0xF0 ;
79+ instruction[ 2 ] |= imm4;
80+ instruction[ 1 ] &= 0xF0 ;
81+ instruction[ 1 ] |= imm12_bytes[ 0 ] ;
82+ instruction[ 0 ] = imm12_bytes[ 1 ] ;
83+ Ok ( ( ) )
84+ } ,
85+ }
86+ }
87+ }
0 commit comments