ad转换汇编语言程序_C程序如何转换成汇编

本文探讨了C程序如何转换为汇编语言,涉及函数堆栈框架、CPU寄存器、参数传递、局部变量分配等方面,解释了函数调用、堆栈帧的创建和退出过程,以及访问全局和局部变量的方法。

ad转换汇编语言程序

在较早的文章中,我们已经了解了C运行时:启动main如何将C程序存储在RAM存储器中之前 。 在这里,我们将看到“ C程序如何转换为程序集?” 以及它在机器级别上的不同方面。

/!\:最初发布于www.vishalchovatiya.com

关于功能堆栈框架的一些知识

  • 在执行功能代码期间,将在堆栈存储器中创建一个新的堆栈框架,以允许访问功能参数和局部变量。
  • 堆栈帧增长的方向完全取决于编译器ABI,这超出了本文的讨论范围。
  • 有关堆栈帧大小,内存分配,从堆栈帧返回的完整信息在编译时确定。
  • 在研究汇编代码之前,您应该了解两件事:
  1. x86机器的CPU寄存器。
  2. x86汇编指令:由于这是一个非常广泛的主题,并且经常更新,因此我们只会看到示例所需的指令。

x86 CPU寄存器

通用寄存器

指针寄存器

段寄存器

索引寄存器

除了所有这些之外,还有许多其他寄存器,甚至我都不知道。 但是上述寄存器足以理解后续主题。

C程序如何转换为汇编?

我们将考虑以下示例,该示例的内联汇编旨在理解其在计算机级别上工作的不同方面:

我们将关注func()函数的堆栈框架。 但是在分析它的堆栈框架之前,我们将了解函数的调用是如何发生的

函数调用

函数调用是通过调用指令(请参见第15行)完成的,该指令等同于以下子例程指令:

push rip + 1 ; return address is address of next instructions
jmp func

在这里, call rip+1 (并不是为了简单起见, rip+1只是为了简单起见,从技术上讲,它将由指令的大小代替)存储在堆栈中,该堆栈是对func()调用结束后的返回地址。

功能栈框

功能堆栈框架分为三部分

1.序言/条目:您可以看到针对起始括号{的序言(第2至4行)是为func()设置堆栈帧的序言,第2行将上一个帧指针推入堆栈,第3行正在使用堆栈结尾更新当前的帧指针,而这将是新的帧开始。

push基本上相当于:

sub esp, 4   ; decrements ESP by 4 which is kind of space allocation
mov [esp], X ; put new stack item value X in

参数传递

在调用call指令之前, func()参数存储在第14行的edi寄存器中。 如果有更多参数,则它将被存储在后续寄存器中,或者将使用堆栈和地址。

func()第4行通过将参数arg帧指针(由rbp寄存器指向)拉低4个字节来保留空间,因为它的类型为int 。 然后mov指令将使用edi值存储对其进行初始化。 这就是在当前堆栈帧中传递和存储参数的方式。

---|-------------------------|--- main()
             |                         |          
             |                         |          
             |                         |          
             |-------------------------|          
             |    main frame pointer   |          
rbp & rsp ---|-------------------------|--- func()
in func()    |           arg           |          
             |-------------------------|          
             |            a            |          
             |-------------------------|    stack 
             |            +            |      |   
             |            +            |      |   
             |            +            |      |   
          ---|-------------------------|---  \|/  
             |                         |          
             |                         |

为局部变量分配空间

2.用户 代码:第5行再次通过将帧指针进一步拉低4个字节来保留局部变量a空间。 mov指令将使用值5初始化该内存。

访问全局和局部静态变量

  • 正如您在上面看到的那样,因为g的地址是固定的(位于数据段中),所以直接用g的绝对地址对其进行寻址。
  • 并非总是如此。 在这里,我们已经为x86模式编译了代码,这就是为什么它使用绝对地址访问它。
  • 在x64模式下,地址是使用rip寄存器解析的,这意味着汇编器和链接器应该合作计算g相对于rip寄存器所指向的当前指令最终位置的偏移量。
  • 局部静态变量也适用相同的语句。

3.后记/退出:用户代码执行后,将前一帧指针从堆栈检索由pop指令我们已存储在第2行pop等同于:

mov X, [esp] ; put top stack item value into X 
add esp, 4   ; increments ESP by 4 which is kind of deallocation

从函数返回

ret指令通过从call指令存储的堆栈中检索跳转地址,从其中func()地方跳回到下一条指令。 ret是子例程指令,它等效于:

pop rip ; 
jmp rip ;

如果指定了任何返回值,则它将存储在eax寄存器中,您可以在第16行中看到。

因此,这就是“ C程序如何转换为汇编程序?”。 尽管此类信息严格与编译器和ABI结合在一起。 但是大多数编译器,ABI和指令集体系结构或多或少遵循相同的原则。 如果您没有阅读我以前的文章 ,这里有一些简单的常见问题解答可以帮助您更好地理解:

直观的常见问题解答

问:如何确定堆栈的生长方向?

答:简单...! 通过比较两个不同函数的局部变量的地址。

int *main_ptr = NULL ;
int *func_ptr = NULL ;
void func ()  { int a; func_ptr = &a; }
int main ()
 {
    int a; main_ptr = &a;
    func();
    (main_ptr > func_ptr) ? printf ( "DOWN\n" ) : printf ( "UP\n" );
    return 0 ;
}

问:如何故意破坏堆栈?

损坏存储在堆栈帧中的SFR值。

void func ()
 {
    int a;
    memset (&a, 0 , 100 ); // Corrupt SFR values stored in stack frame
}
int main ()
 {
    func();
    return 0 ;
}

问:如何增加堆栈框架大小?

alloca()是答案。 GOOGLE一下,或看看这个 。 尽管不建议这样做。

翻译自: https://hackernoon.com/how-c-program-converts-into-assembly-0m1f32f5

ad转换汇编语言程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值