88# See https://swift.org/LICENSE.txt for license information
99
1010#
11- # macho2uf2 -- Converts a statically-linked executable Mach-O into a flat "UF2" file suitable for flashing as a single
12- # contiguous blob onto some embedded devices, in particular Raspberry Pi Pico (W). Note that the UF2 format allows for
13- # discontiguous memory regions, but this utility does not support that.
11+ # macho2uf2 -- Converts a statically-linked executable Mach-O into a flat "UF2" file
12+ # suitable for flashing as a single contiguous blob onto some embedded devices, in
13+ # particular Raspberry Pi Pico (W). Note that the UF2 format allows for discontiguous
14+ # memory regions, but this utility does not support that.
1415#
1516# Usage:
16- # $ macho2uf2.py <input> <output> --base-address <base-address> --segments <segment-list>
17+ # $ macho2uf2.py <input> <output> --base-address <base-address> --segments
18+ # <segment-list>
1719#
1820# Example:
19- # $ macho2uf2.py ./blink ./blink.uf2 --base-address 0x00200000 --segments '__TEXT,__DATA,__VECTORS'
21+ # $ macho2uf2.py ./blink ./blink.uf2 --base-address 0x00200000 --segments
22+ # '__TEXT,__DATA,__VECTORS'
2023#
2124# Requirements and notes:
22- # * The output UF2 file is a flat contiguous representation of the segments (--segments) based on their VM addresses.
23- # * The UF2 file's first byte corresponds to the specified base address (--base-address).
25+ # * The output UF2 file is a flat contiguous representation of the segments
26+ # (--segments) based on their VM addresses.
27+ # * The UF2 file's first byte corresponds to the specified base address
28+ # (--base-address).
2429# * Any gaps between segments are filled with zero bytes.
25- # * Because of that, you want the input Mach-O to have all segments "close", and not have gaps.
30+ # * Because of that, you want the input Mach-O to have all segments "close", and not
31+ # have gaps.
2632#
2733
2834import argparse
2935import os
30- import subprocess
3136import struct
32- from macholib import MachO
33- from macholib import mach_o
37+ import subprocess
3438
3539MY_DIR = os .path .dirname (os .path .abspath (__file__ ))
3640
41+
3742def main ():
3843 parser = argparse .ArgumentParser ()
39- parser .add_argument (' input' )
40- parser .add_argument (' output' )
41- parser .add_argument (' --base-address' , required = True )
42- parser .add_argument (' --segments' , required = True )
43- parser .add_argument (' --pico-family' , required = True )
44+ parser .add_argument (" input" )
45+ parser .add_argument (" output" )
46+ parser .add_argument (" --base-address" , required = True )
47+ parser .add_argument (" --segments" , required = True )
48+ parser .add_argument (" --pico-family" , required = True )
4449 args = parser .parse_args ()
4550 args .base_address = int (args .base_address , 16 )
4651 args .segments = args .segments .split ("," )
4752 if args .pico_family == "rp2040" :
4853 family_id = 0xE48BFF56
4954 add_errata_block = False
5055 elif args .pico_family == "rp2350" :
51- family_id = 0XE48BFF59
56+ family_id = 0xE48BFF59
5257 add_errata_block = True
5358 else :
5459 assert False
5560
56- subprocess .check_call ([MY_DIR + "/macho2bin.py" , args .input , args .input + ".bin" , "--base-address" , "0x%08x" % args .base_address , "--segments" , "," .join (args .segments )])
61+ subprocess .check_call (
62+ [
63+ MY_DIR + "/macho2bin.py" ,
64+ args .input ,
65+ args .input + ".bin" ,
66+ "--base-address" ,
67+ "0x%08x" % args .base_address ,
68+ "--segments" ,
69+ "," .join (args .segments ),
70+ ]
71+ )
5772
5873 def emit_block (output , block , vmaddr , block_number , num_blocks , family_id ):
5974 assert len (block ) <= 256
60-
75+
6176 if len (block ) < 256 :
62- block += b' \0 ' * (256 - len (block ))
63-
77+ block += b" \0 " * (256 - len (block ))
78+
6479 # UF2_Block header
65- output += struct .pack ("<I" , 0x0A324655 ) # magicStart0
66- output += struct .pack ("<I" , 0x9E5D5157 ) # magicStart1
67- output += struct .pack ("<I" , 0x2000 ) # flags: familyID present
68- output += struct .pack ("<I" , vmaddr ) # targetAddr
69- output += struct .pack ("<I" , 256 ) # payloadSize
70- output += struct .pack ("<I" , block_number ) # blockNo
71- output += struct .pack ("<I" , num_blocks ) # numBlocks
72- output += struct .pack ("<I" , family_id ) # fileSize / familyID: rp2040/rp2350 family ID
80+ output += struct .pack ("<I" , 0x0A324655 ) # magicStart0
81+ output += struct .pack ("<I" , 0x9E5D5157 ) # magicStart1
82+ output += struct .pack ("<I" , 0x2000 ) # flags: familyID present
83+ output += struct .pack ("<I" , vmaddr ) # targetAddr
84+ output += struct .pack ("<I" , 256 ) # payloadSize
85+ output += struct .pack ("<I" , block_number ) # blockNo
86+ output += struct .pack ("<I" , num_blocks ) # numBlocks
87+ output += struct .pack (
88+ "<I" , family_id
89+ ) # fileSize / familyID: rp2040/rp2350 family ID
7390
7491 # Data
7592 if len (block ) < 476 :
76- block += b' \0 ' * (476 - len (block ))
93+ block += b" \0 " * (476 - len (block ))
7794 output += block
7895
7996 # UF2_Block footer
80- output += struct .pack ("<I" , 0x0AB16F30 ) # magicEnd
81-
97+ output += struct .pack ("<I" , 0x0AB16F30 ) # magicEnd
98+
8299 return output
83100
84- output = b''
101+ output = b""
85102 with open (args .input + ".bin" , "rb" ) as f :
86103 if add_errata_block :
87104 # RP2350-E10 errata
@@ -97,14 +114,20 @@ def emit_block(output, block, vmaddr, block_number, num_blocks, family_id):
97114 block = f .read (256 )
98115 if len (block ) == 0 :
99116 break
100- output = emit_block (output , block , vmaddr , block_number , num_blocks , family_id )
117+ output = emit_block (
118+ output , block , vmaddr , block_number , num_blocks , family_id
119+ )
101120 block_number += 1
102121 vmaddr += 256
103122
104123 with open (args .output , "wb" ) as f :
105124 f .write (output )
106-
107- print (f"Produced { args .output } with total size { len (output )} (0x{ len (output ):0x} ) bytes, payload size { total_size } (0x{ total_size :0x} ) bytes" )
108125
109- if __name__ == '__main__' :
126+ print (
127+ f"Produced { args .output } with total size { len (output )} (0x{ len (output ):0x} )"
128+ f" bytes, payload size { total_size } (0x{ total_size :0x} ) bytes"
129+ )
130+
131+
132+ if __name__ == "__main__" :
110133 main ()
0 commit comments