github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/cmd/sigs2stub/main.cpp (about) 1 #include <fstream> 2 #include <iostream> 3 #include <sstream> 4 #include <stdint.h> 5 6 #include "clang/Tooling/Tooling.h" 7 #include "clang/AST/AST.h" 8 9 // read_file reads and returns the contents of the given file. The returned 10 // boolean indicates success. 11 std::tuple<std::string, bool> read_file(char *path) { 12 std::ifstream ifs(path); 13 if (!ifs) { 14 return std::tuple<std::string, bool>("", false); 15 } 16 std::string content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); 17 return std::tuple<std::string, bool>(content, true); 18 } 19 20 #define SECTION_ATTR_PREFIX ".text.0x" 21 22 void dump_func(const clang::FunctionDecl *f) { 23 const clang::FunctionType *f_type = f->getFunctionType(); 24 //f_type->dump(); 25 clang::CallingConv cc = f_type->getCallConv(); 26 //std::cout << "calling convention: " << cc << std::endl; 27 clang::SectionAttr *attr = f->getAttr<clang::SectionAttr>(); 28 if (!attr) { 29 // unable to locate function address. 30 return; 31 } 32 std::string section_attr = attr->getName().str(); 33 if (section_attr.find(SECTION_ATTR_PREFIX) != 0) { 34 // unable to locate section prefix. 35 return; 36 } 37 std::string addr_str = section_attr.substr(strlen(SECTION_ATTR_PREFIX)); 38 uint32_t addr = 0; 39 std::stringstream ss; 40 ss << std::hex << addr_str; 41 ss >> addr; 42 if (ss.fail() || !ss.eof()) { 43 std::cerr << "unable to parse hexadecimal value '" << addr_str << "'" << std::endl; 44 return; 45 } 46 47 //printf("times (0x%06X - _text_vstart) - ($ - $$) db 0xCC\n", addr); 48 printf("; address: 0x%06X\n", addr); 49 printf("%s:\n", f->getName().str().c_str()); 50 printf(" push ebp\n"); 51 printf(" mov ebp, esp\n"); 52 llvm::ArrayRef<clang::ParmVarDecl *> params = f->parameters(); 53 int nparams = f->getNumParams(); 54 int param_stack_bytes = 0; 55 switch (cc) { 56 case clang::CC_X86FastCall: 57 // The first two arguments are passed in ecx and edx. 58 for (int arg_num = nparams; arg_num > 0; arg_num--) { 59 // TODO: handle param type; param->getType(); 60 switch (arg_num) { 61 // The first two arguments are passed in ecx and edx. 62 case 1: 63 printf(" push ecx ; arg_%d (%s)\n", arg_num, params[arg_num-1]->getName().str().c_str()); 64 break; 65 case 2: 66 printf(" push edx ; arg_%d (%s)\n", arg_num, params[arg_num-1]->getName().str().c_str()); 67 break; 68 default: 69 printf(" push DWORD [ebp + %d] ; arg_%d (%s)\n", (arg_num-1)*4, arg_num, params[arg_num-1]->getName().str().c_str()); 70 param_stack_bytes += 4; 71 break; 72 } 73 } 74 break; 75 default: 76 for (int arg_num = nparams; arg_num > 0; arg_num--) { 77 printf(" push DWORD [ebp + %d] ; arg_%d (%s)\n", (arg_num-1)*4, arg_num, params[arg_num-1]->getName().str().c_str()); 78 param_stack_bytes += 4; 79 } 80 break; 81 } 82 printf(" call [ia_%s]\n", f->getName().str().c_str()); 83 printf(" mov esp, ebp\n"); 84 printf(" push ebp\n"); 85 printf(" ret %d\n", param_stack_bytes); 86 printf("\n"); 87 } 88 89 // visit_decl vists the declaration of the AST. 90 bool visit_decl(void *ctx, const clang::Decl *decl) { 91 //decl->dump(); 92 const char *kind = decl->getDeclKindName(); 93 //std::cout << "kind: " << kind << std::endl; 94 if (strcmp(kind, "Function") == 0) { 95 dump_func(decl->getAsFunction()); 96 } 97 return true; 98 } 99 100 int main(int argc, char **argv) { 101 // Parse command line arguments. 102 if (argc < 2) { 103 std::cerr << "Usage: sigs2stubs [OPTION]... FILE.cpp" << std::endl; 104 return -1; 105 } 106 char *path = argv[1]; 107 108 // Read source file. 109 std::tuple<std::string, bool> t = read_file(path); 110 bool ok = std::get<1>(t); 111 if (!ok) { 112 std::cerr << "unable to parse file '" << path << "'" << std::endl; 113 return -1; 114 } 115 std::string input = std::get<0>(t); 116 117 // Parse source file. 118 std::vector<std::string> clang_args = std::vector<std::string>(); 119 // pass -m32 to clang (needed to recognize __fastcall). 120 clang_args.push_back("-m32"); 121 std::unique_ptr<clang::ASTUnit> au = clang::tooling::buildASTFromCodeWithArgs(input, clang_args, path); 122 if (!au->visitLocalTopLevelDecls(nullptr, visit_decl)) { 123 std::cerr << "visitLocalTopLevelDecls failed" << std::endl; 124 return -1; 125 } 126 }