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  }