用实例理解c语言的多参数传递过程


int test_va_arg()
{
    char str[4+1] = {0};

    char argv1[4+1]  = {'1', '1', '1', '1', 0};
    char argv2[4+1]  = {'2', '2', '2', '2', 0};
    char argv3[4+1]  = {'3', '3', '3', '3', 0};
    char argv4[4+1]  = {'4', '4', '4', '4', 0};
    char argv5[4+1]  = {'5', '5', '5', '5', 0};
    char argv6[4+1]  = {'6', '6', '6', '6', 0};
    char argv7[4+1]  = {'7', '7', '7', '7', 0};
    char argv8[4+1]  = {'8', '8', '8', '8', 0};
    char argv9[4+1]  = {'9', '9', '9', '9', 0};
    char argv10[4+1] = {'a', 'a', 'a', 'a', 0};

    
    strcpy(str,"INFO");
    //tLog( str, argv1, argv2, argv3, argv4, argv5, argv6, argv7, argv8, argv9, argv10 ); 
    //tLog1( str, argv1, argv2, argv3, argv4, argv5, argv6, argv7, argv8, argv9, argv10 ); 

    strcpy( (void *)0x42000200, (const char *)"xoutputx: %s.\n" );


    
    fuck_log1( str, argv1, argv2, argv3, argv4, argv5, argv6, argv7, argv8, argv9, argv10 );

    //tLog1( str, argv1, argv2, argv3, argv4, argv5, argv6, argv7, argv8, argv9, argv10 );
    

    
    return 0;
}

/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * relocate - common relocation function for AArch64 U-Boot
 *
 * (C) Copyright 2013
 * Albert ARIBAUD
 * David Feng
 */

#include
#include
#include
#include
#include


ENTRY( fuck_code )
    LDR    X10, =0x42000000
    STR    X10, [X10, #0x00]
                
    STP    X29, X30, [SP,#-0x100]!
    MOV    X29, SP
    STR    X19, [SP,#0x10]
            
    MOV    W0, #0xFFFFFFf8
    STR    W0, [X29,#0x38]
    LDR    W1, [X29,#0x38]
    LDR    X0, [X29,#0x20]

    STR    w1, [X10, #0x08]
    TBZ    W1, #0x1F, loc_3718a


    ADD    W2, W1, #8
    CMP    W2, #0
    STR    W2, [X29,#0x38]
    STR    w1, [X10, #0x10]

    ADD    W2, W2, #3

    STR    w2, [X10, #0x18]
    B.LE   loc_3728a

loc_3718a:    
    LDR    X4, =0x1234
    B      quit    
loc_3728a:
    LDR    X4, =0x55aa     
quit:  
    STR    X4, [X10, #0xf0]

    LDR    X19, [SP,#0x10]
    LDP    X29, X30, [SP],#0x100
    ret
ENDPROC(fuck_code)

ENTRY( fuck_log1 )           
    STP             X29, X30, [SP,#-0x100]!
    //变量sp+0x00  存储 X29, X30 的原始值
    MOV             X29, SP
    LDR    X10, =0x42000000
    STR    X10, [x10, #0x00]
          
    ADD             X0,  X29, #0x100   
    //x0 栈顶指针 指向函数调用之前的堆栈sp
    //函数的参数如果是8个以内(包含8个) 用x0-x7来传递  (param0-param7)
    //超过8个的部分 从此sp开始依次先储存好 比如超过3个
    //sp+0x00->param8 sp+0x08->param9 sp+0x10->param10

    STR             X19, [SP,#0x10]
    //变量sp+0x10  存储 x19 的原始值
    ldr             X19, =0x42000200   //"xoutputx: %s.\n" printf的第一个参数 x0

    mov    X11, X0
    STR    X11, [x10, #0x08]                                  
    ldr    X12, [X11]
    STR    X12, [x10, #0x10] 
    
    add    X11, x11, #0x08
    STR    X11, [x10, #0x18]
    ldr    X12, [X11]
    STR    X12, [x10, #0x20] 
    
    add    X11, x11, #0x08
    STR    X11, [x10, #0x28]
    ldr    X12, [X11]
    STR    X12, [x10, #0x30] 
    
    add    X11, x11, #0x08
    STR    X11, [x10, #0x38]
    ldr    X12, [X11]
    STR    X12, [x10, #0x40] 

    //add    X11, x11, #0x08
    STR    X7, [x10, #0x48]
    ldr    X12, [X7]
    STR    X12, [x10, #0x50] 

    STR    X6, [x10, #0x58]
    ldr    X12, [X6]
    STR    X12, [x10, #0x60] 

    

    STP             X0, X0, [X29, #0x20] 
    //变量sp+0x20 sp+0x28 存储 x0 栈顶指针

    ADD             X0, X29, #0xC0
    STR             X0, [X29,#0x30]
    //变量sp+0x30 = sp+0xc0

    MOV             W0, #0xFFFFFFC8
    STR             W0, [X29,#0x38]
    //变量sp+0x38 = 0xFFFFFFC8  4字节

    MOV             W0, #0xFFFFFF80
    STR             W0, [X29,#0x3C]
    //变量sp+0x3C = 0xFFFFFF80  4字节

       
    STP             X1, X2, [X29,#0xC8] 
    STP             X3, X4, [X29,#0xD8]
    STP             X5, X6, [X29,#0xE8]
    STR             X7, [X29,#0xF8]
    //传递进来的参数 依次压入堆栈 x1-x7 c8-d8-e8-f8-0x100-[x0 栈顶指针的位置]


    STR             Q0, [X29,#0x40]
    STR             Q1, [X29,#0x50]
    STR             Q2, [X29,#0x60]
    STR             Q3, [X29,#0x70]
    STR             Q4, [X29,#0x80]
    STR             Q5, [X29,#0x90]
    STR             Q6, [X29,#0xA0]
    STR             Q7, [X29,#0xB0]
    //0x40-0xc0 用0填充这块区域 没有用到

loc_36FC:
    LDR             W1, [X29,#0x38]
    //变量sp+0x38 = 0xFFFFFFC8  4字节
    LDR             X0, [X29,#0x20]
    //栈顶指针
    TBZ             W1, #0x1F, loc_3718
    //测试 sp+0x38 0xFFFFFFC8 & 0x1F 是否等于0,等于0的话跳转到loc_3718
    //否则执行下一条指令
    ADD             W2, W1, #8
    //[sp+0x38] & 0x1f 不等于0, 自加8 
    CMP             W2, #0
    STR             W2, [X29,#0x38]
    B.LE            loc_3728
    //不等于0 跳转

loc_3718:     
    //等于0 说明经过7次循环 x1-x7已经用完了 
    ADD             X1, X0, #0xF
    AND             X1, X1, #0xFFFFFFFFFFFFFFF8
    STR             X1, [X29,#0x20]
    B               loc_3730

loc_3728:                                
    LDR             X0, [X29,#0x28]
    //sp+0x28 存储 x0 栈顶指针
    ADD             X0, X0, W1,SXTW
    //x0+w1 w1符号扩展 此例是一个负数 
    //传递进来的参数 依次压入堆栈 x1-x7 c8-d8-e8-f8-0x100-[x0 栈顶指针的位置]
    //栈顶指针的位置 + 负数 精确计算好了 第一次是定位到 x1 
    //7*8=56 0x38 0xFFFFFFC8 + 0x38 = 0x00000000 即-0x38
    //下一次循环就是 -0x30 总共循环7次

loc_3730:                              
    LDR             X1, [X0]
    //取得压入堆栈的x1的值 第二次是x2 ...
    CBZ             X1, loc_3744
    //等于空退出函数
    MOV             X0, X19 
    BL              printf
    B               loc_36FC

loc_3744:                              
    LDR             X19, [SP,#0x10]
    //变量sp+0x10   恢复 x19 的原始值
    LDP             X29, X30, [SP],#0x100
    //恢复 X29, X30 的原始值,sp堆栈平衡
    RET
    //程序返回
    
ENDPROC(fuck_log1)

IDA伪C码:

__int64 *__fastcall fuck_log1(long double a1, long double a2, long double a3, long double a4, long double a5, long double a6, long double a7, long double a8, __int64 a9, __int64 a10, __int64 a11, __int64 a12, __int64 a13, __int64 a14, _QWORD *a15, _QWORD *a16, __int64 a17, __int64 a18, __int64 a19, __int64 a20)
{
  int v20; // w1
  __int64 *result; // x0
  __int64 *v22; // [xsp+20h] [xbp+20h]
  int v23; // [xsp+38h] [xbp+38h]

  MEMORY[0x42000000] = 0x42000000LL;
  MEMORY[0x42000008] = &a17;
  MEMORY[0x42000010] = a17;
  MEMORY[0x42000018] = &a18;
  MEMORY[0x42000020] = a18;
  MEMORY[0x42000028] = &a19;
  MEMORY[0x42000030] = a19;
  MEMORY[0x42000038] = &a20;
  MEMORY[0x42000040] = a20;
  MEMORY[0x42000048] = a16;
  MEMORY[0x42000050] = *a16;
  MEMORY[0x42000058] = a15;
  MEMORY[0x42000060] = *a15;
  v22 = &a17;
  v23 = -56;
  while ( 1 )
  {
    v20 = v23;
    result = v22;
    if ( (v23 & 0x80000000) != 0 && (v23 += 8, v20 + 8 <= 0) )
      result = (__int64 *)((char *)&a17 + v20);
    else
      v22 = (__int64 *)(((unsigned __int64)v22 + 15) & 0xFFFFFFFFFFFFFFF8LL);
    if ( !*result )
      break;
    printf((const unsigned __int8 *)0x42000200);
  }
  return result;
}

原始c码:


void tLog1(char *sInfo, ...)
{
    char *str   = NULL;
    char *str1 = NULL;
    char *str2 = NULL;
    va_list sArgv;          // 申请参数列表变量
    va_start(sArgv, sInfo); // 申明最后一个传递给函数的已知的固定参数
    /* 依次读取固定参数 sInfo 后的 参数 */

    while(1){
      str  = va_arg(sArgv, char*);
      if( str ){  
          printf("output: %s \n", str );
      }else{
          break;  
      }
    }
    va_end(sArgv);
}

你可能感兴趣的:(c语言,开发语言)