def merge_elfs(env, elf_in_file_name1, elf_in_file_name2, elf_in_file_xbl_sec, elf_out_file_name, is_elf1_64_bit, is_elf2_64_bit, is_elf_xbl_sec_64_bit, is_out_elf_64_bit, zi_oob_enabled): [elf_header1, phdr_table1] = \ mbn_tools.preprocess_elf_file(elf_in_file_name1) # Check to make sure second file path exists before using if elf_in_file_name2 != "": [elf_header2, phdr_table2] = \ mbn_tools.preprocess_elf_file(elf_in_file_name2) # Check to make sure xbl_sec file path exists before using if elf_in_file_xbl_sec != "": [elf_headerxblsec, phdr_tablexblsec] = \ mbn_tools.preprocess_elf_file(elf_in_file_xbl_sec) # Open Files elf_in_fp1 = mbn_tools.OPEN(elf_in_file_name1, "rb") if elf_in_file_name2 != "": elf_in_fp2 = mbn_tools.OPEN(elf_in_file_name2, "rb") if elf_in_file_xbl_sec != "": elf_in_fpxblsec = mbn_tools.OPEN(elf_in_file_xbl_sec, "rb") if elf_out_file_name is not None: elf_out_fp = mbn_tools.OPEN(elf_out_file_name, "wb+") # Calculate the new program header size. This is dependant on the output # ELF type and number of program headers going into output. if is_out_elf_64_bit: phdr_total_size = elf_header1.e_phnum * ELF64_PHDR_SIZE phdr_total_count = elf_header1.e_phnum else: phdr_total_size = elf_header1.e_phnum * ELF32_PHDR_SIZE phdr_total_count = elf_header1.e_phnum # This logic only applies if two files are to be merged if elf_in_file_name2 != "": if is_out_elf_64_bit: phdr_total_size += elf_header2.e_phnum * ELF64_PHDR_SIZE phdr_total_count += elf_header2.e_phnum else: phdr_total_size += elf_header2.e_phnum * ELF32_PHDR_SIZE phdr_total_count += elf_header2.e_phnum # Account for xbl_sec header if included if elf_in_file_xbl_sec != "": phdr_total_count += 1 if is_out_elf_64_bit: phdr_total_size += ELF64_PHDR_SIZE else: phdr_total_size += ELF32_PHDR_SIZE # Create a new ELF header for the output file if is_out_elf_64_bit: out_elf_header = mbn_tools.Elf64_Ehdr('\0' * ELF64_HDR_SIZE) out_elf_header.e_phoff = ELF64_HDR_SIZE out_elf_header.e_ehsize = ELF64_HDR_SIZE out_elf_header.e_phentsize = ELF64_PHDR_SIZE out_elf_header.e_machine = 183 out_elf_header.e_ident = str('\x7f' + 'E' + 'L' + 'F' + \ '\x02' + \ '\x01' + \ '\x01' + \ '\x00' + \ '\x00' + \ ('\x00' * 7)) out_elf_header.e_entry = elf_header1.e_entry else: out_elf_header = mbn_tools.Elf32_Ehdr('\0' * ELF32_HDR_SIZE) out_elf_header.e_phoff = ELF32_HDR_SIZE out_elf_header.e_ehsize = ELF32_HDR_SIZE out_elf_header.e_phentsize = ELF32_PHDR_SIZE out_elf_header.e_machine = 40 out_elf_header.e_entry = elf_header1.e_entry out_elf_header.e_ident = str('\x7f' + 'E' + 'L' + 'F' + \ '\x01' + \ '\x01' + \ '\x01' + \ '\x00' + \ '\x00' + \ ('\x00' * 7)) # Address needs to be verified that it is not greater than 32 bits # as it is possible to go from a 64 bit elf to 32. if (elf_header1.e_entry > 0xFFFFFFFF): print "ERROR: File 1's entry point is too large to convert." exit() out_elf_header.e_entry = elf_header1.e_entry # Common header entries out_elf_header.e_type = 2 out_elf_header.e_version = 1 out_elf_header.e_shoff = 0 out_elf_header.e_flags = 0 out_elf_header.e_shentsize = 0 out_elf_header.e_shnum = 0 out_elf_header.e_shstrndx = 0 # If ZI OOB is enabled then it is possible that a segment could be discarded # Scan for that instance and handle before setting e_phnum and writing header # Ensure ELF output is 32 bit if zi_oob_enabled == True and is_out_elf_64_bit == False: for i in range(len(phdr_table1)): if (phdr_table1[i].p_vaddr > 0xFFFFFFFF) or \ (phdr_table1[i].p_paddr > 0xFFFFFFFF): if phdr_table1[i].p_filesz == 0: phdr_total_count = phdr_total_count - 1 if elf_in_file_name2 != "": for i in range(len(phdr_table2)): if (phdr_table2[i].p_vaddr > 0xFFFFFFFF) or \ (phdr_table2[i].p_paddr > 0xFFFFFFFF): if phdr_table2[i].p_filesz == 0: phdr_total_count = phdr_total_count - 1 # Do not include xbl_sec in above calculation # xbl_sec is to be treated as a single blob # Now it is ok to populate the ELF header and write it out out_elf_header.e_phnum = phdr_total_count # write elf header if is_out_elf_64_bit == False: elf_out_fp.write(mbn_tools.Elf32_Ehdr.getPackedData(out_elf_header)) else: elf_out_fp.write(mbn_tools.Elf64_Ehdr.getPackedData(out_elf_header)) phdr_offset = out_elf_header.e_phoff # offset of where to put next phdr # offset the start of the segments just after the program headers segment_offset = roundup(out_elf_header.e_phoff + phdr_total_size, PAGE_SIZE) # Output first elf data for i in range(elf_header1.e_phnum): curr_phdr = phdr_table1[i] # Copy program header piece by piece to ensure possible conversion success if is_out_elf_64_bit == True: # Converting from 32 to 64 elf requires no data size validation new_phdr = mbn_tools.Elf64_Phdr('\0' * ELF64_PHDR_SIZE) new_phdr.p_type = curr_phdr.p_type new_phdr.p_offset = segment_offset new_phdr.p_vaddr = curr_phdr.p_vaddr new_phdr.p_paddr = curr_phdr.p_paddr new_phdr.p_filesz = curr_phdr.p_filesz new_phdr.p_memsz = curr_phdr.p_memsz new_phdr.p_flags = curr_phdr.p_flags new_phdr.p_align = curr_phdr.p_align else: # Converting from 64 to 32 elf requires data size validation # Note that there is an option to discard a segment if it is only ZI # and its address is greater than 32 bits new_phdr = mbn_tools.Elf32_Phdr('\0' * ELF32_PHDR_SIZE) new_phdr.p_type = curr_phdr.p_type new_phdr.p_offset = segment_offset if curr_phdr.p_vaddr > 0xFFFFFFFF: if (zi_oob_enabled == True) and (curr_phdr.p_filesz == 0): continue else: print "ERROR: File 1 VAddr is too large for conversion." exit() new_phdr.p_vaddr = curr_phdr.p_vaddr if curr_phdr.p_paddr > 0xFFFFFFFF: if (zi_oob_enabled == True) and (curr_phdr.p_filesz == 0): continue else: print "ERROR: File 1 PAddr is too large for conversion." exit() new_phdr.p_paddr = curr_phdr.p_paddr if curr_phdr.p_filesz > 0xFFFFFFFF: print "ERROR: File 1 Filesz is too large for conversion." exit() new_phdr.p_filesz = curr_phdr.p_filesz if curr_phdr.p_memsz > 0xFFFFFFFF: print "ERROR: File 1 Memsz is too large for conversion." exit() new_phdr.p_memsz = curr_phdr.p_memsz if curr_phdr.p_flags > 0xFFFFFFFF: print "ERROR: File 1 Flags is too large for conversion." exit() new_phdr.p_flags = curr_phdr.p_flags if curr_phdr.p_align > 0xFFFFFFFF: print "ERROR: File 1 Align is too large for conversion." exit() new_phdr.p_align = curr_phdr.p_align #print "i=",i #print "phdr_offset=", phdr_offset # update output file location to next phdr location elf_out_fp.seek(phdr_offset) # increment phdr_offset to next location phdr_offset += out_elf_header.e_phentsize inp_data_offset = curr_phdr.p_offset # used to read data from input file # print "inp_data_offset=" # print inp_data_offset # # print "curr_phdr.p_offset=" # print curr_phdr.p_offset # # print "curr_phdr.p_filesz=" # print curr_phdr.p_filesz # output current phdr if is_out_elf_64_bit == False: elf_out_fp.write(mbn_tools.Elf32_Phdr.getPackedData(new_phdr)) else: elf_out_fp.write(mbn_tools.Elf64_Phdr.getPackedData(new_phdr)) # Copy the ELF segment bytes_written = mbn_tools.file_copy_offset(elf_in_fp1, inp_data_offset, elf_out_fp, new_phdr.p_offset, new_phdr.p_filesz) # update data segment offset to be aligned after previous segment segment_offset += roundup(new_phdr.p_filesz, SEGMENT_ALIGN); elf_in_fp1.close() # Output second elf data if applicable if elf_in_file_name2 != "": for i in range(elf_header2.e_phnum): curr_phdr = phdr_table2[i] # Copy program header piece by piece to ensure possible conversion success if is_out_elf_64_bit == True: # Converting from 32 to 64 elf requires no data size validation new_phdr = mbn_tools.Elf64_Phdr('\0' * ELF64_PHDR_SIZE) new_phdr.p_type = curr_phdr.p_type new_phdr.p_offset = segment_offset new_phdr.p_vaddr = curr_phdr.p_vaddr new_phdr.p_paddr = curr_phdr.p_paddr new_phdr.p_filesz = curr_phdr.p_filesz new_phdr.p_memsz = curr_phdr.p_memsz new_phdr.p_flags = curr_phdr.p_flags new_phdr.p_align = curr_phdr.p_align else: # Converting from 64 to 32 elf requires data size validation # Note that there is an option to discard a segment if it is only ZI # and its address is greater than 32 bits new_phdr = mbn_tools.Elf32_Phdr('\0' * ELF32_PHDR_SIZE) new_phdr.p_type = curr_phdr.p_type new_phdr.p_offset = segment_offset if curr_phdr.p_vaddr > 0xFFFFFFFF: if (zi_oob_enabled == True) and (curr_phdr.p_filesz == 0): continue else: print "ERROR: File 2 VAddr is too large for conversion." exit() new_phdr.p_vaddr = curr_phdr.p_vaddr if curr_phdr.p_paddr > 0xFFFFFFFF: if (zi_oob_enabled == True) and (curr_phdr.p_filesz == 0): continue else: print "ERROR: File 2 PAddr is too large for conversion." exit() new_phdr.p_paddr = curr_phdr.p_paddr if curr_phdr.p_filesz > 0xFFFFFFFF: print "ERROR: File 2 Filesz is too large for conversion." exit() new_phdr.p_filesz = curr_phdr.p_filesz if curr_phdr.p_memsz > 0xFFFFFFFF: print "ERROR: File 2 Memsz is too large for conversion." exit() new_phdr.p_memsz = curr_phdr.p_memsz if curr_phdr.p_flags > 0xFFFFFFFF: print "ERROR: File 2 Flags is too large for conversion." exit() new_phdr.p_flags = curr_phdr.p_flags if curr_phdr.p_align > 0xFFFFFFFF: print "ERROR: File 2 Align is too large for conversion." exit() new_phdr.p_align = curr_phdr.p_align # print "i=",i # print "phdr_offset=", phdr_offset # update output file location to next phdr location elf_out_fp.seek(phdr_offset) # increment phdr_offset to next location phdr_offset += out_elf_header.e_phentsize inp_data_offset = curr_phdr.p_offset # used to read data from input file # print "inp_data_offset=" # print inp_data_offset # # print "curr_phdr.p_offset=" # print curr_phdr.p_offset # # print "curr_phdr.p_filesz=" # print curr_phdr.p_filesz # output current phdr if is_out_elf_64_bit == False: elf_out_fp.write(mbn_tools.Elf32_Phdr.getPackedData(new_phdr)) else: elf_out_fp.write(mbn_tools.Elf64_Phdr.getPackedData(new_phdr)) # Copy the ELF segment bytes_written = mbn_tools.file_copy_offset(elf_in_fp2, inp_data_offset, elf_out_fp, new_phdr.p_offset, new_phdr.p_filesz) # update data segment offset to be aligned after previous segment segment_offset += roundup(new_phdr.p_filesz, SEGMENT_ALIGN); elf_in_fp2.close() # Embed xbl_sec image if provided if elf_in_file_xbl_sec != "": # Scan pheaders in xbl_sec for segment that contains entry point address entry_seg_offset = -1 entry_addr = elf_headerxblsec.e_entry for i in range(elf_headerxblsec.e_phnum): phdr = phdr_tablexblsec[i] max_addr = phdr.p_vaddr + phdr.p_memsz if phdr.p_vaddr <= entry_addr <= max_addr: entry_seg_offset = phdr.p_offset break if entry_seg_offset == -1: print "Error: Failed to find entry point in any segment!" exit() # magical equation for program header's phys and virt addr phys_virt_addr = entry_addr - entry_seg_offset if is_out_elf_64_bit: # Converting from 32 to 64 elf requires no data size validation new_phdr = mbn_tools.Elf64_Phdr('\0' * ELF64_PHDR_SIZE) new_phdr.p_type = 0x1 new_phdr.p_offset = segment_offset new_phdr.p_vaddr = phys_virt_addr new_phdr.p_paddr = phys_virt_addr new_phdr.p_filesz = os.path.getsize(elf_in_file_xbl_sec) new_phdr.p_memsz = new_phdr.p_filesz new_phdr.p_flags = 0x5 | (mbn_tools.MI_PBT_SWAPPED_SEGMENT << mbn_tools.MI_PBT_FLAG_SEGMENT_TYPE_SHIFT); new_phdr.p_align = 0x1000 else: # Converting from 64 to 32 elf requires data size validation # Don't discard the segment containing xbl_sec, simply error out # if the address is greater than 32 bits new_phdr = mbn_tools.Elf32_Phdr('\0' * ELF32_PHDR_SIZE) new_phdr.p_type = 0x1 # new_phdr.p_offset = segment_offset new_phdr.p_flags = 0x5 | (mbn_tools.MI_PBT_SWAPPED_SEGMENT << mbn_tools.MI_PBT_FLAG_SEGMENT_TYPE_SHIFT); new_phdr.p_align = 0x1000 if phys_virt_addr > 0xFFFFFFFF: if zi_oob_enabled == False or curr_phdr.p_filesz != 0: print "ERROR: File xbl_sec VAddr or PAddr is too large for conversion." exit() new_phdr.p_vaddr = phys_virt_addr new_phdr.p_paddr = phys_virt_addr if os.path.getsize(elf_in_file_xbl_sec) > 0xFFFFFFFF: print "ERROR: File xbl_sec Filesz is too large for conversion." exit() new_phdr.p_filesz = os.path.getsize(elf_in_file_xbl_sec) new_phdr.p_memsz = new_phdr.p_filesz # update output file location to next phdr location elf_out_fp.seek(phdr_offset) # increment phdr_offset to next location phdr_offset += out_elf_header.e_phentsize # Copy entire xbl_sec file, so start from byte 0 inp_data_offset = 0 # Output xbl_sec's phdr elf_in_file_xbl_sec if is_out_elf_64_bit == False: elf_out_fp.write(mbn_tools.Elf32_Phdr.getPackedData(new_phdr)) else: elf_out_fp.write(mbn_tools.Elf64_Phdr.getPackedData(new_phdr)) # Copy the ENTIRE xbl_sec image bytes_written = mbn_tools.file_copy_offset(elf_in_fpxblsec, inp_data_offset, elf_out_fp, new_phdr.p_offset, new_phdr.p_filesz) # update data segment offset to be aligned after previous segment # Not necessary, unless appending more pheaders after this point segment_offset += roundup(new_phdr.p_filesz, SEGMENT_ALIGN); elf_in_fpxblsec.close() elf_out_fp.close() return 0
def write_elf(env, binary_out, current_version, min_version, is_elf_64_bit): if binary_out is not None: elf_out_fp = mbn_tools.OPEN(binary_out, "wb+") # Calculate the new program header size. This is dependant on the output # ELF type and number of program headers going into output. if is_elf_64_bit: phdr_total_size = ELF64_PHDR_SIZE phdr_total_count = 1 else: phdr_total_size = ELF32_PHDR_SIZE phdr_total_count = 1 # Create a new ELF header for the output file if is_elf_64_bit: out_elf_header = mbn_tools.Elf64_Ehdr('\0' * ELF64_HDR_SIZE) out_elf_header.e_phoff = ELF64_HDR_SIZE out_elf_header.e_ehsize = ELF64_HDR_SIZE out_elf_header.e_phentsize = ELF64_PHDR_SIZE out_elf_header.e_machine = 183 out_elf_header.e_ident = str('\x7f' + 'E' + 'L' + 'F' + \ '\x02' + \ '\x01' + \ '\x01' + \ '\x00' + \ '\x00' + \ ('\x00' * 7)) out_elf_header.e_entry = 0 else: out_elf_header = mbn_tools.Elf32_Ehdr('\0' * ELF32_HDR_SIZE) out_elf_header.e_phoff = ELF32_HDR_SIZE out_elf_header.e_ehsize = ELF32_HDR_SIZE out_elf_header.e_phentsize = ELF32_PHDR_SIZE out_elf_header.e_machine = 40 out_elf_header.e_entry = 0 out_elf_header.e_ident = str('\x7f' + 'E' + 'L' + 'F' + \ '\x01' + \ '\x01' + \ '\x01' + \ '\x00' + \ '\x00' + \ ('\x00' * 7)) # Common header entries out_elf_header.e_type = 0 out_elf_header.e_version = 1 out_elf_header.e_shoff = 0 out_elf_header.e_flags = 0 out_elf_header.e_shentsize = 0 out_elf_header.e_shnum = 0 out_elf_header.e_shstrndx = 0 # Now it is ok to populate the ELF header and write it out out_elf_header.e_phnum = phdr_total_count # write elf header if is_elf_64_bit == False: elf_out_fp.write(mbn_tools.Elf32_Ehdr.getPackedData(out_elf_header)) else: elf_out_fp.write(mbn_tools.Elf64_Ehdr.getPackedData(out_elf_header)) phdr_offset = out_elf_header.e_phoff # offset of where to put next phdr # offset the start of the segments just after the program headers segment_offset = roundup(out_elf_header.e_phoff + phdr_total_size, PAGE_SIZE) name_size = len(NOTE_NAME) version_string_size = (len(current_version['string']) + 1) * 2 # Copy program header piece by piece to ensure possible conversion success if is_elf_64_bit == True: new_phdr = mbn_tools.Elf64_Phdr('\0' * ELF64_PHDR_SIZE) new_phdr.p_type = 4 #Note segment new_phdr.p_offset = segment_offset new_phdr.p_vaddr = 0 new_phdr.p_paddr = 0 desc_size = 8 + version_string_size + pad(version_string_size, 8) new_phdr.p_filesz = 24 + name_size + 1 + pad(name_size + 1, 8) + desc_size new_phdr.p_memsz = 0 new_phdr.p_flags = 0 new_phdr.p_align = 0 else: new_phdr = mbn_tools.Elf32_Phdr('\0' * ELF32_PHDR_SIZE) new_phdr.p_type = 4 #Note segment new_phdr.p_offset = segment_offset new_phdr.p_vaddr = 0 new_phdr.p_paddr = 0 desc_size = 8 + version_string_size + pad(version_string_size, 4) new_phdr.p_filesz = 12 + name_size + 1 + pad(name_size + 1, 4) + desc_size new_phdr.p_memsz = 0 new_phdr.p_flags = 0 new_phdr.p_align = 0 # update output file location to next phdr location elf_out_fp.seek(phdr_offset) # increment phdr_offset to next location phdr_offset += out_elf_header.e_phentsize # output current phdr if is_elf_64_bit == False: elf_out_fp.write(mbn_tools.Elf32_Phdr.getPackedData(new_phdr)) else: elf_out_fp.write(mbn_tools.Elf64_Phdr.getPackedData(new_phdr)) elf_out_fp.seek(segment_offset) if is_elf_64_bit == True: elf_out_fp.write( struct.pack('QQQ', len(NOTE_NAME), desc_size, NOTE_TYPE)) elf_out_fp.write(NOTE_NAME) elf_out_fp.write(struct.pack('c', '\0')) for x in range(pad(name_size + 1, 8)): elf_out_fp.write(struct.pack('x')) elf_out_fp.write( struct.pack('II', current_version['num'], min_version['num'])) elf_out_fp.write(current_version['string'].encode('utf-16-le')) elf_out_fp.write(struct.pack('H', 0)) for x in range(pad(version_string_size, 8)): elf_out_fp.write(struct.pack('x')) else: elf_out_fp.write( struct.pack('III', len(NOTE_NAME), desc_size, NOTE_TYPE)) elf_out_fp.write(NOTE_NAME) elf_out_fp.write(struct.pack('c', '\0')) for x in range(pad(name_size + 1, 4)): elf_out_fp.write(struct.pack('x')) elf_out_fp.write( struct.pack('II', current_version['num'], min_version['num'])) elf_out_fp.write(struct.pack('H', 0)) elf_out_fp.write(current_version['string'].encode('utf-16-le')) for x in range(pad(version_string_size, 4)): elf_out_fp.write(struct.pack('x')) elf_out_fp.close() return 0