# Library Symbols — Feature Design Document ## 1. Visão Geral Sistema de importação e resolução de símbolos de bibliotecas externas (ex: Borland OWL, MFC, Qt, .NET BCL). O parser consome dumps de símbolos (formato TurboDump inicialmente), armazena-os como referência autoritativa, e cruza automaticamente com os resultados de análise estática para resolver imports opacos (ordinais) em nomes semânticos. A informação resolvida é injetada no pipeline de embeddings (pgvector) e na construção de contexto do chat RAG, melhorando dramaticamente a qualidade das respostas do LLM. **Fase atual (1):** Parser TurboDump + integração completa com AI/RAG. Sem sessões de chat temático (Fase 2 futura). ## 2. Arquitetura ``` ┌──────────────────────────────────────────────────────────────────┐ │ LIBRARY UPLOAD FLOW │ │ │ │ POST /libraries/upload │ │ │ {parserType, rawContent, libraryName, aliases[]} │ │ ▼ │ │ LibraryService.parseAndStore() │ │ ├─ LibrarySymbolParser (interface) │ │ │ └─ TurboDumpParser ← implementação atual │ │ │ └─ DefFileParser ← futuro │ │ │ └─ MapFileParser ← futuro │ │ ├─ Cria Library entity │ │ ├─ Cria LibraryAlias entities (mapeamento DLL ↔ library) │ │ ├─ Chama parser.parse() → List │ │ └─ Salva LibrarySymbol entities em lote │ └──────────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────┐ │ SYMBOL RESOLUTION FLOW │ │ │ │ StaticAnalysisCompletedEvent (disparado após IDA/Ghidra) │ │ ▼ │ │ SymbolResolutionService.resolveImports(analysisId) │ │ ├─ Carrega todos os StaticImport do analysis │ │ ├─ Para cada import com ordinal != 0: │ │ │ ├─ Busca LibraryAlias pelo campo dll do import │ │ │ ├─ Se encontrado: busca LibrarySymbol por (library, ordinal)│ │ │ └─ Se encontrado: atualiza StaticImport.resolvedName │ │ │ e StaticImport.librarySymbolId │ │ └─ Loga estatísticas: quantos imports resolvidos │ └──────────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────────┐ │ AI / RAG ENRICHMENT │ │ │ │ EmbeddingChunkBuilder │ │ ├─ buildImportChunks(): inclui resolvedName + className │ │ ├─ buildMetadataChunk(): inclui Library Diagnostics section │ │ └─ buildFunctionChunkContent(): callees resolvidos via OWL │ │ │ │ ChatContextBuilder │ │ ├─ buildSystemPrompt(): inclui [LIBRARY CONTEXT] seção │ │ └─ detectFunctionReferences(): busca por classes/métodos OWL │ └──────────────────────────────────────────────────────────────────┘ ``` ## 3. Modelo de Dados ### 3.1 Tabelas Novas #### `library` | Coluna | Tipo | Descrição | |--------|------|-----------| | `id` | UUID PK | Identificador | | `name` | VARCHAR(255) NOT NULL | Nome da biblioteca (ex: "Borland OWL 2.52") | | `description` | TEXT | Descrição opcional (ex: "Object Windows Library for Borland C++") | | `created_at` | TIMESTAMPTZ NOT NULL | Data de criação | #### `library_alias` | Coluna | Tipo | Descrição | |--------|------|-----------| | `id` | UUID PK | Identificador | | `library_id` | UUID FK → library(id) ON DELETE CASCADE | Biblioteca pai | | `alias` | VARCHAR(255) NOT NULL | Nome do DLL/arquivo no import table (ex: "OWLWI", "owl252.dll") | | `UNIQUE(library_id, alias)` | | Um alias pertence a uma única biblioteca | #### `library_symbol` | Coluna | Tipo | Descrição | |--------|------|-----------| | `id` | UUID PK | Identificador | | `library_id` | UUID FK → library(id) ON DELETE CASCADE | Biblioteca pai | | `module_name` | VARCHAR(255) NOT NULL | Módulo original (ex: "Owl252" do TurboDump) | | `class_name` | VARCHAR(500) | Classe C++ (nulo para funções livres) | | `symbol_name` | VARCHAR(500) NOT NULL | Nome completo do símbolo (ex: "TButtonGadget::Paint(TDC*)") | | `ordinal` | INTEGER NOT NULL | Número ordinal de importação | | `symbol_type` | VARCHAR(50) | Tipo: CONSTRUCTOR, DESTRUCTOR, METHOD, FUNCTION, STATIC_MEMBER | | `parameters` | VARCHAR(1000) | Assinatura dos parâmetros (extraído do nome) | | `category` | VARCHAR(100) | Categoria (ex: "UI/Gadget", "Core/Application", "Graphics/Bitmap") | | `created_at` | TIMESTAMPTZ NOT NULL | Data de criação | Índices: `idx_library_symbol_library_ordinal ON library_symbol (library_id, ordinal)` `idx_library_symbol_class ON library_symbol (class_name) WHERE class_name IS NOT NULL` ### 3.2 Tabela Alterada #### `static_import` (adições) | Coluna | Tipo | Descrição | |--------|------|-----------| | `library_symbol_id` | UUID FK → library_symbol(id) ON SET NULL | Símbolo de biblioteca que corresponde a este import | | `resolved_name` | VARCHAR(500) | Nome resolvido (ex: "TButtonGadget::Paint(TDC*)") | Índice: `idx_static_import_library_symbol ON static_import (library_symbol_id) WHERE library_symbol_id IS NOT NULL` ## 4. Fluxo de Dados Detalhado ### 4.1 Upload da Biblioteca ``` POST /libraries/upload Content-Type: application/json { "name": "Borland OWL 2.52", "description": "Object Windows Library for Borland C++ 5.0", "parserType": "TURBO_DUMP", "aliases": ["OWLWI", "owl252.dll", "Owl252"], "rawContent": "Turbo Dump Version 5.0.1..." } ``` 1. `LibraryService.parseAndStore()` recebe o request 2. Busca um `LibrarySymbolParser` pelo `parserType` (pode ser um Map ou via qualifier annotation) 3. Cria `Library` entity e salva 4. Cria `LibraryAlias` entities para cada alias 5. Chama `parser.parse(rawContent, libraryId)` → `List` 6. Salva `LibrarySymbol` entities em batch (500 por vez) 7. Retorna `LibraryResponse` com contagem de símbolos ### 4.2 Resolução de Import Symbols Após a análise IDA/Ghidra completar, o `StaticAnalysisCompletedEvent` é publicado. O listener `SymbolResolutionListener` (ou `SymbolResolutionService`) escuta o evento: 1. Carrega `StaticImport` para o `analysisId` 2. Para cada import com `ordinal != 0`: a. Busca `LibraryAlias` onde `alias ILIKE dll` (case-insensitive) b. Se encontrado, busca `LibrarySymbol` por `(library_id, ordinal)` c. Se encontrado, atualiza `StaticImport.librarySymbolId` e `resolvedName` 3. Loga: `Resolved 152/200 imports (76%) for analysis ` 4. Opcional: publica um evento `SymbolsResolvedEvent` para notificar downstream Alternativamente, se o listener não for viável (timing), o `StaticAnalysisHandler` chama `symbolResolutionService.resolveImports(analysisId)` no final do `process()`. ### 4.3 Enriquecimento de Embedding Chunks #### Import Chunks ```java // Antes "[IMPORT] OWLWI.ord_75 @ 0x00405A00" // Depois (com library symbol resolvido) "[IMPORT] OWLWI.TButtonGadget::Paint(TDC*) @ 0x00405A00 " + " Library: Borland OWL 2.52 | Class: TButtonGadget | Method: Paint" ``` #### Metadata Chunk — Nova seção [LIBRARY DIAGNOSTICS] ``` [LIBRARY DIAGNOSTICS] Framework: Borland OWL 2.52 (Object Windows Library) Detected classes (15): TApplication, TFrameWindow, TButtonGadget, TBitmap, TControlBar, TChooseColorDialog, TChooseFontDialog, TClipboard, TBrush, TDC, TCursor, TBitSet, TCharSet, TCommonDialog, TControlGadget Category distribution: UI/Gadget (6), Core/Application (3), Graphics (4), UI/Window (2) Architecture: Event-driven GUI, message dispatch via TEventHandler::Find() ``` #### Function Chunks No `buildFunctionChunkContent()`, ao listar callees, se o callee for um import com `resolvedName`: ```java // Antes "Callees: sub_401000, ordinal_75, ordinal_67" // Depois "Callees: sub_401000, TButtonGadget::Paint [OWL/ord75], TButtonGadget::TButtonGadget [OWL/ord67]" ``` ### 4.4 Enriquecimento do System Prompt ```java // Antes String.format(""" [BINARY OVERVIEW] Filename: %s Format: %s Architecture: %s Compiler: %s """, ...); // Depois String.format(""" [BINARY OVERVIEW] Filename: %s Format: %s Architecture: %s Compiler: Borland C++ [LIBRARY CONTEXT] This binary links against Borland OWL 2.52 (Object Windows Library), a C++ framework for Windows GUI applications. Key classes detected: %s OWL uses a message-driven architecture similar to MFC. Windows messages are routed through TEventHandler response tables. Common patterns: - TFrameWindow is the main application window - TButtonGadget handles button controls with Paint() for WM_PAINT - Dialogs extend TCommonDialog via TChooseColorDialog/TChooseFontDialog - Graphics use TDC (Device Context wrapper) with TBrush/TPen/TBitmap When analyzing functions that call OWL imports, consider the OWL class hierarchy and event dispatch model to infer the function's role. """, ..., libraryDiagnostics); ``` ## 5. API REST ### 5.1 Endpoints | Método | Path | Descrição | |--------|------|-----------| | `POST` | `/libraries/upload` | Upload de um dump de símbolos de biblioteca | | `GET` | `/libraries` | Listar todas as bibliotecas carregadas | | `GET` | `/libraries/{id}` | Detalhes de uma biblioteca | | `DELETE` | `/libraries/{id}` | Remover uma biblioteca e seus símbolos | | `GET` | `/libraries/{id}/symbols` | Listar símbolos (com paginação, filtro por nome/ordinal) | | `POST` | `/analysis/{analysisId}/resolve-symbols` | Forçar resolução de símbolos (debug) | ### 5.2 DTOs ```java // Request public record LibraryUploadRequest( @NotBlank String name, String description, @NotBlank String parserType, @NotEmpty List aliases, @NotBlank String rawContent ) {} // Response public record LibraryResponse( UUID id, String name, String description, int symbolCount, int aliasCount, Instant createdAt ) { public static LibraryResponse from(Library lib) { ... } } public record LibrarySymbolResponse( UUID id, String moduleName, String className, String symbolName, int ordinal, String symbolType, String parameters, String category ) { public static LibrarySymbolResponse from(LibrarySymbol sym) { ... } } ``` ### 5.3 ImportResponse (estendido) ```java public record ImportResponse( UUID id, String dll, String name, String address, int ordinal, // Novos campos opcionais: String resolvedName, // nulo se não resolvido String libraryName, // nulo se não resolvido String className, // nulo se não for método de classe UUID librarySymbolId // nulo se não resolvido ) { public static ImportResponse from(StaticImport imp) { var sym = imp.getLibrarySymbol(); return new ImportResponse( imp.getId(), imp.getDll(), imp.getName(), imp.getAddress(), imp.getOrdinal(), imp.getResolvedName(), Objects.nonNull(sym) ? sym.getLibrary().getName() : null, Objects.nonNull(sym) ? sym.getClassName() : null, imp.getLibrarySymbolId() ); } } ``` ## 6. Parser Architecture ### 6.1 Interface ```java package ai.decompile.analysis.service.parser; import java.util.List; public interface LibrarySymbolParser { /** Identificador único do formato (ex: "TURBO_DUMP", "DEF_FILE") */ String getFormatName(); /** Valida se o conteúdo é compatível com este parser */ boolean canParse(String rawContent); /** Parseia o conteúdo e retorna símbolos para association com libraryId */ List parse(String rawContent); } /** Registro intermediário entre parser e serviço */ public record ParsedSymbol( String moduleName, String className, String symbolName, int ordinal, String symbolType, String parameters, String category ) {} ``` ### 6.2 TurboDumpParser Implementação concreta para o formato do utilitário `TDUMP.EXE` da Borland: **Formato de entrada:** ``` 000020 THEADR TAppDictionary::TAppDictionary() 00003E COMENT Purge: Yes, List: Yes, Class: 160 (0A0h) Dynamic link import (IMPDEF) Imported by: ordinal Internal Name: TAppDictionary::TAppDictionary() Module Name: Owl252 ordinal: 2 000069 MODEND ``` **Lógica de parsing:** 1. Divide o texto por blocos delimitados por `MODEND` 2. Para cada bloco: - Extrai `symbolName` da linha `THEADR` - Extrai `moduleName` do campo `Module Name:` - Extrai `ordinal` do campo `ordinal:` - Se não encontrar `ordinal:` ou o bloco não for `IMPDEF`, pula - Classifica `symbolType`: - Contém `::~` → `DESTRUCTOR` - Contém `::` e termina em `()` sem `~` → `CONSTRUCTOR` - Contém `::` → `METHOD` - Sem `::` → `FUNCTION` - Extrai `className` do trecho antes de `::` - Extrai `parameters` dos parênteses após o nome do método - Classifica `category` por prefixo de classe: ``` TApp, TModule, TStatus, TXCompatibility → Core/Application TFrame, TWindow, TDialog, TClipboard → UI/Window TButton, TGadget, TControl, TSlider → UI/Control TBitmap, TPalette, TDib, TIcon, TDC → Graphics TBrush, TPen, TFont → Graphics/Drawing TColor → Graphics/Color TCursor → UI/Cursor TBitSet, TCharSet → Core/DataTypes default → Core/Other ``` ### 6.3 Registro de Parsers ```java @Component @RequiredArgsConstructor public class LibrarySymbolParserRegistry { // Spring injeta todos os beans LibrarySymbolParser private final Map parsers; // Construtor monta o mapa formatName → parser public LibrarySymbolParserRegistry(List parserList) { this.parsers = parserList.stream() .collect(Collectors.toMap( LibrarySymbolParser::getFormatName, Function.identity())); } public LibrarySymbolParser get(String formatName) { var parser = parsers.get(formatName); if (Objects.isNull(parser)) { throw new IllegalArgumentException( "No parser found for format '" + formatName + "'. " + "Available: " + parsers.keySet()); } return parser; } public List getAvailableFormats() { return List.copyOf(parsers.keySet()); } } ``` ## 7. Estrutura de Código ``` src/main/java/ai/decompile/analysis/ ├── model/ │ ├── entity/ │ │ ├── Library.java (novo) │ │ ├── LibraryAlias.java (novo) │ │ ├── LibrarySymbol.java (novo) │ │ └── StaticImport.java (modificado: +resolvedName, +librarySymbolId, +librarySymbol) │ ├── repository/ │ │ ├── LibraryRepository.java (novo) │ │ ├── LibraryAliasRepository.java (novo) │ │ └── LibrarySymbolRepository.java (novo) │ └── dto/ │ ├── LibraryResponse.java (novo) │ ├── LibrarySymbolResponse.java (novo) │ ├── LibraryUploadRequest.java (novo) │ └── ImportResponse.java (modificado) ├── service/ │ ├── parser/ │ │ ├── LibrarySymbolParser.java (novo - interface) │ │ ├── ParsedSymbol.java (novo - record) │ │ ├── LibrarySymbolParserRegistry.java (novo) │ │ └── TurboDumpParser.java (novo) │ ├── LibraryService.java (novo) │ ├── SymbolResolutionService.java (novo) │ └── AnalysisService.java (modificado: chama resolution após saveResult) ├── event/ │ └── SymbolsResolvedEvent.java (novo, opcional) └── controller/ └── LibraryController.java (novo) src/main/resources/db/migration/ ├── V021__create_library_tables.sql (novo) └── V022__alter_static_import_library.sql (novo) src/main/java/ai/decompile/ai/service/ ├── EmbeddingChunkBuilder.java (modificado: enriquecido com library info) └── ChatContextBuilder.java (modificado: system prompt + context enriquecido) ``` ## 8. Casos de Teste ### 8.1 TurboDumpParser | Teste | Entrada | Esperado | |-------|---------|----------| | Construtor de classe | `TAppDictionary::TAppDictionary()` | type=CONSTRUCTOR, class=TAppDictionary | | Destrutor | `TBitmap::~TBitmap()` | type=DESTRUCTOR, class=TBitmap | | Método com parâmetros | `TDC::SelectObject(const TPen*)` | type=METHOD, class=TDC, params=const TPen* | | Método sem parâmetros | `TBitSet::IsEmpty()` | type=METHOD, params=null | | Função livre | `NColors(unsigned short)` | type=FUNCTION, className=null | | Bloco sem ordinal | Qualquer não-IMPDEF | Ignorado (não retorna símbolo) | | Parser chamado 2x | Conteúdo com 1854 IMPDEF | 1854 símbolos retornados | ### 8.2 SymbolResolutionService | Teste | Setup | Esperado | |-------|-------|----------| | Match por ordinal + alias | Import(dll="OWLWI", ordinal=75), LibrarySymbol(ordinal=75, library=L1), Alias("OWLWI"→L1) | resolvedName = "TButtonGadget::Paint(TDC*)" | | Ordinal zero (nomeado) | Import(ordinal=0) | Não resolvido (pula) | | Alias não encontrado | Import(dll="UNKNOWN", ordinal=5) | Não resolvido | | Ordinal não encontrado | Import(dll="OWLWI", ordinal=9999) | Não resolvido | | Sem bibliotecas carregadas | Nenhuma Library no banco | Nenhum import resolvido | | Case insensitive alias | Import(dll="owlwi"), Alias("OWLWI") | Resolvido | ### 8.3 EmbeddingChunkBuilder (enriquecido) | Teste | Setup | Esperado | |-------|-------|----------| | Import com resolvedName | StaticImport com resolvedName="TDC::Paint(TDC*)" | Chunk content contém o resolvedName | | Metadata com library diagnostics | Library + LibrarySymbol carregados | Seção [LIBRARY DIAGNOSTICS] presente | | Function callees resolvidos | Function com xrefs para imports resolvidos | Callee list mostra class::method [OWL] | ## 9. Rollout e Rollback - **Nova feature, sem quebra de compatibilidade**: migration adiciona colunas nullable ao `static_import` - **Sem bibliotecas carregadas**: comportamento idêntico ao atual (resolvedName = null, nenhum chunk afetado) - **Rollback**: reverter migrations V021 e V022, remover código novo (as APIs novas simplesmente não existiriam mais)