initial commit
This commit is contained in:
207
libraries/FastLED/ci/util/elf.py
Normal file
207
libraries/FastLED/ci/util/elf.py
Normal file
@@ -0,0 +1,207 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user