@@ -428,7 +428,7 @@ def populate_got(env):
428
428
if got_entry .name == "mp_fun_table" :
429
429
dest = "mp_fun_table"
430
430
elif got_entry .name .startswith ("mp_fun_table+0x" ):
431
- dest = int (got_entry .name .split ("+" )[1 ], 16 ) // env .arch .word_size
431
+ dest = ( "fun_table" , int (got_entry .name .split ("+" )[1 ], 16 ) ) // env .arch .word_size
432
432
elif got_entry .sec_name .startswith (".text" ):
433
433
dest = ".text"
434
434
elif got_entry .sec_name .startswith (".rodata" ):
@@ -437,6 +437,8 @@ def populate_got(env):
437
437
dest = ".data.rel.ro"
438
438
elif got_entry .sec_name .startswith (".bss" ):
439
439
dest = ".bss"
440
+ elif got_entry .sec_name == ".external.mp_port_fun_table" :
441
+ dest = ("port_fun_table" , got_entry .sym .port_fun_offset )
440
442
else :
441
443
assert 0 , (got_entry .name , got_entry .sec_name )
442
444
env .mpy_relocs .append ((".text" , env .got_section .addr + got_entry .offset , dest ))
@@ -651,7 +653,7 @@ def do_relocation_data(env, text_addr, r):
651
653
kind = sec .name
652
654
elif sec .name == ".external.mp_fun_table" :
653
655
assert addr == 0
654
- kind = s .mp_fun_table_offset
656
+ kind = ( "fun_table" , s .mp_fun_table_offset )
655
657
else :
656
658
assert 0 , sec .name
657
659
if env .arch .separate_rodata :
@@ -777,6 +779,7 @@ def link_objects(env, native_qstr_vals_len, native_qstr_objs_len):
777
779
]
778
780
)
779
781
}
782
+ port_fun_sec = Section (".external.mp_port_fun_table" , b"" , 0 )
780
783
for sym in env .unresolved_syms :
781
784
assert sym ["st_value" ] == 0
782
785
if sym .name == "_GLOBAL_OFFSET_TABLE_" :
@@ -789,12 +792,20 @@ def link_objects(env, native_qstr_vals_len, native_qstr_objs_len):
789
792
sym .section = env .qstr_obj_section
790
793
elif sym .name in env .known_syms :
791
794
sym .resolved = env .known_syms [sym .name ]
795
+ elif sym .name in fun_table :
796
+ sym .section = mp_fun_table_sec
797
+ sym .mp_fun_table_offset = fun_table [sym .name ]
798
+ elif sym .name in port_fun_entries :
799
+ sym .section = port_fun_sec
800
+ sym .port_fun_offset = port_fun_entries [sym .name ]
801
+ log (
802
+ LOG_LEVEL_3 ,
803
+ "Port_fun_table reference: {} -> port_fun_sec+{}" .format (
804
+ sym .name , sym .port_fun_offset
805
+ ),
806
+ )
792
807
else :
793
- if sym .name in fun_table :
794
- sym .section = mp_fun_table_sec
795
- sym .mp_fun_table_offset = fun_table [sym .name ]
796
- else :
797
- raise LinkError ("{}: undefined symbol: {}" .format (sym .filename , sym .name ))
808
+ raise LinkError ("{}: undefined symbol: {}" .format (sym .filename , sym .name ))
798
809
799
810
# Align sections, assign their addresses, and create full_text
800
811
env .full_text = bytearray (env .arch .asm_jump (8 )) # dummy, to be filled in later
@@ -868,13 +879,35 @@ def write_qstr(self, s):
868
879
self .write_bytes (s )
869
880
870
881
def write_reloc (self , base , offset , dest , n ):
882
+ """
883
+ Write a relocation entry into the mpy that the dynamic linker will have to resolve
884
+ when the mpy is loaded. A relocation entry consists of a kind/op byte, followed by an
885
+ optional offset word, followed by an optional count word.
886
+ - base+offset is the location where the fixup is to be done, base is '.text' or 'rodata'.
887
+ - dest is the target whose address needs to be placed into the fixup: 0=string table,
888
+ 1=rodata_const_table, 2=bss_const_table, 3..5: unused, 6=mp_fun_table,
889
+ 7..126:entry in mp_fun_table, 1000..66535:entry in mp_port_fun_table.
890
+ - n is number of consecutive words to fix up.
891
+ """
871
892
need_offset = not (base == self .prev_base and offset == self .prev_offset + 1 )
872
893
self .prev_offset = offset + n - 1
873
- if dest <= 2 :
894
+ index = None
895
+ if isinstance (dest , tuple ):
896
+ if dest [0 ] == "port_fun_table" :
897
+ # offset into mp_port_fun_table
898
+ assert dest [1 ] < 65536
899
+ index = dest [1 ]
900
+ dest = 126 # magic number for the loader to refer to mp_port_fun_table
901
+ elif dest [0 ] == "fun_table" :
902
+ assert 6 <= dest [1 ] < 126
903
+ assert n == 1
904
+ dest = dest [1 ]
905
+ else :
906
+ assert 0 , dest
907
+ elif dest <= 2 :
874
908
dest = (dest << 1 ) | (n > 1 )
875
909
else :
876
- assert 6 <= dest <= 127
877
- assert n == 1
910
+ assert dest == 6 , dest # only case left: mp_fun_table itself
878
911
dest = dest << 1 | need_offset
879
912
assert 0 <= dest <= 0xFE , dest
880
913
self .write_bytes (bytes ([dest ]))
@@ -884,6 +917,8 @@ def write_reloc(self, base, offset, dest, n):
884
917
elif base == ".rodata" :
885
918
base = 1
886
919
self .write_uint (offset << 1 | base )
920
+ if index is not None :
921
+ self .write_uint (index )
887
922
if n > 1 :
888
923
self .write_uint (n )
889
924
@@ -966,8 +1001,12 @@ def build_mpy(env, entry_offset, fmpy, native_qstr_vals, native_qstr_objs):
966
1001
kind = bss_const_table_idx
967
1002
elif kind == "mp_fun_table" :
968
1003
kind = 6
1004
+ elif isinstance (kind , tuple ) and kind [0 ] == "fun_table" :
1005
+ kind = (kind [0 ], 7 + kind [1 ])
1006
+ elif isinstance (kind , tuple ) and kind [0 ] == "port_fun_table" :
1007
+ pass
969
1008
else :
970
- kind = 7 + kind
1009
+ assert 0 , kind
971
1010
assert addr % env .arch .word_size == 0 , addr
972
1011
offset = addr // env .arch .word_size
973
1012
if kind == prev_kind and base == prev_base and offset == prev_offset + 1 :
@@ -989,6 +1028,28 @@ def build_mpy(env, entry_offset, fmpy, native_qstr_vals, native_qstr_objs):
989
1028
out .close ()
990
1029
991
1030
1031
+ ################################################################################
1032
+ # Port-specific relocation table, which contains the addresses of a number of port-specific
1033
+ # functions compiled into the firmware. These entries are used by the dynamic linker to fix-up
1034
+ # relocation entries in dynamically loaded mpy modules.
1035
+ port_fun_entries = {}
1036
+
1037
+
1038
+ def load_port_fun (portfun_file ):
1039
+ """
1040
+ Load list of port-specific functions from file used for C-#include: has a function name
1041
+ followed by a comma on each line
1042
+ """
1043
+ ix = len (port_fun_entries )
1044
+ with open (portfun_file ) as f :
1045
+ for l in f :
1046
+ m = re .match (r"^([A-Za-z0-9_][A-Za-z0-9_]*),$" , l )
1047
+ if m :
1048
+ port_fun_entries [m .group (1 )] = ix
1049
+ ix += 1
1050
+ # print("port_fun_entries=", port_fun_entries)
1051
+
1052
+
992
1053
################################################################################
993
1054
# main
994
1055
@@ -1062,6 +1123,9 @@ def main():
1062
1123
cmd_parser .add_argument ("--arch" , default = "x64" , help = "architecture" )
1063
1124
cmd_parser .add_argument ("--preprocess" , action = "store_true" , help = "preprocess source files" )
1064
1125
cmd_parser .add_argument ("--qstrs" , default = None , help = "file defining additional qstrs" )
1126
+ cmd_parser .add_argument (
1127
+ "--portfun" , default = None , help = "file defining port-specific functions"
1128
+ )
1065
1129
cmd_parser .add_argument (
1066
1130
"--output" , "-o" , default = None , help = "output .mpy file (default to input with .o->.mpy)"
1067
1131
)
@@ -1071,6 +1135,9 @@ def main():
1071
1135
global log_level
1072
1136
log_level = args .verbose
1073
1137
1138
+ if args .portfun :
1139
+ load_port_fun (args .portfun )
1140
+
1074
1141
if args .preprocess :
1075
1142
do_preprocess (args )
1076
1143
else :
0 commit comments