208 lines
6.6 KiB
Python
208 lines
6.6 KiB
Python
# pyright: reportUnknownMemberType=false
|
|
import subprocess
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List
|
|
|
|
|
|
def run_command(command: List[str], show_output: bool = False) -> str:
|
|
"""
|
|
Run a command using subprocess and capture the output.
|
|
|
|
Args:
|
|
command (list): Command to run.
|
|
show_output (bool): Print command and its output if True.
|
|
|
|
Returns:
|
|
str: Standard output of the command.
|
|
|
|
Raises:
|
|
RuntimeError: If the command fails.
|
|
"""
|
|
if show_output:
|
|
print(f"Running command: {' '.join(command)}")
|
|
result = subprocess.run(command, capture_output=True, text=True)
|
|
if result.returncode != 0:
|
|
raise RuntimeError(f"Command failed: {' '.join(command)}\n{result.stderr}")
|
|
if show_output and result.stdout:
|
|
print(f"Command output: {result.stdout}")
|
|
return result.stdout
|
|
|
|
|
|
def analyze_elf_file(objdump_path: Path, cppfilt_path: Path, elf_file: Path):
|
|
"""
|
|
Analyze the ELF file using objdump to display its contents.
|
|
|
|
Args:
|
|
objdump_path (Path): Path to the objdump executable.
|
|
cppfilt_path (Path): Path to the c++filt executable.
|
|
elf_file (Path): Path to the ELF file.
|
|
"""
|
|
command = [str(objdump_path), "-h", str(elf_file)] # "-h" option shows headers.
|
|
print(f"Analyzing ELF file: {elf_file}")
|
|
output = run_command(command, show_output=True)
|
|
print("\nELF File Analysis:")
|
|
print(output)
|
|
list_symbols_and_sizes(objdump_path, cppfilt_path, elf_file)
|
|
|
|
|
|
def cpp_filt(cppfilt_path: Path, input_text: str) -> str:
|
|
"""
|
|
Demangle C++ symbols using c++filt.
|
|
|
|
Args:
|
|
cppfilt_path (Path): Path to c++filt executable.
|
|
input_text (str): Text to demangle.
|
|
|
|
Returns:
|
|
str: Demangled text.
|
|
"""
|
|
command = [str(cppfilt_path), "-t", "-n"]
|
|
print(f"Running c++filt on input text with {cppfilt_path}")
|
|
process = subprocess.Popen(
|
|
command,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
)
|
|
stdout, stderr = process.communicate(input=input_text)
|
|
if process.returncode != 0:
|
|
raise RuntimeError(f"Error running c++filt: {stderr}")
|
|
return stdout
|
|
|
|
|
|
def dump_symbol_sizes(nm_path: Path, cpp_filt_path: Path, elf_file: Path) -> str:
|
|
nm_command = [
|
|
str(nm_path),
|
|
"-S",
|
|
"--size-sort",
|
|
str(elf_file),
|
|
]
|
|
print(f"Listing symbols and sizes in ELF file: {elf_file}")
|
|
print("Running command: ", " ".join(nm_command))
|
|
nm_result = subprocess.run(
|
|
nm_command,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
)
|
|
if nm_result.returncode != 0:
|
|
raise RuntimeError(f"Error running nm command: {nm_result.stderr}")
|
|
|
|
cpp_filt_command = [str(cpp_filt_path), "--no-strip-underscore"]
|
|
print("Running c++filt command: ", " ".join(cpp_filt_command))
|
|
cpp_filt_result = subprocess.run(
|
|
cpp_filt_command,
|
|
input=nm_result.stdout,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
)
|
|
if cpp_filt_result.returncode != 0:
|
|
raise RuntimeError(f"Error running c++filt command: {cpp_filt_result.stderr}")
|
|
|
|
# now reverse sort the lines
|
|
lines = cpp_filt_result.stdout.splitlines()
|
|
|
|
@dataclass
|
|
class Entry:
|
|
address: str
|
|
size: int
|
|
everything_else: str
|
|
|
|
def parse_line(line: str) -> Entry:
|
|
address, size, *rest = line.split()
|
|
return Entry(address, int(size, 16), " ".join(rest))
|
|
|
|
data: list[Entry] = [parse_line(line) for line in lines]
|
|
data.sort(key=lambda x: x.size, reverse=True)
|
|
lines = [f"{d.size:6d} {d.everything_else}" for d in data]
|
|
return "\n".join(lines)
|
|
|
|
|
|
def demangle_symbol(cppfilt_path: Path, symbol: str) -> str:
|
|
"""
|
|
Demangle a C++ symbol using c++filt.
|
|
|
|
Args:
|
|
cppfilt_path (Path): Path to the c++filt executable.
|
|
symbol (str): The symbol to demangle.
|
|
|
|
Returns:
|
|
str: The demangled symbol.
|
|
"""
|
|
command = [str(cppfilt_path), symbol]
|
|
return run_command(command, show_output=False).strip()
|
|
|
|
|
|
def list_symbols_and_sizes(objdump_path: Path, cppfilt_path: Path, elf_file: Path):
|
|
"""
|
|
List all symbols and their sizes from the ELF file using objdump.
|
|
|
|
Args:
|
|
objdump_path (Path): Path to the objdump executable.
|
|
cppfilt_path (Path): Path to the c++filt executable.
|
|
elf_file (Path): Path to the ELF file.
|
|
"""
|
|
command = [
|
|
str(objdump_path),
|
|
"-t",
|
|
str(elf_file),
|
|
] # "-t" option lists symbols with sizes.
|
|
print(f"Listing symbols and sizes in ELF file: {elf_file}")
|
|
output = run_command(command, show_output=False)
|
|
|
|
symbols: List[Dict[str, Any]] = []
|
|
for line in output.splitlines():
|
|
parts = line.split()
|
|
# Expected parts length can vary, check if size and section index (parts[2] & parts[4]) are valid
|
|
if len(parts) > 5 and parts[2].isdigit() and parts[4].startswith("."):
|
|
symbol = {
|
|
"name": parts[-1],
|
|
"size": int(parts[2], 16), # size is in hex format
|
|
"section": parts[4],
|
|
"type": parts[3],
|
|
}
|
|
symbols.append(symbol)
|
|
|
|
if symbols:
|
|
print("\nSymbols and Sizes in ELF File:")
|
|
for symbol in symbols:
|
|
demangled_name = demangle_symbol(cppfilt_path, symbol["name"])
|
|
print(
|
|
f"Symbol: {demangled_name}, Size: {symbol['size']} bytes, Type: {symbol['type']}, Section: {symbol['section']}"
|
|
)
|
|
else:
|
|
print("No symbols found or unable to parse symbols correctly.")
|
|
|
|
|
|
def check_elf_format(objdump_path: Path, elf_file: Path):
|
|
"""
|
|
Check the format of the ELF file using objdump to confirm it's being read correctly.
|
|
|
|
Args:
|
|
objdump_path (Path): Path to the objdump executable.
|
|
elf_file (Path): Path to the ELF file.
|
|
"""
|
|
command = [str(objdump_path), "-f", str(elf_file)]
|
|
print(f"Checking ELF file format: {elf_file}")
|
|
output = run_command(command, show_output=True)
|
|
print("\nELF File Format Information:")
|
|
print(output)
|
|
|
|
|
|
def check_section_contents(objdump_path: Path, elf_file: Path):
|
|
"""
|
|
Dump the contents of all sections in the ELF file using objdump.
|
|
|
|
Args:
|
|
objdump_path (Path): Path to the objdump executable.
|
|
elf_file (Path): Path to the ELF file.
|
|
"""
|
|
command = [str(objdump_path), "-s", str(elf_file)]
|
|
print(f"Dumping all sections of ELF file: {elf_file}")
|
|
output = run_command(command, show_output=True)
|
|
print("\nELF File Sections Content:")
|
|
print(output)
|