Загрузка данных
#!/usr/bin/env python3
import sys
import subprocess
import os
import re
import struct
def patch_boot_data_length(bin_path, final_length):
""" Patches the NXP Boot Data 'Length' field inside the final binary """
if not os.path.exists(bin_path):
return
try:
with open(bin_path, "r+b") as f:
f.seek(0x24) # Fixed offset for Boot Data Length
f.write(struct.pack("<I", final_length))
print(f"[LMA Filter] BootROM Patch: Successfully updated Boot Data Length to {final_length} bytes ({hex(final_length)})")
except Exception as e:
print(f"[LMA Filter] BootROM Patch Error: {e}")
def main():
if len(sys.argv) < 2:
print("[LMA Filter] Error: No real objcopy path provided.")
sys.exit(1)
real_objcopy = sys.argv[1] # CRITICAL FIX: Extract string path, not full list
args = sys.argv[2:]
elf_path = "zephyr.elf"
for arg in args:
if arg.endswith("zephyr.elf"):
elf_path = arg
break
# Avoid infinite loops during internal Zephyr post-processing steps
is_elf_to_elf_adjust = any("--change-section-lma" in arg for arg in args) and ("zephyr.elf" in elf_path) and not any(fmt in " ".join(args) for fmt in ["ihex", "binary"])
if is_elf_to_elf_adjust:
sys.exit(0)
is_hex_or_bin_generation = any("--output-target=" in arg for arg in args) or any(fmt in " ".join(args) for fmt in ["ihex", "binary"])
if is_hex_or_bin_generation and os.path.exists(elf_path):
sdk_bin_dir = os.path.dirname(real_objcopy)
readelf_bin = os.path.join(sdk_bin_dir, "arm-zephyr-eabi-readelf")
if not os.path.exists(readelf_bin):
for file in os.listdir(sdk_bin_dir):
if file.endswith("readelf"):
readelf_bin = os.path.join(sdk_bin_dir, file)
break
# Step 1: Find the absolute physical end of the CODE region in SDRAM (0x80000000 - 0x803FFFFF)
code_end_vma = 0x80000000
if os.path.exists(readelf_bin):
# CRITICAL FIX: Force English locale (LC_ALL=C) to avoid Russian readelf output mismatches
res = subprocess.run([readelf_bin, "-SW", elf_path], capture_output=True, text=True, env={"LC_ALL": "C"})
if res.returncode == 0:
section_regex = re.compile(r'^\s*\[\s*\d+\]\s+([\.\w\-_]+)\s+(\w+)\s+([0-9a-fA-F]{8,16})\s+([0-9a-fA-F]{6,8})\s+([0-9a-fA-F]{6,8})')
for line in res.stdout.splitlines():
match = section_regex.match(line)
if match:
sec_name, sec_type, vma_str, _, size_str = match.groups()
vma_val = int(vma_str, 16)
size_val = int(size_str, 16)
if 0x80000000 <= vma_val < 0x80400000 and sec_type != "NOBITS":
end_pos = vma_val + size_val
if end_pos > code_end_vma:
code_end_vma = end_pos
code_end_lma = code_end_vma - 0x20000000
# MPU-Safe alignment for data sections packing
flash_data_start_lma = (code_end_lma + 0x3FFF) & ~0x3FFF
print(f"[LMA Filter] Dynamic MPU-Safe Packing Initiated:")
print(f" -> Code region ends at Flash LMA: {hex(code_end_lma)}")
print(f" -> Data payload will start at aligned Flash LMA: {hex(flash_data_start_lma)}")
# Step 2: Parse and collect sections based strictly on ELF Flags and Types
dynamic_offsets = []
final_payload_end_lma = flash_data_start_lma
if os.path.exists(readelf_bin):
# CRITICAL FIX: Force English locale (LC_ALL=C) here as well
res = subprocess.run([readelf_bin, "-SW", elf_path], capture_output=True, text=True, env={"LC_ALL": "C"})
if res.returncode == 0:
section_regex = re.compile(
r'^\s*\[\s*\d+\]\s+([\.\w\-_]+)\s+(\w+)\s+([0-9a-fA-F]{8,16})\s+[0-9a-fA-F]+\s+([0-9a-fA-F]+)\s+[0-9a-fA-F]+\s+([AXMSILG01-9\s]+)'
)
# First collect all valid sections
parsed_sections = []
for line in res.stdout.splitlines():
match = section_regex.match(line)
if match:
sec_name, sec_type, vma_str, size_str, flags_str = match.groups()
vma_val = int(vma_str, 16)
size_val = int(size_str, 16)
is_alloc = 'A' in flags_str
has_contents = (sec_type != "NOBITS") and (size_val > 0)
is_in_sdram = (0x80000000 <= vma_val < 0x82000000)
if is_alloc and has_contents and is_in_sdram:
parsed_sections.append((vma_val, size_val, sec_name))
# Sort strictly by VMA to prevent overlapping segments faults in objcopy
parsed_sections.sort(key=lambda x: x)
# Build targeted LMA change arguments
current_flash_lma = flash_data_start_lma
for vma_val, size_val, sec_name in parsed_sections:
if vma_val >= 0x80400000:
dynamic_offsets += ["--change-section-lma", f"{sec_name}={hex(current_flash_lma)}"]
current_flash_lma += (size_val + 3) & ~3
final_payload_end_lma = current_flash_lma
else:
standard_lma = vma_val - 0x20000000
dynamic_offsets += ["--change-section-lma", f"{sec_name}={hex(standard_lma)}"]
# Run the standard objcopy tool to generate final hex or bin targets
final_cmd = [real_objcopy] + dynamic_offsets + args
subprocess.run(final_cmd, check=True)
# CRITICAL FIX FOR GDB LOAD: Apply the exact same LMA patches back into 'zephyr.elf' file itself
if dynamic_offsets: # Only patch if we successfully parsed the ELF sections
print("[LMA Filter] GDB Patching: Applying LMA modifications directly to zephyr.elf...")
elf_patch_cmd = [real_objcopy] + dynamic_offsets + [elf_path, elf_path]
subprocess.run(elf_patch_cmd, check=True)
# Step 3: Patch the generated binary file for standalone boot validation
calculated_image_size = (final_payload_end_lma - 0x60000000)
elf_dir = os.path.dirname(elf_path)
potential_bin = os.path.join(elf_dir, "zephyr.bin")
if not os.path.exists(potential_bin):
potential_bin = os.path.join(os.getcwd(), "zephyr", "zephyr.bin")
if os.path.exists(potential_bin):
patch_boot_data_length(potential_bin, calculated_image_size)
if __name__ == "__main__":
main()