Iakes Goenaga

26 papers B 2C 1Journal 9Unranked 12
YearRankTypeTitle / Venue / Authors
2025 B conf
COLING
Iker de la Iglesia, Iakes Goenaga, Johanna Ramirez-Romero, Jose Maria Villa-Gonzalez, Josu Goikoetxea, Ander Barrena
2024 J jnl
Artif. Intell. Medicine
Iakes Goenaga, Aitziber Atutxa, Koldo Gojenola, Maite Oronoz, Rodrigo Agerri
2024 J jnl
CoRR
Iker de la Iglesia, Iakes Goenaga, Johanna Ramirez-Romero, Jose Maria Villa-Gonzalez, Josu Goikoetxea, Ander Barrena
2023 J jnl
BMC Bioinform.
Iakes Goenaga, Edgar Andrés, Koldo Gojenola, Aitziber Atutxa
2023 ch.
European Language Equality
Inguna Skadina, Andrejs Vasiljevs, Marcis Pinnis, Aivars Berzins, Nora Aranberri, Joachim Van den Bogaert, Sally O'Connor, Mercedes García-Martínez, Iakes Goenaga, Jan Hajic, Manuel Herranz, Christian Lieske, Martin Popel, Maja Popovic, Sheila Castilho, Federico Gaspari, Rudolf Rosa, Riccardo Superbo, Andy Way
2023 J jnl
CoRR
Iakes Goenaga, Aitziber Atutxa, Koldo Gojenola, Maite Oronoz, Rodrigo Agerri
2023 conf
SEPLN (Projects and Demonstrations)
Rodrigo Agerri, Iñigo Alonso, Aitziber Atutxa, Ander Berrondo, Ainara Estarrona, Iker García-Ferrero, Iakes Goenaga, Koldo Gojenola, Maite Oronoz, Igor Perez-Tejedor, German Rigau, Anar Yeginbergenova
2023 J jnl
CoRR
Rodrigo Agerri, Iñigo Alonso, Aitziber Atutxa, Ander Berrondo, Ainara Estarrona, Iker García-Ferrero, Iakes Goenaga, Koldo Gojenola, Maite Oronoz, Igor Perez-Tejedor, German Rigau, Anar Yeginbergenova
2022 ch.
European Language Equality
Rodrigo Agerri, Eneko Agirre, Itziar Aldabe, Nora Aranberri, Jose Maria Arriola, Aitziber Atutxa, Gorka Azkune, Jon Ander Campos, Arantza Casillas, Ainara Estarrona, Aritz Farwell, Iakes Goenaga, Josu Goikoetxea, Koldo Gojenola, Inma Hernáez, Mikel Iruskieta, Gorka Labaka, Oier Lopez de Lacalle, Eva Navas, Maite Oronoz, Arantxa Otegi, Alicia Pérez, Olatz Perez-de-Viñaspre, German Rigau, Ander Salaberria, Jon Sánchez, Ibon Saratxaga, Aitor Soroa
2022 B conf
LREC
Ona De Gibert Bonet, Iakes Goenaga, Jordi Armengol-Estapé, Olatz Perez-de-Viñaspre, Carla Parra Escartín, Marina Sanchez, Marcis Pinnis, Gorka Labaka, Maite Melero
2021 J jnl
J. Biomed. Informatics
Iakes Goenaga, Xabier Lahuerta, Aitziber Atutxa, Koldo Gojenola
2019 conf
BioNLP-OST@EMNLP-IJNCLP
Xabier Lahuerta, Iakes Goenaga, Koldo Gojenola, Aitziber Atutxa, Maite Oronoz
2019 conf
IberLEF@SEPLN
Iakes Goenaga, Sergio Santana, Sara Santiso Gonzáles, Koldo Gojenola, Alicia Pérez, Arantza Casillas
2019 J jnl
Int. J. Medical Informatics
Arantza Casillas, Nerea Ezeiza, Iakes Goenaga, Alicia Pérez, Xabier Soto
2018 conf
IberEval@SEPLN
Iakes Goenaga, Aitziber Atutxa, Koldo Gojenola, Arantza Casillas, Arantza Díaz de Ilarraza, Nerea Ezeiza, Maite Oronoz, Alicia Pérez, Olatz Perez-de-Viñaspre
2018 conf
IberEval@SEPLN
Iakes Goenaga, Aitziber Atutxa, Koldo Gojenola, Arantza Casillas, Arantza Díaz de Ilarraza, Nerea Ezeiza, Maite Oronoz, Alicia Pérez, Olatz Perez-de-Viñaspre
2018 conf
BIBM
Mikel Laburu, Alicia Pérez, Arantza Casillas, Iakes Goenaga, Maite Oronoz
2018 conf
CLEF (Working Notes)
Aitziber Atutxa, Arantza Casillas, Nerea Ezeiza, Víctor Fresno, Iakes Goenaga, Koldo Gojenola, Raquel Martínez, Maite Oronoz Anchordoqui, Olatz Perez-de-Viñaspre
2016 conf
TSD
Arantxa Otegi, Nerea Ezeiza, Iakes Goenaga, Gorka Labaka
2013 conf
SPMRL@EMNLP
Iakes Goenaga, Koldo Gojenola, Nerea Ezeiza
2013 conf
SPMRL@EMNLP
Djamé Seddah, Reut Tsarfaty, Sandra Kübler, Marie Candito, Jinho D. Choi, Richárd Farkas, Jennifer Foster, Iakes Goenaga, Koldo Gojenola Galletebeitia, Yoav Goldberg, Spence Green, Nizar Habash, Marco Kuhlmann, Wolfgang Maier, Joakim Nivre, Adam Przepiórkowski, Ryan Roth, Wolfgang Seeker, Yannick Versley, Veronika Vincze, Marcin Wolinski, Alina Wróblewska, Éric Villemonte de la Clergerie
2013 J jnl
Proces. del Leng. Natural
Jose Maria Arriola, María Jesús Aranzabe, Iakes Goenaga
2012 conf
SPMRL@ACL 2012
Iakes Goenaga, Koldo Gojenola, María Jesús Aranzabe, Arantza Díaz de Ilarraza, Kepa Bengoetxea
2010 C conf
CIARP
Ana Zelaia Jauregi, Basilio Sierra, Olatz Arregi Uriarte, Klara Ceberio, Arantza Díaz de Ilarraza, Iakes Goenaga
2010 conf
IBERAMIA
Olatz Arregi, Klara Ceberio, Arantza Díaz de Ilarraza Sánchez, Iakes Goenaga, Basilio Sierra, Ana Zelaia
2010 J jnl
Proces. del Leng. Natural
Olatz Arregi, Klara Ceberio, Arantza Díaz de Ilarraza, Iakes Goenaga, Basilio Sierra, Ana Zelaia
redb/extractors/decompiler/bninja/analysis/cfg-old.py
← Index redb/extractors/decompiler/bninja/analysis/cfg-old.py python
from collections import deque
from enum import Enum

from binaryninja.enums import (
    BranchType,
    InstructionTextTokenType,
)

# Support both package and standalone imports
try:
    from ..utils.hashes import calculate_md5, calculate_sha256
except ImportError:
    # Fallback to absolute imports (for multiprocessing spawned processes)
    from redb.extractors.decompiler.bninja.utils.hashes import calculate_md5, calculate_sha256


class CFGAnalysis:
    def __init__(self, function):
        self.function = function

    def determine_block_type(self, block) -> str:
        """Determine the type of a basic block."""
        # Check if it's a thunk function (usually just a jump or call)
        if len(block.disassembly_text) <= 2 and any(
            "jmp" in line.tokens[0].text.lower() for line in block.disassembly_text
        ):
            return "THUNK"

        # Check if it contains only data (no valid instructions)
        if all(not line.tokens for line in block.disassembly_text):
            return "DATA"

        # Default to code
        return "CODE"

    def extract_cyclomatic_complexity(self):
        """
        Cyclomatic complexity (McCabe’s metric) measures the number of linearly independent paths
        through a function’s control flow graph (CFG).
        The standard formula is:

            M = E - N + 2

        where:
            - E = number of edges in the CFG
            - N = number of nodes (basic blocks)
            - 2 accounts for the entry and exit nodes of a single connected graph
        """
        if self.function is None:
            return 0

        # number of basic blocks
        num_blocks = len(self.function.basic_blocks)
        # number of edges in the graph
        num_edges = sum(
            len(basic_block.outgoing_edges)
            for basic_block in self.function.basic_blocks
        )
        return num_edges - num_blocks + 2

    def extract_function_cfg(self):
        """Extract information about a function CFG and return it as a dictionary."""

        function = self.function
        function_data = {
            "function_address": self.function.start,
            "blocks": [],
            "measures": {
                "cyclomatic_complexity": self.extract_cyclomatic_complexity(),
            },
        }

        if self.function is None:
            return function_data

        # Get the map of the depth associated to every block
        depths = self.get_map_depth()

        # Get the map of the positions associated to every block
        id_maps = self.get_block_id_map()

        # Extract block data with graph structure information
        for block in function.basic_blocks:
            # dominators per every block translated
            dominators = sorted(self.extract_dominators(block, id_maps))

            # post dominators
            post_dominators = sorted(self.extract_post_dominators(block, id_maps))

            # Build block instructions string
            block_instructions = "\n".join(str(line) for line in block.disassembly_text)

            # Determine block type
            block_type = self.determine_block_type(block)

            # Extract successors directly from basic block
            successor_blocks = [edge.target.start for edge in block.outgoing_edges]
            # We ensure a canonical order and we sort the edges
            successor_blocks.sort()

            # Extract predecessors directly from basic block
            predecessor_blocks = [edge.source.start for edge in block.incoming_edges]
            # We ensure a canonical order and we sort the edges
            predecessor_blocks.sort()

            # Determine branch type from outgoing edges
            branch_type = self.determine_branch_type(block)

            instructions_count = len(block.disassembly_text)

            # Create block record
            block_json = {
                "function_address": self.function.start,
                "block_start_address": block.start,
                "block_end_address": block.end,
                "block_size": block.end - block.start,
                "instructions_count": instructions_count,
                "block_instructions_hash": calculate_sha256(block_instructions),
                "predecessor_blocks": predecessor_blocks,
                "successor_blocks": successor_blocks,
                "depth": depths[block.start],
                "position": id_maps[block.start],
                "branch_type": branch_type,
                "block_type": block_type,
                "flags": self.extract_block_flags(block),
                "dominators": dominators,
                "post_dominators": post_dominators,
            }
            function_data["blocks"].append(block_json)

        return function_data

    def extract_dominators(self, bb, id_maps):
        """Extract the dominators normalized"""
        dom_idx = [id_maps[d.start] for d in bb.dominators]
        return dom_idx

    def extract_post_dominators(self, bb, id_maps):
        """Extract the post-dominators normalized"""
        post_dom_idx = [id_maps[d.start] for d in bb.post_dominators]
        return post_dom_idx

    def determine_branch_type(self, block):
        """
        Determine the type of branch at the end of a basic block.
        This combines edge type information with instruction analysis.
        """
        # If no outgoing edges, it might be a return or terminal block
        if not block.outgoing_edges:
            # Check if the last instruction is a return
            for line in reversed(list(block.disassembly_text)):
                if line.tokens and any(
                    token.text.lower() in ["ret", "retn"] for token in line.tokens
                ):
                    return "RETURN"
            return "UNKNOWN"

        # Collect branch types from all outgoing edges
        branch_types = []
        for edge in block.outgoing_edges:
            edge_type = edge.type
            # Map edge type to our branch type enum
            if isinstance(edge_type, str):
                if edge_type == "IndirectCall":
                    branch_types.append("CALL")
                else:
                    branch_types.append("UNKNOWN")
            else:
                # Use our mapping for integer/enum values
                type_mapping = {
                    BranchType.UnconditionalBranch: "DIRECT",
                    BranchType.FalseBranch: "CONDITIONAL",
                    BranchType.TrueBranch: "CONDITIONAL",
                    BranchType.CallDestination: "CALL",
                    BranchType.FunctionReturn: "RETURN",
                    BranchType.SystemCall: "CALL",
                    BranchType.IndirectBranch: "INDIRECT",
                    BranchType.ExceptionBranch: "UNKNOWN",
                    BranchType.UnresolvedBranch: "UNKNOWN",
                    BranchType.UserDefinedBranch: "UNKNOWN",
                }
                branch_types.append(type_mapping.get(edge_type, "UNKNOWN"))

        # Determine overall branch type (prioritize CALL > RETURN > CONDITIONAL > DIRECT)
        if "CALL" in branch_types:
            return "CALL"
        elif "RETURN" in branch_types:
            return "RETURN"
        elif "CONDITIONAL" in branch_types:
            return "CONDITIONAL"
        elif "DIRECT" in branch_types:
            return "DIRECT"
        elif len(block.outgoing_edges) == 1:
            return "FALLTHROUGH"

        # If edge analysis was inconclusive, fall back to instruction analysis
        last_instr = None
        for line in reversed(list(block.disassembly_text)):
            if line.tokens:
                last_instr = line
                break

        if last_instr:
            mnemonic = None
            for token in last_instr.tokens:
                if token.type == InstructionTextTokenType.InstructionToken:
                    mnemonic = token.text.lower()
                    break

            if mnemonic:
                if mnemonic == "call":
                    return "CALL"
                elif mnemonic == "jmp":
                    return "DIRECT"
                elif mnemonic.startswith("j") and mnemonic != "jmp":
                    return "CONDITIONAL"
                elif mnemonic in ["ret", "retn"]:
                    return "RETURN"

        return "UNKNOWN"

    def get_map_depth(self):
        """
        Run a BFS on the basic blocks of the function to assign a depth to every block
        """

        depths = {}
        entry = self.function.get_basic_block_at(self.function.start)

        ### Simple BFS
        q = deque()
        q.append(entry)
        depths[entry.start] = 0

        while q:
            b = q.popleft()
            b_depth = depths[b.start]
            for edge in b.outgoing_edges:
                tgt = edge.target

                if tgt is None:
                    continue

                if tgt.start not in depths:
                    depths[tgt.start] = b_depth + 1
                    q.append(tgt)

        return depths

    def get_block_id_map(self):
        """
        Assign a unique, sequential ID to each basic block of the function using a BFS starting from the entry block.
        """

        id_map = {}
        entry = self.function.get_basic_block_at(self.function.start)

        q = deque()
        q.append(entry)

        current_id = 0
        id_map[entry.start] = current_id

        while q:
            b = q.popleft()
            for edge in b.outgoing_edges:
                tgt = edge.target

                if tgt is None:
                    continue

                if tgt.start not in id_map:
                    current_id += 1
                    id_map[tgt.start] = current_id
                    q.append(tgt)

        return id_map

    def extract_block_flags(self, block):
        """
        Get the flags for every basic block. Currently, we implemented these heuristics:
            - if a basic block is the entry node for a function
            - if a basic block is the exit block for a function
            - if a basic block is part of a natural loop
        """
        flags = []

        if block.start == self.function.start:
            flags.append(BlockFlags.EntryBlock.value)

        if any(edge.type == BranchType.FunctionReturn for edge in block.outgoing_edges):
            flags.append(BlockFlags.ExitBlock.value)

        # if this block is in its dominance frontier, then it's part of a natural loop
        if block in block.dominance_frontier:
            flags.append(BlockFlags.LoopBlock.value)

        return flags


class BlockFlags(Enum):
    # generally, the basic block identifying the entry point of the function
    EntryBlock = "EntryBlock"
    # any basic blocks that makes the control flow exiting from the current function
    ExitBlock = "ExitBlock"
    # any block is in a natural loop if it is in its own dominance frontier
    LoopBlock = "LoopBlock"


class BlockType(Enum):
    THUNK = "THUNK"
    DATA = "DATA"
    PADDING = "PADDING"
    CODE = "CODE"