-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgenbin.py
executable file
·411 lines (349 loc) · 14.9 KB
/
genbin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
#!/usr/bin/env python
# Tool used for generate bin images OTA and non OTA for ESP8266
# It is a mix of esptool-ck and Espressif's gen_appbin.py
# Copyright Didier Bertrand (https://github.com/freedib), January 2021
#
# The tool extract the sections from the elf file and generate:
# flash.bin + irom0.text.bin or user1.bin/user2.bin
#
# No intermediate file are created and no external tool are required
#
# Args are positionnal and similar to esptool.py
# Examples (notice tha args are positional):
# generate firmware.bin and firmware.bin.irom0text.bin
# genbin 0 dio 40m 4MB-c1 firmware.elf firmware.bin firmware.bin.irom0text.bin")
# generate user1.16384.new.9.bin
# genbin 1 dio 40m 16MB firmware.elf user1.16384.new.9.bin")
# generate user2.4096.new.6.bin
# genbin 2 dio 40m 4MB-c1 firmware.elf user2.4096.new.6.bin")
# generate user1.16384.new.9.bin
# genbin 1 dio 40m 16MB firmware.elf user1.16384.new.9.bin")
# generate user1.bin and user2.bin
# genbin 12 dio 40m 16MB firmware.elf user1.bin user2.bin")
import os, sys, string, struct, zlib
verbose = False
############ ELF extraction (from esptool-ck) ############
# get string at offset sh_name in strings
# returns the string
def get_elf_string(sh_name, strings):
pythonstring = ''
if len(strings)==0 or sh_name==0:
return ''
for c in range(sh_name,len(strings)):
if strings[c] == 0:
break
pythonstring = pythonstring+chr(strings[c])
return pythonstring
# get one section index
# return a tuple with (name, offset, size, address)
def get_elf_section (e_shndx,e_shoff,e_shentsize):
global cstrings
# read one elf section
offset = e_shoff + e_shndx*e_shentsize
if verbose:
print(("Section %2s: offset=%s(%s)") % (e_shndx,offset,hex(offset)), end=' ')
elf_fd.seek(offset)
data = elf_fd.read(40);
# read section header
# see Elf32_Shdr in esptool-ck for fields
elf_section = struct.Struct("<IIIIIIIIII").unpack(data)
sh_name = elf_section[0]
sh_addr = elf_section[3]
sh_offset = elf_section[4]
sh_size = elf_section[5]
section_name = get_elf_string(sh_name,cstrings)
if verbose:
print(("name=%-15s, sh_name=%4s, sh_offset=%7s, offset=%7s, sh_addr=%10s, sh_size=%6s") %
(section_name,hex(sh_name),hex(sh_offset),hex(offset),hex(sh_addr),sh_size))
return ((section_name,sh_offset,sh_size,sh_addr)) # name, size, offset, address
# extract a section by index from elf and return it
# return a tuple with (data, address)
def read_elf_section_by_index (e_shndx):
if sections[e_shndx][2] > 0: # size
elf_fd.seek(sections[e_shndx][1]) # offset
data = elf_fd.read(sections[e_shndx][2]); # size
return (data, sections[e_shndx][3]) # address
# extract a section by name from elf and return it
# return a tuple with (data, address)
def read_elf_section_by_name (section_name,filename=None):
for e_shndx in range(0,len(sections)):
if sections[e_shndx][0] == section_name: # name
return read_elf_section_by_index (e_shndx)
# extract a section by name from elf and save it if filename is furnished
def extract_elf_section_by_name (section_name, filename):
section = read_elf_section_by_name (section_name)
if filename != None:
section_fd = open(filename, "wb")
if section_fd:
section_fd.write(section[0])
section_fd.close()
else:
print ('%s write fail\n'%(filename))
# search a symbol by name dans return its definition
# see Elf32_Sym in esptool-ck for fields
def search_elf_symbol (name):
global strtab, symtab
if not strtab:
strtab = (read_elf_section_by_name('.strtab'))[0]
if not symtab:
symtab = (read_elf_section_by_name('.symtab'))[0]
for entry in range(0,len(symtab),16):
symbol = struct.Struct("<IIIBBH").unpack(symtab[entry:entry+16])
symbol_name = get_elf_string(symbol[0],strtab)
if (symbol_name==name):
if verbose:
print(("symbol=%s, name=%s, st_name=%s, st_size=%s, st_value=%s, st_info=%s, st_other=%s, st_shndx=%s") %
(entry, get_elf_string(symbol[0],strtab), hex(symbol[0]),hex(symbol[1]),hex(symbol[2]),
hex(symbol[3]),hex(symbol[4]),hex(symbol[5])))
return symbol
# open elf file, extract main header, strings and sections indexes
def open_elf(elf_filename):
# open the file
global elf_fd
global cstrings
global sections
global symtab
global strtab
elf_fd = open(elf_filename, "rb")
elf_size = os.stat(elf_filename).st_size
if verbose:
print(("Parsing: %s Size: %s(%s)") % (elf_filename,elf_size,hex(elf_size)))
# read elf header
data = elf_fd.read(52);
# see Elf32_Ehdr in esptool-ck for fields
elf_header = struct.Struct("<IIIIHHIIIIIHHHHHH").unpack(data)
e_shoff = elf_header[9]
e_shentsize = elf_header[14]
e_shnum = elf_header[15]
e_shstrndx = elf_header[16]
if verbose:
print(("Sections: e_shnum=%s(%s) e_shoff=%s(%s) e_shentsize=%s(%s) e_shstrndx=%s(%s)") %
(e_shnum,hex(e_shnum),e_shoff,hex(e_shoff),e_shentsize,hex(e_shentsize),e_shstrndx,hex(e_shstrndx)))
# get strings and sections
cstrings = []
sections = []
sections.append(get_elf_section (e_shstrndx,e_shoff,e_shentsize)) # read strings section info
cstrings = (read_elf_section_by_index(0))[0] # read strings
sections = []
for e_shndx in range(0,e_shnum): # section 0 not used
sections.append(get_elf_section (e_shndx,e_shoff,e_shentsize)) # name, size, offset, address
symtab = None
strtab = None
# tests
# search_elf_symbol ('call_user_start')
# for e_shndx in range(0,e_shnum):
# symtab = read_elf_section_by_index(e_shndx)
def close_elf():
elf_fd.close()
############ BIN creation (from gen_appbin) ############
CHECKSUM_INIT = 0xEF
chk_sum = CHECKSUM_INIT
total_bytes = 0
# append data to a file
def write_file(filename,data,clear_file=False):
global total_bytes
open_mode = 'ab'
if clear_file:
open_mode = 'wb'
fp = open(filename,open_mode)
if fp:
fp.seek(0,os.SEEK_END)
fp.write(data)
if verbose:
print(('+++ %8s(%8s) @ %8s(%8s) -> ')%
(len(data), hex(len(data)), total_bytes, hex(total_bytes)), end=' ')
total_bytes = total_bytes+len(data)
if verbose:
print(('%8s(%8s)')%
(total_bytes, hex(total_bytes)), end=' :: ')
fp.close()
else:
print ('!!! %s write fail\n'%(filename))
# append a section to output file. crate a heade for this section ans compute checksum
def combine_bin(section_name,dest_filename,use_section_offset_addr,need_chk):
global chk_sum
section = read_elf_section_by_name(section_name)
data_bin = section[0]
if use_section_offset_addr:
start_offset_addr = section[1]
else:
start_offset_addr = 0
data_len = len(data_bin)
if need_chk:
section_len = (data_len + 3) & (~3)
else:
section_len = (data_len + 15) & (~15)
header = struct.pack('<II',start_offset_addr,section_len)
write_file(dest_filename,header)
if verbose:
print(('add header1(%s) = %s (0x%s), %s (0x%s)')%(len(header), start_offset_addr, hex(start_offset_addr),
section_len, hex(section_len)))
write_file(dest_filename,data_bin)
if verbose:
print ('add section %s, size is %d, chk_sum=0x%s' % (section_name, section_len, hex(chk_sum)))
if need_chk:
for loop in range(len(data_bin)):
chk_sum ^= ord(chr(data_bin[loop]))
padding_len = section_len - data_len
if padding_len: # padding
padding = [0]*padding_len
write_file(dest_filename,bytes(padding))
if verbose:
print(('add padding(%s)')%(len(bytes(padding))))
if need_chk:
for loop in range(len(padding)):
chk_sum ^= ord(chr(padding[loop]))
# compute the crc for the generated file
def getFileCRC(_path):
try:
blocksize = 1024 * 64
f = open(_path,"rb")
str = f.read(blocksize)
crc = 0
while(len(str) != 0):
crc = zlib.crc32(str, crc)
str = f.read(blocksize)
f.close()
except:
print ('get file crc error!' )
return 0
return crc
BIN_MAGIC_FLASH = 0xE9
BIN_MAGIC_IROM = 0xEA
# bin formats (from esp8266_parse_bin.py)
# non ota: eagle.flash.bin (firmware.bin) (ido-rtos)
# Header: 0xe9 3 0x2 0x90 0x40100004
# Segments: .text, .data, .rodata
# Padding: 0-15 bytes + checksum
# ota: user1.16384.new.9.bin
# Header: 0xea 4 0x0 0x1 0x40100004
# Segment .irom0text
# Header: 0xe9 3 0x2 0x90 0x40100004
# Segments: .text, .data, .rodata
# Padding: 0-15 bytes + checksum
# Extra Data [no-header] of Length: 4 -> 0xe7 0x41 0x67 0x53
# read rections from elf file and generate bin files
def gen_appbin (user_bin, flash_mode, flash_clk_div, flash_size_map, flash_filename, iron_filename=None):
global chk_sum
chk_sum = CHECKSUM_INIT
clear_file = True
entry_symbol = search_elf_symbol('call_user_start')
if entry_symbol:
entry_addr = entry_symbol[1]
if verbose:
print("Entry addr = "+hex(entry_addr))
if user_bin: # add irom0ext to image
header = struct.pack('<BBBBI',BIN_MAGIC_IROM,4,0,user_bin,entry_addr)
sum_size = len(header)
write_file(flash_filename,header,clear_file)
clear_file = False
if verbose:
print(('add header2(%s) = %s %s %s %s %s')%(len(header), hex(BIN_MAGIC_IROM),
4, 0, user_bin, hex(entry_addr)))
# irom0.text.bin
combine_bin('.irom0.text',flash_filename,False,False)
else: # extract irom0ext
extract_elf_section_by_name ('.irom0.text', iron_filename)
byte2=int(flash_mode)&0xff
byte3=(((int(flash_size_map)<<4)| int(flash_clk_div))&0xff)
header = struct.pack('<BBBBI',BIN_MAGIC_FLASH,3,byte2,byte3,entry_addr)
sum_size = len(header)
write_file(flash_filename,header,clear_file)
clear_file = False
if verbose:
print(('add header3(%s) = %s %s %s %s %s')%
(len(header), hex(BIN_MAGIC_FLASH), 3, hex(byte2), hex(byte3), hex(entry_addr)))
combine_bin('.text',flash_filename,True,True)
combine_bin('.data',flash_filename,True,True)
combine_bin('.rodata',flash_filename,True,True)
# write checksum header
flash_data_line = 16
data_line_bits = 0xf
sum_size = os.path.getsize(flash_filename) + 1
sum_size = flash_data_line - (data_line_bits&sum_size)
if sum_size:
padding = [0]*(sum_size)
write_file(flash_filename,bytes(padding))
if verbose:
print(('add padding(%s)')%(len(padding)))
write_file(flash_filename,bytes([chk_sum&0xFF]))
if verbose:
print(('add chk=%s')%(chk_sum&0xFF))
if user_bin:
all_bin_crc = getFileCRC(flash_filename)
if verbose:
print ('genbin.py: crc32 before inversion = %s, %d' % (hex(all_bin_crc), all_bin_crc))
if sys.version_info.major >= 3:
if all_bin_crc > 0x80000000:
all_bin_crc = 0x100000000 - all_bin_crc - 1
else:
all_bin_crc = all_bin_crc + 1
else:
if all_bin_crc < 0:
all_bin_crc = abs(all_bin_crc) - 1
else :
all_bin_crc = abs(all_bin_crc) + 1
if verbose:
print ('genbin.py: crc32 after inversion = %s, %d' % (hex(all_bin_crc), all_bin_crc))
print (hex(all_bin_crc))
bytes_all_bin_crc = struct.pack('<I',all_bin_crc)
write_file(flash_filename,bytes_all_bin_crc)
if verbose:
print("")
############ Main ############
# return binary value for argument
def get_val (arg, choices, default):
var = default
try:
return choices.index(arg)
except:
return choices.index(default)
def help_and_exit():
print("genbin.py, v1.0.0, Didier Bertrand, 2022")
print("Format:")
print("genbin.py 0 flash_mode flash_clk_div flash_map_size in_file.elf out_flash.bin out_irom.bin")
print("genbin.py [1|2] flash_mode flash_clk_div flash_map_size in_file.elf out_user[1|2].bin ")
print("genbin.py 12 flash_mode flash_clk_div flash_map_size in_file.elf out_user1.bin out_user2.bin")
print("Exemples:")
print("python genbin.py 0 dio 40m 4MB-c1 firmware.elf firmware.bin firmware.bin.irom0text.bin")
print("python genbin.py 1 dio 40m 16MB firmware.elf user1.16384.new.9.bin")
print("python genbin.py 2 dio 40m 4MB-c1 firmware.elf user2.4096.new.6.bin")
print("python genbin.py 1 dio 40m 16MB firmware.elf user1.16384.new.9.bin")
print("python genbin.py 12 dio 40m 16MB firmware.elf user1.bin user2.bin")
exit(0)
def main():
global verbose
if len(sys.argv)<7 or len(sys.argv)>8:
help_and_exit()
user_app = sys.argv[1] # 0, 1, 2 or 12
if user_app[0]=='-':
verbose = True
user_app = user_app[1:]
flash_mode = get_val (sys.argv[2], ['qio','qout','dio','dout'], 'qio')
flash_clk_div = get_val (sys.argv[3], ['40m','26m','20m','80m'], '40m')
flash_size_map = get_val (sys.argv[4], ['512KB','256KB','1MB','2MB','4MB','2MB-c1','4MB-c1','4MB-c2','8MB','16MB'], '4MB-c2')
elf_filename = sys.argv[5]
open_elf (elf_filename)
if len(sys.argv)==8 and user_app=='0':
flash_filename = sys.argv[6] # eagle.flash.bin
iron_filename = sys.argv[7] # eagle.irom0text.bin
print(("Create %s and %s from %s") %
(flash_filename, iron_filename, elf_filename))
gen_appbin (0, flash_mode, flash_clk_div, flash_size_map, flash_filename, iron_filename)
elif len(sys.argv)==7 and (user_app=='1' or user_app=='2'):
user_file = sys.argv[6] # user1.bin or user2.bin
print(("Create %s from %s") %
(user_file, elf_filename))
gen_appbin (int(user_app), flash_mode, flash_clk_div, flash_size_map, user_file)
elif len(sys.argv)==8 and (user_app=='12'): # generate user1 and user2 file
user1_filename = sys.argv[6] # user1.bin or user2.bin
user2_filename = sys.argv[7] # user1.bin or user2.bin
print(("Create %s and %s from %s") %
(user1_filename, user2_filename, elf_filename))
gen_appbin (1, flash_mode, flash_clk_div, flash_size_map, user1_filename)
gen_appbin (2, flash_mode, flash_clk_div, flash_size_map, user2_filename)
else:
help_and_exit()
close_elf()
if __name__=='__main__':
main()