zoukankan      html  css  js  c++  java
  • 函数指针与指针函数,直接向内存区域写入操作码并转成函数指针并调用

    函数指针与指针函数

    阅读hotspot的代码,需要先弄清楚这两个很容易混淆的概念。
    最近我想到一个办法去区分折扣两个,就是在中间加个“的”:

    • 函数的指针
    • 指针的函数

    这样就很容易区分了。

    • 函数指针,实际上是指针,是指向函数的指针
    • 指针函数,实际上函数,是指返回指针的函数

    其实在C语言中,指针数组和数组指针也是一样的区分办法。 加个“的”便可以解决。
    为什么会有这样的问题?老外怎么理解这个事情?我得回去翻翻K&R的那本C的英文教材。TODO。

    指针函数

    是指返回指针的函数,比如:

    int *fun (int a, int b) {
        int c = a + b;
        return &c; // 返回一个指针
    }
    

    函数指针

    是指指向函数的指针

    #include <stdio.h>
    
    int (* fun)(int a, int b); // fun 就是指向函数的指针变量 
    
    int add (int a, int b) {
        int c = a + b;
        return c;
    }
    
    int main(){
        fun = add;
        int result = fun(100, 200);
        printf("result=%d
    ", result);
    }
    

    函数指针定义成类型

    还可以结合类型声明将函数指针定义成类型

    #include <stdio.h>
    
    // 定义一个 返回值是int 参数是两个int的函数指针
    typedef int (* FunRefTypeTwoIntArgs)(int a, int b); // 将上面的函数指针变量 演化为类型 FunRefTypeTwoIntArgs 就是个类型 这样就好用多了
    
    FunRefTypeTwoIntArgs addFun;
    FunRefTypeTwoIntArgs subFun;
    
    int add (int a, int b) {
        int c = a + b;
        return c;
    }
    
    int sub (int a, int b) {
        int c = a - b;
        return c;
    }
    
    int main(){
        addFun = add;
        int result = addFun(100, 200);
        printf("result=%d
    ", result);
        
        subFun = add;
        int result = subFun(500, 100); // 也可以写成  result = (*subFun)(500, 100);
        printf("result=%d
    ", result);
    }
    

    这就有点类似java中接口的样子了。

    指针函数返回函数指针类型,结合hotspot中实际例子

    先是类型定义结合函数指针定义一个CallStub类型,该类型是 一个 指向函数的指针

    // share/vm/runtime/stubRoutines.hpp
      // Calls to Java SimonNote: 函数指针结合typedef类型定义  最终调用是在 javaCalls中的call_helper()
      typedef void (*CallStub)(
        address   link,
        intptr_t* result,
        BasicType result_type,
        Method* method,
        address   entry_point,
        intptr_t* parameters,
        int       size_of_parameters,
        TRAPS
      );
    

    再定义一个返回函数指针的函数,拆开讲:本质是一个函数,是一个返回函数指针的函数,是一个返回 指向函数的指针的 函数,此处还涉及将内存地址直接转成函数指针(指向函数的指针)

    // share/vm/runtime/stubRoutines.hpp
      // Calls to Java SimonNote: 将内存地址 转换成函数指针 CAST_TO_FN_PTR   call_stub 就是一个××返回函数指针×× 的 指针函数  ((CallStub)(castable_address(_call_stub_entry)))
      static CallStub call_stub()                              { return CAST_TO_FN_PTR(CallStub, _call_stub_entry); }
    

    调用上面定义的

    // share/vm/runtime/javaCalls.cpp
    // 发起调用的地方
          StubRoutines::call_stub()(
            (address)&link,
            // (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
            result_val_address,          // see NOTE above (compiler problem)
            result_type,
            method(),
            entry_point,
            args->parameters(),
            args->size_of_parameters(),
            CHECK
          );
    

    直接在内存中写入机器码转成函数指针并调用的demo

    下面这段 代码 还有遗留问题 还是没能跑成功,至于原因,我目前还没有答案。

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/mman.h>
    
    typedef int (* addFun)(int a, int b);
    
    int addWrap(int a, int b) {
    	int c = add(a, b);
    	return c;
    }
    
    int add(int a, int b) {
    	return a + b;
    }
    
    int main() {
    
    	int codeSize = sizeof(char) * 42;
    	char* pCode = (char*)malloc(codeSize);
    	char* pGen = pCode;
    	mprotect(pCode, codeSize, PROT_READ | PROT_WRITE | PROT_EXEC); // 加了这一行也是core。
    
    	*pGen++ = 0x55;  // 0x00000000004004fd <+0>:	55	push   %rbp
    	*pGen++ = 0x48; *pGen++ = 0x89; *pGen++ = 0xe5; // 0x00000000004004fe <+1>:	48 89 e5	mov    %rsp,%rbp
    	*pGen++ = 0x48; *pGen++ = 0x83; *pGen++ = 0xec; *pGen++ = 0x20; // 0x0000000000400501 <+4>:	48 83 ec 20	sub    $0x20,%rsp
    	*pGen++ = 0x89; *pGen++ = 0x7d; *pGen++ = 0xec; // 0x0000000000400505 <+8>:	89 7d ec	mov    %edi,-0x14(%rbp)
    	*pGen++ = 0x89; *pGen++ = 0x75; *pGen++ = 0xe8; // 0x0000000000400508 <+11>:	89 75 e8	mov    %esi,-0x18(%rbp)
    	*pGen++ = 0x8b; *pGen++ = 0x55; *pGen++ = 0xe8; //	   0x000000000040050b <+14>:	8b 55 e8	mov    -0x18(%rbp),%edx
    	*pGen++ = 0x8b; *pGen++ = 0x45; *pGen++ = 0xec; //	   0x000000000040050e <+17>:	8b 45 ec	mov    -0x14(%rbp),%eax
    	*pGen++ = 0x89; *pGen++ = 0xd6; //	   0x0000000000400511 <+20>:	89 d6	mov    %edx,%esi
    	*pGen++ = 0x89; *pGen++ = 0xc7; //	   0x0000000000400513 <+22>:	89 c7	mov    %eax,%edi
    	*pGen++ = 0xb8; *pGen++ = 0x00; *pGen++ = 0x00; *pGen++ = 0x00; *pGen++ = 0x00; //	   0x0000000000400515 <+24>:	b8 00 00 00 00	mov    $0x0,%eax
    	*pGen++ = 0xe8; *pGen++ = 0x08; *pGen++ = 0x00; *pGen++ = 0x00; *pGen++ = 0x00; //	   0x000000000040051a <+29>:	e8 08 00 00 00	callq  0x400527 <add>
    
    	*pGen++ = 0x89; *pGen++ = 0x45; *pGen++ = 0xfc; //	   0x000000000040051f <+34>:	89 45 fc	mov    %eax,-0x4(%rbp)
    	*pGen++ = 0x8b; *pGen++ = 0x45; *pGen++ = 0xfc; //	   0x0000000000400522 <+37>:	8b 45 fc	mov    -0x4(%rbp),%eax
    	*pGen++ = 0xc9; //	   0x0000000000400525 <+40>:	c9	leaveq
    	*pGen++ = 0xc3; //	   0x0000000000400526 <+41>:	c3	retq
    
    //	addFun af = addWrap;
    	addFun af = (addFun)pCode; // 如果我把这行注释掉  上一行行去掉注释  用上一行的方式 就没问题 但是反之 跑起来就会core core在下一行
    	int r = af(5, 15);
    	printf("%d
    ", r);
    	free(pCode);
    	return 0;
    }
    

    有人说用mmap才可以解决core的问题
    我有空试试

  • 相关阅读:
    IllegalStateException
    TimeUnit简析
    Cron表达式
    任务调度之Timer与TimerTask配合
    Executor简析
    this逃逸
    SQL、SQL Server、MySQL与Oracle
    数据库与实例
    vw 、vh、vmin 、vmax
    逻辑(内存)分页与物理分页
  • 原文地址:https://www.cnblogs.com/simoncook/p/11807101.html
Copyright © 2011-2022 走看看