| ############################################################################ |
| # SPDX-License-Identifier: Apache-2.0 |
| # |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. The |
| # ASF licenses this file to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance with the |
| # License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| # |
| ############################################################################ |
| |
| """ELF file parser for LTP test case extraction. |
| |
| Used during test collection phase without fixture dependencies |
| """ |
| |
| import logging |
| import os |
| import re |
| import subprocess |
| from typing import List, Pattern, Union |
| |
| |
| class Symbol: |
| """ELF symbol representation.""" |
| |
| def __init__(self, name: str, address: str = "", symbol_type: str = ""): |
| """Initialize ELF symbol representation.""" |
| self.name = name |
| self.address = address |
| self.type = symbol_type |
| |
| |
| class ElfParser: |
| """ELF file parser for extracting symbols.""" |
| |
| def __init__(self, elf_path: str): |
| """Initialize ELF file parser.""" |
| if not os.path.exists(elf_path) or not self._is_elf_file(elf_path): |
| raise AttributeError("can't load ELF file") |
| |
| self.elf_path = elf_path |
| self._symbols: List[Symbol] = [] |
| |
| def _is_elf_file(self, path: str) -> bool: |
| """Check if this is ELF file.""" |
| try: |
| with open(path, "rb") as f: |
| magic = f.read(4) |
| return magic == b"\x7fELF" |
| except Exception: # pragma: no cover |
| return False |
| |
| @property |
| def symbols(self) -> List[Symbol]: |
| """Get all symbols from ELF file.""" |
| if not self._symbols: |
| self._symbols = self._extract_symbols() |
| return self._symbols |
| |
| def _extract_symbols(self) -> List[Symbol]: |
| """Extract symbols using nm command.""" |
| try: |
| result = subprocess.run( |
| ["nm", "--defined-only", self.elf_path], |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE, |
| text=True, |
| check=True, |
| ) |
| |
| symbols = [] |
| for line in result.stdout.splitlines(): |
| line = line.strip() |
| if not line: |
| continue # pragma: no cover |
| |
| parts = line.split() |
| if len(parts) >= 3: |
| address, symbol_type, name = parts[0], parts[1], parts[2] |
| symbols.append(Symbol(name, address, symbol_type)) |
| elif len(parts) >= 2: # pragma: no cover |
| symbol_type, name = parts[0], parts[1] |
| symbols.append(Symbol(name, symbol_type=symbol_type)) |
| elif len(parts) == 1: # pragma: no cover |
| name = parts[0] |
| symbols.append(Symbol(name)) |
| |
| return symbols |
| |
| except subprocess.CalledProcessError as e: # pragma: no cover |
| logging.error( |
| f"Failed to extract symbols from {self.elf_path}: {e}" |
| ) |
| return [] |
| |
| except Exception as e: # pragma: no cover |
| logging.error(f"Unexpected error parsing ELF symbols: {e}") |
| return [] |
| |
| def get_symbols_by_pattern( |
| self, prefix: str = "", suffix: str = "" |
| ) -> List[Symbol]: |
| """Filter symbols by prefix and suffix.""" |
| return [ |
| s |
| for s in self.symbols |
| if s.name.startswith(prefix) and s.name.endswith(suffix) |
| ] |
| |
| def has_symbol(self, symbol_name: Union[str, Pattern[str]]) -> bool: |
| """Check if symbol exists.""" |
| if isinstance(symbol_name, str): |
| return any(symbol.name == symbol_name for symbol in self.symbols) |
| |
| if isinstance(symbol_name, re.Pattern): |
| return any( |
| symbol_name.search(symbol.name) for symbol in self.symbols |
| ) |
| raise ValueError("symbol_name must be either str or re.Pattern") |