通过函数的入口地址来调用函数

 

例程:

int i; //定义一个测试变量
void test() //定义一个函数
{
i = 6; //给测试变量赋初值
}
int main()
{
int addr; //定义一个保存地址的变量
addr = (int)test; //将函数test的函数入口地址存入addr
((void(*)(int))addr)(0); //根据函数入口地址调用test函数
//((void(*)(void))addr)();//比较上一条语句可以不用传参数了
for(;;); //无限循环
}
//

经常见到嵌入式设计中,将某一程序段的入口地址转换为一个函数,我们来分析一下它的成分:
     如在bootloader的0x00000020地址上的双字单元中有这样一条语句:
@Address is 0x00000020
     bpowerdown         @Jump to the flag "powerdown"
...
...
powerdown:
...
...
     然后在某一C头文件中可以见到这样的宏声明:
#define EnterPWDN(clkcon) ((void (*)(int))0x20)(clkcon)
     初一看,乱七八糟的,现在,我们来整理一下。
     不难看出,当我们编程调用EnterPWDN(clkcon)函数的时候,编译器在编译前首先把EnterPWDN(clkcon)转换为
((void (*)(int))0x20)(clkcon)语句。
对于这个语句,我们将之分解成3部分来看。
     1:(void (*)(int)
     2:0x20
     3:(clkcon)

     (clkcon)是函数的参数,就不用说了。0x20呢?当然就是要转换的函数的入口地址了。那对于(void(*)(int)呢??呵呵……,这一部分作为一个整体,描述了转换后的函数的类型,即无返回值,带一个整形参数。而中间那个”(*)“,就表示要转换成一个函数(或者说把0x20转换为一个地址,因为在ARM汇编中,就把C语言的函数名当作一个地址标号看待了)。就像我们平常用的强制类型转换一样,(int)temp,只不过这里是将一个数转换为另一类型,而那是将一个地址转换为一个函数罢了。

关于arm中: #define EnterPWDN(clkcon)((void (*)(int))0x00000020)(clkcon)

1。首先要知道带参数的宏定义。定义方法如下:

     #define 宏名(参数) 字符串

     如下:#define add(a,b) (a)+(b)

     那么:程序中的 b=add((2*3),(4*5)) 等价于 b=(2*3)+(4*5) 。

     具体请参考c语言书籍。

2。下面看:EnterPWDN(0x7fff4);

     会被替换成:((void (*)(int))0x00000020)(0x7fff4);

     这个涉及到函数指针,函数指针的用法如下:

     void fun(int x);

      void(*pfun) (int x);

     pfun=fun;

     那么调用函数fun可以这样写:a. fun(5); b. (*pfun)(5); 

      则:((void(*)(int))0x00000020)(0x7fff4); 中蓝色0x7fff4是传递的参数,红色(void(*)(int))0x00000020是函数名。其实函数名本身就是一个地址。像上式中,如果你知道函数fun存在地址0x012345,那么理论上你可以这么写:0x012345(5),当然实际上不能这么写,因为你虽然fun在0x012345地址,但是你编译器不知到这个函数的参数、返回值等信息。所以要强制转换一下。

     接下来看一下怎么把一个地址转换成函数名:

     如果把 char a ; 中的a转换成 int  形变量,要这样:b=(int)a;

     同样要把0x00000020地址转换为函数名,也要强制转换,只要在0x00000020的前面加上要转换的类型。(void(*)(int))就是函数类型,和void (*pfun) (int x); // 声明一个函数指针 是相对应的。

3。再看一下EnterPWDN(0x7fff4); 调用之后具体的参数传递,这个涉及到c和汇编的相互调用。

     看

__ENTRY
ResetEntry
 
      b ResetHandler
    
      b HandlerUndef ;handler forUndefined mode
      b HandlerSWI ;handler for SWIinterrupt
      b HandlerPabort ;handler forPAbort
      b HandlerDabort ;handler forDAbort
      b .  ;reserved
      b HandlerIRQ ;handler for IRQinterrupt
      b HandlerFIQ ;handler for FIQinterrupt

;@0x20
      b EnterPWDN ; Must be @0x20.

     跳到0x00000020地址后会接着跳转到标号EnterPWDN 处。

 EnterPWDN
      mov r2,r0  ;r2=rCLKCON
      tst r0,#0x8  ;SLEEP mode?
      bne ENTER_SLEEP

     看这里第一句:mov r2,r0  。其实 r0中存的就是参数0x7fff4 ,这个涉及到c调用汇编,自己找一下参考书吧,我就不献丑了。 

ARM中#define EnterPWDN(clkcon) ((void(*)(int))0x20)(clkcon))代表什么意思

1.首先,这是一个带参数宏定义,所以,如果有语句EnterPWDN(0x7fff4)则展开以后就是如下语句 ((void(*)(int))0x20)(0xffff4))

2.(void(*)(int))0x20的意思

(void(*)(int))作为一个强制类型转换,将0x20转换为一个函数指针类型,这个函数的原型为 voidfun(int); 而后面的0xffff4就是代表的函数的参数,所以 ((void(*)(int))0x20)(0xffff4))整句话的意思就是调用地址在0x20处的函数,参数为0xffff4.其实看后面实际上就是跳转到地址0x20处执行代码,并且把参数传到r0,ATPCS中规定1-4个参数对应的存储寄存器为r0-r3。

3.再看在2440init.s中对该部分的定义

b ResetHandler
b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b .   ;reserved
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt

;@0x20
b EnterPWDN ; Must be @0x20.

   EnterPWDN
        mov r2,r0   ;r2=rCLKCON
        tst r0,#0x8   ;SLEEP mode?
        bne ENTER_SLEEP

b EnterPWDN ; Must be @0x20.这条语句,对应的指令地址为0x20,这就是前面((void(*)(int))0x20)中0x20的意义,然后语句跳到EnterPWDN执行,单然,参数值就传给了r0,后面的东西查看硬件说明。

  • 8
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

隨意的風

如果你觉得有帮助,期待你的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值