llvm-tooling について
llvm-tooling は、LLVM と Clang の基盤でツールを開発する人向けのリファレンススキルです。ソース解析、デバッグ、リファクタリング、言語サーバー連携を例とともに扱います。
主な機能
- 01Clang プラグインの開発
- 02LibTooling と AST Matchers
- 03LLDB の拡張
- 04Clangd と LSP の拡張
- 05ソース間変換
活用シーン
- 01Clang ベースのソース解析ツールの作成
- 02カスタム LLDB コマンドの追加
- 03clangd 向けコードアクションの実装
- 04C++ リファクタリングの自動化
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ gmh5225/ awesome-llvm-security --skill llvm-toolingインストール後、Claude Code で/llvm-toolingを入力して読み込まれたか確認します。
詳細
LLVM ツール開発スキル
このスキルでは、ソースコードの解析、デバッグ、IDE 統合のために LLVM/Clang インフラストラクチャを使用するツールの開発について説明します。
Clang プラグインの開発
プラグインのアーキテクチャ
Clang プラグインは、コンパイル中に Clang の機能を拡張する動的にロードされるライブラリです。
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
class MyVisitor : public clang::RecursiveASTVisitor<MyVisitor> {
public:
bool VisitFunctionDecl(clang::FunctionDecl *FD) {
llvm::outs() << "Found function: " << FD->getName() << "\n";
return true;
}
};
class MyConsumer : public clang::ASTConsumer {
MyVisitor Visitor;
public:
void HandleTranslationUnit(clang::ASTContext &Context) override {
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
}
};
class MyPlugin : public clang::PluginASTAction {
protected:
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance &CI, llvm::StringRef) override {
return std::make_unique<MyConsumer>();
}
bool ParseArgs(const clang::CompilerInstance &CI,
const std::vector<std::string> &args) override {
return true;
}
};
static clang::FrontendPluginRegistry::Add<MyPlugin>
X("my-plugin", "My custom plugin description");
Clang プラグインの実行
# Build plugin
clang++ -shared -fPIC -o MyPlugin.so MyPlugin.cpp \
$(llvm-config --cxxflags --ldflags)
# Run plugin
clang -Xclang -load -Xclang ./MyPlugin.so \
-Xclang -plugin -Xclang my-plugin \
source.cpp
LibTooling
スタンドアロンツール
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::tooling;
using namespace clang::ast_matchers;
// Define matcher
auto functionMatcher = functionDecl(hasName("targetFunction")).bind("func");
// Callback handler
class FunctionCallback : public MatchFinder::MatchCallback {
public:
void run(const MatchFinder::MatchResult &Result) override {
if (auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func")) {
llvm::outs() << "Found: " << FD->getQualifiedNameAsString() << "\n";
}
}
};
int main(int argc, const char **argv) {
auto ExpectedParser = CommonOptionsParser::create(argc, argv, MyCategory);
ClangTool Tool(ExpectedParser->getCompilations(),
ExpectedParser->getSourcePathList());
FunctionCallback Callback;
MatchFinder Finder;
Finder.addMatcher(functionMatcher, &Callback);
return Tool.run(newFrontendActionFactory(&Finder).get());
}
AST Matchers リファレンス
// Declaration matchers
functionDecl() // Match function declarations
varDecl() // Match variable declarations
recordDecl() // Match struct/class/union
fieldDecl() // Match class fields
cxxMethodDecl() // Match C++ methods
cxxConstructorDecl() // Match constructors
// Expression matchers
callExpr() // Match function calls
binaryOperator() // Match binary operators
unaryOperator() // Match unary operators
memberExpr() // Match member access
// Narrowing matchers
hasName("name") // Filter by name
hasType(asString("int")) // Filter by type
isPublic() // Filter by access specifier
hasAnyParameter(...) // Match parameters
// Traversal matchers
hasDescendant(...) // Match anywhere in subtree
hasAncestor(...) // Match in parent chain
has(...) // Match direct children
forEach(...) // Match all children
LLDB 拡張機能の開発
Python コマンド
import lldb
def my_command(debugger, command, result, internal_dict):
"""Custom LLDB command implementation"""
target = debugger.GetSelectedTarget()
process = target.GetProcess()
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
# Get variable value
var = frame.FindVariable("my_var")
result.AppendMessage(f"my_var = {var.GetValue()}")
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand(
'command script add -f my_module.my_command my_cmd')
スクリプト化されたプロセス
class MyScriptedProcess(lldb.SBScriptedProcess):
def __init__(self, target, args):
super().__init__(target, args)
def get_thread_with_id(self, tid):
# Return thread info
pass
def get_registers_for_thread(self, tid):
# Return register context
pass
def read_memory_at_address(self, addr, size):
# Read memory
pass
LLDB GUI 拡張機能
- visual-lldb:LLDB 用 GUI フロントエンド
- vegvisir:ブラウザー上で動作する LLDB GUI
- lldbg:軽量なネイティブ GUI
- CodeLLDB:VSCode デバッガー拡張機能
Clangd/Language Server Protocol
カスタム LSP 拡張機能
// Implementing custom code actions
class MyCodeActionProvider {
public:
std::vector<CodeAction> getCodeActions(
const TextDocumentIdentifier &File,
const Range &Range,
const CodeActionContext &Context) {
std::vector<CodeAction> Actions;
// Add custom refactoring action
CodeAction Action;
Action.title = "Extract to function";
Action.kind = "refactor.extract";
// Define workspace edits
WorkspaceEdit Edit;
// ... populate edits
Action.edit = Edit;
Actions.push_back(Action);
return Actions;
}
};
Clangd の設定
# .clangd configuration file
CompileFlags:
Add: [-xc++, -std=c++17]
Remove: [-W*]
Diagnostics:
ClangTidy:
Add: [modernize-*, performance-*]
Remove: [modernize-use-trailing-return-type]
Index:
Background: Build
ソース間変換
Clang Rewriter
#include "clang/Rewrite/Core/Rewriter.h"
class MyRewriter : public RecursiveASTVisitor<MyRewriter> {
Rewriter &R;
public:
MyRewriter(Rewriter &R) : R(R) {}
bool VisitFunctionDecl(FunctionDecl *FD) {
// Add comment before function
R.InsertTextBefore(FD->getBeginLoc(), "// Auto-generated\n");
// Replace function name
R.ReplaceText(FD->getLocation(),
FD->getName().size(), "new_name");
return true;
}
};
RefactoringTool
#include "clang/Tooling/Refactoring.h"
class MyRefactoring : public RefactoringCallback {
public:
void run(const MatchFinder::MatchResult &Result) override {
// Generate replacements
Replacement Rep(
*Result.SourceManager,
CharSourceRange::getTokenRange(Range),
"new_text");
Replacements.insert(Rep);
}
};
LLVM Tooling で構築された注目のツール
解析ツール
- cppinsights:C++ テンプレートのインスタンス化を可視化
- ClangBuildAnalyzer:ビルド時間の解析
- clazy:Qt に特化した静的解析
リファクタリングツール
- clang-rename:シンボルの名前変更
- clang-tidy:リンティングと自動修正
- include-what-you-use:インクルードの最適化
コード生成
- classgen:IDA 用の型情報を抽出
- constexpr-everything:constexpr を自動適用
- clang-expand:関数のインライン展開
ベストプラクティス
- AST Matchers を使用する:手動での走査よりも読みやすくなります
- 書式を維持する:スタイルを保つため、Rewriter は慎重に使用します
- マクロを処理する:マクロ展開位置に注意します
- 十分にテストする:C++ には多くのエッジケースがあります
- わかりやすい診断を提供する:明確なエラーメッセージにより、使いやすさが向上します
リソース
ツールの包括的な一覧については、README.md の Clang Plugins、Clangd/Language Server、LLDB の各セクションを参照してください。
詳細情報の取得
詳細かつ最新のリソースリンク、ツール一覧、プロジェクトの参照情報が必要な場合は、以下から最新のデータを取得してください。
https://raw.githubusercontent.com/gmh5225/awesome-llvm-security/refs/heads/main/README.md
この README には、以下の内容を網羅した厳選リストが含まれています。
- Clang プラグインと拡張機能(Clang Plugins セクション)
- 言語サーバーの実装(Clangd/Language Server セクション)
- LLDB デバッガーツールと GUI(LLDB セクション)
- 静的解析およびリファクタリングツール
コメント
Markdown 対応