blob: cb29175b1b2521c7b72e9320bd198e7514bc70e5 [file] [log] [blame]
// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
// This is a very rough prototype of an utility that extracts syscall descriptions from header files.
// It needs to extract struct/union descriptions, better analyze types,
// analyze pointer directions (in, out), figure out len types (usually marked with sal).
// The easiest way to build it is to build it as part of clang. Add the following lines to CMakeLists.txt:
// +add_clang_executable(syz-declextract syz-declextract/syz-declextract.cpp)
// +target_link_libraries(syz-declextract clangTooling)
// It was used to extract windows descriptions:
// syz-declextract -extra-arg="--driver-mode=cl" -extra-arg="-I/path/to/windows/headers" Windows.h
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Driver/Options.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Tooling.h"
using namespace clang;
using namespace clang::tooling;
std::string convertType(ASTContext &C, QualType T) {
auto name = T.getAsString();
if (name == "HANDLE")
return name;
if (T->isIntegralOrEnumerationType()) {
int size = C.getTypeSize(T);
char buf[10];
sprintf(buf, "int%d", size);
return buf;
if (T->isVoidPointerType()) {
return "ptr[inout, array[int8]]";
if (T->isPointerType()) {
auto inner = convertType(C, T->getPointeeType());
if (inner == "")
return "ptr[inout, array[int8]]";
char buf[1024];
sprintf(buf, "ptr[inout, %s]", inner.c_str());
return buf;
return "intptr";
class DeclExtractCallVisitor : public RecursiveASTVisitor<DeclExtractCallVisitor> {
explicit DeclExtractCallVisitor(ASTContext *Context)
: Context(*Context) {}
bool VisitFunctionDecl(const FunctionDecl *D) {
if (D->doesThisDeclarationHaveABody())
return true;
// TODO(dvyukov): need to select only stdcall (WINAPI) functions.
// But the following 2 approaches do not work.
if (false) {
if (auto *FPT = D->getType()->getAs<FunctionProtoType>()) {
if (FPT->getExtInfo().getCC() != CC_X86StdCall)
return true;
if (false) {
if (!D->hasAttr<StdCallAttr>())
return true;
// Tons of functions are bulk ignored below because they cause
// static/dynamic link failures, reboot machine, etc.
auto fn = D->getNameInfo().getAsString();
if (fn.empty()) return true;
if (*fn.rbegin() == 'W') return true; // Unicode versions.
const char *ignore_prefixes[] {
for (auto prefix: ignore_prefixes) {
if (strncmp(fn.c_str(), prefix, strlen(prefix)) == 0) return true;
const char *ignore_functions[] {
for (auto func: ignore_functions) {
if (strstr(fn.c_str(), func)) return true;
// These are already described:
const char *ignore_exact[] {
for (auto func: ignore_exact) {
if (strcmp(fn.c_str(), func) == 0) return true;
const char *ignore_files[] {
auto src = D->getSourceRange().getBegin().printToString(Context.getSourceManager());
if (strstr(src.c_str(), "/um/") == 0) return true;
for (auto file: ignore_files) {
if (strstr(src.c_str(), file)) return true;
for (const ParmVarDecl *P : D->parameters()) {
auto typ = convertType(Context, P->getType());
if (typ == "") {
llvm::outs() << D->getNameInfo().getAsString() << ": UNKNOWN TYPE: " <<
QualType(P->getType()).getAsString() << "\n";
return true;
if (Generated[D->getNameInfo().getAsString()])
return true;
Generated[D->getNameInfo().getAsString()] = true;
llvm::outs() << D->getNameInfo().getAsString() << "(";
int i = 0;
for (const ParmVarDecl *P : D->parameters()) {
if (i)
llvm::outs() << ", ";
auto name = P->getNameAsString();
if (name == "") {
char buf[10];
sprintf(buf, "arg%d", i);
name = buf;
llvm::outs() << name << " " << convertType(Context, P->getType());
if (i == 9)
llvm::outs() << ")";
auto ret = convertType(Context, D->getReturnType());
if (ret == "HANDLE")
llvm::outs() << " " << ret;
llvm::outs() << "\n";
return true;
ASTContext &Context;
std::map<std::string, bool> Generated;
class DeclExtractCallConsumer : public clang::ASTConsumer {
explicit DeclExtractCallConsumer(ASTContext *Context)
: Visitor(Context) {}
virtual void HandleTranslationUnit(clang::ASTContext &Context) {
DeclExtractCallVisitor Visitor;
class DeclExtractCallAction : public clang::ASTFrontendAction {
DeclExtractCallAction() {}
virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
return std::unique_ptr<clang::ASTConsumer>(
new DeclExtractCallConsumer(&Compiler.getASTContext()));
static llvm::cl::OptionCategory MyToolCategory("my-tool options");
int main(int argc, const char **argv) {
CommonOptionsParser OptionsParser(argc, argv, MyToolCategory);
ClangTool Tool(OptionsParser.getCompilations(),