Skip to content

Commit cbd4e9b

Browse files
sylvioalvesradimkarnis
authored andcommitted
fix(elf2image): Multiple fixes from 3rd party frameworks
* Do not consider HEADER length for padding: When --ram-only-header is used, pad_len must only be incremented by IROM_ALIGN only when it within BOOTLOADER_FLASH_OFFSET, otherwise it will fail. * Pad sections to account for alignment: Parse ELF section alignment values and use those to check to see if successive sections are separated because of them. Pad a previous section if doing so would cause it to precisely align with the next section start address. This ensures that alignment requirements don't spoil our attempt to merge sections. * Limit additional padding to certain cases: During the segments merging, add additional padding only if --ram-only-header is enabled. Signed-off-by: Sylvio Alves <[email protected]> Signed-off-by: Keith Packard <[email protected]> Signed-off-by: Marek Matej <[email protected]>
1 parent 983338f commit cbd4e9b

File tree

1 file changed

+72
-19
lines changed

1 file changed

+72
-19
lines changed

esptool/bin_image.py

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,12 @@ class ImageSegment(object):
140140
"""Wrapper class for a segment in an ESP image
141141
(very similar to a section in an ELFImage also)"""
142142

143-
def __init__(self, addr, data, file_offs=None):
143+
def __init__(self, addr, data, file_offs=None, flags=0, align=4):
144144
self.addr = addr
145145
self.data = data
146146
self.file_offs = file_offs
147+
self.flags = flags
148+
self.align = align
147149
self.include_in_checksum = True
148150
if self.addr != 0:
149151
self.pad_to_alignment(
@@ -187,13 +189,33 @@ def get_memory_type(self, image):
187189
def pad_to_alignment(self, alignment):
188190
self.data = pad_to(self.data, alignment, b"\x00")
189191

192+
def end_addr_if_aligned(self, alignment):
193+
"""
194+
Return the segment end address as it would be if
195+
aligned as requested by the argument.
196+
"""
197+
end_addr = self.addr + len(self.data)
198+
addr_mod = end_addr % alignment
199+
if addr_mod != 0:
200+
end_addr += alignment - addr_mod
201+
return end_addr
202+
203+
def pad_until_addr(self, addr):
204+
"""
205+
Pad the segment with `0x00` starting with segment address
206+
until the address given by the argument.
207+
"""
208+
pad = addr - (self.addr + len(self.data))
209+
if pad > 0:
210+
self.data += b"\x00" * pad
211+
190212

191213
class ELFSection(ImageSegment):
192214
"""Wrapper class for a section in an ELF image, has a section
193215
name as well as the common properties of an ImageSegment."""
194216

195-
def __init__(self, name, addr, data):
196-
super(ELFSection, self).__init__(addr, data)
217+
def __init__(self, name, addr, data, flags, align=4):
218+
super(ELFSection, self).__init__(addr, data, flags=flags, align=align)
197219
self.name = name.decode("utf-8")
198220

199221
def __repr__(self):
@@ -409,6 +431,26 @@ def merge_adjacent_segments(self):
409431
# merged in
410432
elem = self.segments[i - 1]
411433
next_elem = self.segments[i]
434+
435+
# When creating the images from 3rd-party frameworks ELFs, the merging
436+
# could bring together segments with incompatible alignment requirements.
437+
# At this point, we add padding so the resulting placement respects the
438+
# original alignment requirements of those segments.
439+
if self.ROM_LOADER != ESP8266ROM and self.ram_only_header:
440+
elem_pad_addr = elem.end_addr_if_aligned(next_elem.align)
441+
442+
if (
443+
elem_pad_addr != elem.addr + len(elem.data)
444+
and elem_pad_addr == next_elem.addr
445+
):
446+
log.info(
447+
"Inserting {} bytes padding between {} and {}".format(
448+
next_elem.addr - (elem.addr + len(elem.data)),
449+
elem.name,
450+
next_elem.name,
451+
)
452+
)
453+
elem.pad_until_addr(elem_pad_addr)
412454
if all(
413455
(
414456
elem.get_memory_type(self) == next_elem.get_memory_type(self),
@@ -832,10 +874,7 @@ def get_alignment_data_needed(segment):
832874
# Some chips have a non-zero load offset (eg. 0x1000)
833875
# therefore we shift the ROM segments "-load_offset"
834876
# so it will be aligned properly after it is flashed
835-
align_min = (
836-
self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET - self.SEG_HEADER_LEN
837-
)
838-
if pad_len < align_min:
877+
if pad_len < self.ROM_LOADER.BOOTLOADER_FLASH_OFFSET:
839878
# in case pad_len does not fit minimum alignment,
840879
# pad it to next aligned boundary
841880
pad_len += self.IROM_ALIGN
@@ -1326,10 +1365,18 @@ def _read_sections(self, f, section_header_offs, section_header_count, shstrndx)
13261365
section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER)
13271366

13281367
def read_section_header(offs):
1329-
name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from(
1330-
"<LLLLLL", section_header[offs:]
1331-
)
1332-
return (name_offs, sec_type, lma, size, sec_offs)
1368+
(
1369+
name_offs,
1370+
sec_type,
1371+
_flags,
1372+
lma,
1373+
sec_offs,
1374+
size,
1375+
_,
1376+
_,
1377+
align,
1378+
) = struct.unpack_from("<LLLLLLLLL", section_header[offs:])
1379+
return (name_offs, sec_type, lma, size, sec_offs, _flags, align)
13331380

13341381
all_sections = [read_section_header(offs) for offs in section_header_offsets]
13351382
prog_sections = [s for s in all_sections if s[1] in ELFFile.PROG_SEC_TYPES]
@@ -1338,7 +1385,7 @@ def read_section_header(offs):
13381385
# search for the string table section
13391386
if (shstrndx * self.LEN_SEC_HEADER) not in section_header_offsets:
13401387
raise FatalError(f"ELF file has no STRTAB section at shstrndx {shstrndx}")
1341-
_, sec_type, _, sec_size, sec_offs = read_section_header(
1388+
_, sec_type, _, sec_size, sec_offs, _flags, align = read_section_header(
13421389
shstrndx * self.LEN_SEC_HEADER
13431390
)
13441391
if sec_type != ELFFile.SEC_TYPE_STRTAB:
@@ -1358,14 +1405,20 @@ def read_data(offs, size):
13581405
return f.read(size)
13591406

13601407
prog_sections = [
1361-
ELFSection(lookup_string(n_offs), lma, read_data(offs, size))
1362-
for (n_offs, _type, lma, size, offs) in prog_sections
1408+
ELFSection(
1409+
lookup_string(n_offs),
1410+
lma,
1411+
read_data(offs, size),
1412+
flags=_flags,
1413+
align=align,
1414+
)
1415+
for (n_offs, _type, lma, size, offs, _flags, align) in prog_sections
13631416
if lma != 0 and size > 0
13641417
]
13651418
self.sections = prog_sections
13661419
self.nobits_sections = [
1367-
ELFSection(lookup_string(n_offs), lma, b"")
1368-
for (n_offs, _type, lma, size, offs) in nobits_secitons
1420+
ELFSection(lookup_string(n_offs), lma, b"", flags=_flags, align=align)
1421+
for (n_offs, _type, lma, size, offs, _flags, align) in nobits_secitons
13691422
if lma != 0 and size > 0
13701423
]
13711424

@@ -1398,7 +1451,7 @@ def read_segment_header(offs):
13981451
_flags,
13991452
_align,
14001453
) = struct.unpack_from("<LLLLLLLL", segment_header[offs:])
1401-
return (seg_type, lma, size, seg_offs)
1454+
return (seg_type, lma, size, seg_offs, _flags, _align)
14021455

14031456
all_segments = [read_segment_header(offs) for offs in segment_header_offsets]
14041457
prog_segments = [s for s in all_segments if s[0] == ELFFile.SEG_TYPE_LOAD]
@@ -1408,8 +1461,8 @@ def read_data(offs, size):
14081461
return f.read(size)
14091462

14101463
prog_segments = [
1411-
ELFSection(b"PHDR", lma, read_data(offs, size))
1412-
for (_type, lma, size, offs) in prog_segments
1464+
ELFSection(b"PHDR", lma, read_data(offs, size), flags=_flags, align=_align)
1465+
for (_type, lma, size, offs, _flags, _align) in prog_segments
14131466
if lma != 0 and size > 0
14141467
]
14151468
self.segments = prog_segments

0 commit comments

Comments
 (0)