• arm上的参数列表传递的分析(以android为例)


    1. Linux中可变列表实现的源码分析

    查看Linux0.11的内核源代码,对va_list, va_start, va_arg 的实现如下:

      1. va_list的实现没有差别,char
        typedef char
        va_list;
      2. va_start的实现

        #define va_start(AP, LASTARG)                                            
        (__builtin_saveregs (),                                                 
        AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
        

        注:__builtin_saveregs()是gcc内置的函数,用来保存堆栈,其实好像也没什么必要这么做
        然后将AP指向可变参数前一个参数的的结尾。这里的LASTARG就是前面的param1

      3. va_arg的实现

        #define va_arg(AP, TYPE)                                                 
        (AP += __va_rounded_size (TYPE),                                        
        *((TYPE *) (AP - __va_rounded_size (TYPE))))
        
        注:这里的逻辑有点奇怪,先将指针移动到该参数的结尾,再返回指针减去该参数长度的位置内容。
        其实这是对C语言的 , 操作符的利用,用来实现类型转换。,后面的值才是返回值,所以移动指针的操作必须在前。一个很好的小技巧
      4. 辅助函数

        #define __va_rounded_size(TYPE)   
        (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
        

        注:前面的va_start和va_arg 都没有使用 sizeof,而是用的 va_rounded_size,这个是什么意思呢?其实和结构的成员对齐相似,每个参数最少占用4个字节。
        例如一个char型的参数,也要占用4个字节,4个连续的char型参数,就要占用16个字节。
        所以这里使用用 [x+ (n-1)] / n * n 的对齐公式来实现最少对齐。
        如果要取一个char型参数,要用va_arg(args, int), 而不能用va_arg(args, char),否则会产生编译警告,运行异常。

    2. ARM上可变参数列表实现分析——二进制

    对于pc上的可变参数列表,比较容易理解:参数全部存储在栈上。所以:va_list p定义一个指针,va_start(p, arg_a)获取参数列表地址,该地址就是va_start第二个参数对应数据之后的地址,,在栈上表现为:arg_a+sizeof(arg_a). 此后根据参数类型,使用va_arg依次从指定的参数列表地址取数据。

    但时对于arm上,一个会使用寄存器传递参数的平台,又回怎样处理的呢?

    通过写一个简单的示例程序:

     int test(int a, ...){
           va_list p;
           va_start(p, a);
           printf("%d", va_arg(p, int));
           va_end(p);
      }

    实际编译后如下:

    .text:00000BE0 ; test(int, ...)
    .text:00000BE0                 EXPORT _Z4testiz
    .text:00000BE0 _Z4testiz                               ; CODE XREF: main+8p
    .text:00000BE0
    .text:00000BE0 var_1C          = -0x1C
    .text:00000BE0 varg_r0         = -0x10
    .text:00000BE0 varg_r1         = -0xC
    .text:00000BE0 varg_r2         = -8
    .text:00000BE0 varg_r3         = -4
    .text:00000BE0
    .text:00000BE0                 PUSH    {R0-R3}
    .text:00000BE2                 PUSH    {R0-R2,LR}
    .text:00000BE4                 LDR     R0, =(unk_2168 - 0xBEE)
    .text:00000BE6                 ADD     R3, SP, #0x20+varg_r2
    .text:00000BE8                 LDR     R1, [SP,#0x20+varg_r1]
    .text:00000BEA                 ADD     R0, PC          ; format
    .text:00000BEC                 STR     R3, [SP,#0x20+var_1C]
    .text:00000BEE                 BLX     printf
    .text:00000BF2                 ADD     SP, SP, #0xC
    .text:00000BF4                 POP     {R3}
    .text:00000BF6                 ADD     SP, SP, #0x10
    .text:00000BF8                 BX      R3
    .text:00000BF8 ; End of function test(int,...)

    可以看到粗体部分,进行了2次push。而有所了解得程序员,我们知道一般只会push一次,即后面的那一个——用来保存寄存器状态和返回地址。

    那么第一次是干什么呢?显然这和可变参数有关!

    arm上使用寄存器传递参数时一般只是用r0~r3,因此,系统直接将其压入栈中!然后和一般函数一样的方式去保护可能会被覆盖的寄存器的状态。

    将r0~r3压入栈中之后,如果还有更多的参数,则之前必须(应当)已经压入栈中,那么此时的状态就和pc上的类似了。如下所示

    (高地址)

    ...

    arg_5

    arg_4

    arg_3<---r3

    arg_2<---r2

    arg_1<---r1

    arg_0<---r0

    <需要保护的寄存器值>

    ...

    (低地址)


     3. 可变参数列表类型的转换

    在Android上, va_list可变参数列表并没有直接定义,是gcc的一个built in类型.但是实际分析中发现,其实va_list就是一个char*,因此我们只需要将其想办法进行类型转化即可.

    但是实际过程中,无法通过强制类型转化完成.因此,使用内联汇编完成类型转换.如下

        va_list args;
        char* argBuf;
    
        __asm__(        
            "mov %[argBuf], %[args]"
            : [argBuf] "=r" (argBuf)
            : [args] "r" (args)    
            :
        );
     
  • 相关阅读:
    tsc errors All In One
    TypeScript declare Set Array type All In One
    Linux exit nano editor All In One
    Linux bash pbcopy command All In One
    千焦与千卡的换算公式 All In One
    linux 中 sed N选项将两行合并为一行处理
    linux 中 sed n选项将两行作为一行处理,屏蔽第一行
    linux 中sed命令删除匹配字符之前的一行
    linux 中sed命令 P和p的区别
    linux 中 sed命令替换命令时i选项忽略大小写
  • 原文地址:https://www.cnblogs.com/rainduck/p/3999276.html
Copyright © 2020-2023  润新知