diff --git a/util/qualcomm/elf_segment_extractor.py b/util/qualcomm/elf_segment_extractor.py new file mode 100755 index 0000000000..7ba6e8aabf --- /dev/null +++ b/util/qualcomm/elf_segment_extractor.py @@ -0,0 +1,381 @@ +#!/usr/bin/env python +import sys +import os +import struct +import argparse # Import the argparse module + +# --- Constants for ELF parsing --- + +# e_ident (first 16 bytes of ELF header) +EI_MAG0 = 0 # File identifier +EI_MAG1 = 1 +EI_MAG2 = 2 +EI_MAG3 = 3 +EI_CLASS = 4 # File class (32-bit or 64-bit) +EI_DATA = 5 # Data encoding (endianness) +EI_VERSION = 6 # ELF header version +EI_OSABI = 7 # OS/ABI identification +EI_ABIVERSION = 8 # ABI version +EI_PAD = 9 # Start of padding bytes + +# ELF Class definitions +ELFCLASSNONE = 0 +ELFCLASS32 = 1 # 32-bit ELF +ELFCLASS64 = 2 # 64-bit ELF + +# ELF Data encoding definitions +ELFDATANONE = 0 +ELFDATA2LSB = 1 # Little-endian +ELFDATA2MSB = 2 # Big-endian + +# ELF Header offsets and sizes (for 32-bit and 64-bit) +# All values are in bytes +ELF_HEADER_SIZE_32 = 52 +ELF_HEADER_SIZE_64 = 64 + +# Program Header (Phdr) offsets and sizes (relative to start of Phdr entry) +# These vary based on ELF class (32-bit or 64-bit) + +# 32-bit Program Header (Elf32_Phdr) structure +# typedef struct { +# Elf32_Word p_type; /* Segment type */ +# Elf32_Off p_offset; /* Segment file offset */ +# Elf32_Addr p_vaddr; /* Segment virtual address */ +# Elf32_Addr p_paddr; /* Segment physical address */ +# Elf32_Word p_filesz; /* Segment size in file */ +# Elf32_Word p_memsz; /* Segment size in memory */ +# Elf32_Word p_flags; /* Segment flags */ +# Elf32_Word p_align; /* Segment alignment */ +# } Elf32_Phdr; +PHDR_32_FMT = ' 0: + f.seek(elf_header_info['e_phoff']) + all_program_headers_bytes = f.read(elf_header_info['e_phentsize'] * num_segments) + output_data += all_program_headers_bytes + print(f"Included all {num_segments} Program Headers ({len(all_program_headers_bytes)} bytes).") + else: + print("Warning: No Program Headers found in ELF file to extract for PHT.") + + # 3. Extract specific Segment data if requested via --segment + if segment_args_str is not None: + segments_to_extract_indices = [] + # Split the comma-separated string and process each segment argument + for seg_arg in segment_args_str.split(','): + seg_arg = seg_arg.strip() # Remove any whitespace + if not seg_arg: # Skip empty strings from multiple commas (e.g., "0,,1") + continue + + actual_segment_index = None + if seg_arg == "$" or seg_arg.upper() == "N": + if num_segments == 0: + print(f"Error: No segments found in ELF file '{elf_file_path}'. Cannot extract last segment for '{seg_arg}'.") + return + actual_segment_index = num_segments - 1 + print(f"'{seg_arg}' detected. Auto-selecting last segment (index {actual_segment_index}).") + else: + try: + actual_segment_index = int(seg_arg) + except ValueError: + print(f"Error: Invalid segment index or alias '{seg_arg}'. Segment index must be an integer, '$', or 'N'.") + return + + if not (0 <= actual_segment_index < num_segments): + print(f"Error: Segment index {actual_segment_index} is out of bounds for '{elf_file_path}'.") + print(f"This ELF file has {num_segments} segments (0 to {num_segments - 1}).") + return + + segments_to_extract_indices.append(actual_segment_index) + + # Now extract data for each resolved segment index + for current_segment_index in segments_to_extract_indices: + segment_info = parse_program_header(f, elf_header_info, current_segment_index) + if segment_info is None: + # Error message already printed by parse_program_header + return + + p_offset = segment_info['p_offset'] + p_filesz = segment_info['p_filesz'] + + if p_filesz > 0: + f.seek(p_offset) + segment_data_bytes = f.read(p_filesz) + output_data += segment_data_bytes + print(f"Included Segment {current_segment_index} data ({len(segment_data_bytes)} bytes) via --segment.") + else: + print(f"Warning: Segment {current_segment_index} has no data in the file (p_filesz is 0). Skipping via --segment.") + + # 4. Extract segments based on --hashtable criteria + if extract_hashtable: + print("\nSearching for segments with p_type = 0 (NULL) and p_flags = 0x02000000...") + found_hashtable_segments = False + for i in range(num_segments): + segment_info = parse_program_header(f, elf_header_info, i) + if segment_info is None: + continue # Skip if parsing failed for this segment + + if segment_info['p_type'] == 0 and segment_info['p_flags'] == 0x02000000: + found_hashtable_segments = True + p_offset = segment_info['p_offset'] + p_filesz = segment_info['p_filesz'] + + if p_filesz > 0: + f.seek(p_offset) + segment_data_bytes = f.read(p_filesz) + output_data += segment_data_bytes + print(f"Included Segment {i} (Type: NULL, Flags: 0x{segment_info['p_flags']:x}) data ({len(segment_data_bytes)} bytes) via --hashtable.") + else: + print(f"Warning: Segment {i} (Type: NULL, Flags: 0x{segment_info['p_flags']:x}) has no data in the file (p_filesz is 0). Skipping via --hashtable.") + if not found_hashtable_segments: + print("No segments found matching --hashtable criteria.") + + # Write the concatenated data to the output file + if output_data: + try: + with open(output_file_path, 'wb') as out_f: + out_f.write(output_data) + print(f"\nSuccessfully wrote {len(output_data)} bytes to '{output_file_path}'") + except IOError as io_e: + print(f"Error writing to output file '{output_file_path}': {io_e}") + except Exception as write_e: + print(f"An unexpected error occurred while writing: {write_e}") + else: + print("No data extracted based on the provided options. Output file was not created.") + + + except FileNotFoundError: + print(f"Error: ELF file not found at '{elf_file_path}'") + except Exception as e: + print(f"An error occurred: {e}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Extracts ELF Header, Program Headers, and/or specific Segments from an ELF file.", + formatter_class=argparse.RawTextHelpFormatter + ) + + parser.add_argument( + '--eh', + action='store_true', + help='Extract the ELF Header.' + ) + parser.add_argument( + '--pht', + action='store_true', + help='Extract all Program Headers (Program Header Table).' + ) + parser.add_argument( + '--segment', + type=str, + help='Specify a comma-separated list of 0-based segment indices to extract (e.g., "0,1,2").\n' + 'Use "$" or "N" for the last segment (e.g., "0,N"). Segments will be concatenated in order.' + ) + parser.add_argument( + '--hashtable', + action='store_true', + help='Extract all segments where p_type is NULL (0) and p_flags is 0x02000000.' + ) + parser.add_argument( + 'elf_file_path', + type=str, + help='Path to the input ELF file.' + ) + parser.add_argument( + 'output_file_path', + type=str, + help='Path to the output binary file where extracted data will be concatenated.' + ) + + args = parser.parse_args() + + # Ensure at least one extraction option is provided + if not args.eh and not args.pht and args.segment is None and not args.hashtable: + parser.error("At least one extraction option (--eh, --pht, --segment, or --hashtable) must be provided.") + + extract_elf_data( + args.elf_file_path, + args.output_file_path, + args.eh, + args.pht, + args.segment, + args.hashtable # Pass the new argument to the function + )