Analyze function calls using objdump to build call graph
(
elf_file: str, objdump_path: str, cppfilt_path: str
)
| 399 | |
| 400 | |
| 401 | def analyze_function_calls( |
| 402 | elf_file: str, objdump_path: str, cppfilt_path: str |
| 403 | ) -> dict[str, list[str]]: |
| 404 | """Analyze function calls using objdump to build call graph""" |
| 405 | print("Analyzing function calls using objdump...") |
| 406 | |
| 407 | # Use objdump to disassemble the binary |
| 408 | cmd = f'"{objdump_path}" -t "{elf_file}"' |
| 409 | print(f"Running: {cmd}") |
| 410 | symbol_output = run_command(cmd) |
| 411 | |
| 412 | # Build symbol address map for function symbols |
| 413 | symbol_map: dict[str, dict[str, str]] = {} # address -> {name, demangled} |
| 414 | function_symbols: set[str] = set() # set of function names |
| 415 | |
| 416 | for line in symbol_output.strip().split("\n"): |
| 417 | if not line.strip(): |
| 418 | continue |
| 419 | |
| 420 | # Parse objdump symbol table output |
| 421 | # Format: address flags section size name |
| 422 | parts = line.split() |
| 423 | if len(parts) >= 5 and ("F" in parts[1] or "f" in parts[1]): # Function symbol |
| 424 | try: |
| 425 | address = parts[0] |
| 426 | symbol_name = " ".join(parts[4:]) |
| 427 | |
| 428 | # Demangle the symbol name |
| 429 | demangled_name = demangle_symbol(symbol_name, cppfilt_path) |
| 430 | |
| 431 | entry: dict[str, str] = { |
| 432 | "name": symbol_name, |
| 433 | "demangled": demangled_name, |
| 434 | } |
| 435 | symbol_map[address] = entry |
| 436 | function_symbols.add(demangled_name) |
| 437 | except (ValueError, IndexError): |
| 438 | continue |
| 439 | |
| 440 | print(f"Found {len(function_symbols)} function symbols") |
| 441 | |
| 442 | # Now disassemble text sections to find function calls |
| 443 | cmd = f'"{objdump_path}" -d "{elf_file}"' |
| 444 | print(f"Running: {cmd}") |
| 445 | disasm_output = run_command(cmd) |
| 446 | |
| 447 | if not disasm_output: |
| 448 | print("Warning: No disassembly output received") |
| 449 | return {} |
| 450 | |
| 451 | # Parse disassembly to find function calls |
| 452 | call_graph: defaultdict[str, set[str]] = defaultdict( |
| 453 | set |
| 454 | ) # caller -> set of callees |
| 455 | current_function = None |
| 456 | |
| 457 | # Common call instruction patterns for different architectures |
| 458 | call_patterns = [ |
no test coverage detected