You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用函数时状态寄存器(EFLAGS)保存问题及GCC调用约定相关疑问

GCC Calling Conventions: EFLAGS Preservation and Return Address Clarification

Great question—let’s break down your two key points clearly, since these are common stumbling blocks when learning x86 calling conventions with GCC:

1. Who is responsible for saving the EFLAGS register?

In standard 32-bit GCC calling conventions (like the default cdecl), the EFLAGS register is not required to be preserved by the callee. Here’s what that means in practice:

  • A called function can freely modify any bits in EFLAGS (such as the carry flag CF, zero flag ZF, or overflow flag OF) without restoring their original state before returning.
  • If the caller needs to retain the state of EFLAGS across a function call, it’s entirely the caller’s responsibility to save and restore it manually. This is done with instructions like pushf (push EFLAGS to the stack) before invoking the function, and popf (restore EFLAGS from the stack) once the function returns.

There are rare edge cases where a function explicitly documents that it preserves specific flags, but this is the standard, default behavior.

2. Is the return address pushed to the stack equal to the value of the instruction register (EIP)?

Not exactly—let’s walk through what happens when the call instruction executes:

  1. First, the CPU calculates the address of the next instruction immediately following the call itself (since call is a multi-byte instruction, this is EIP + length_of_call_instruction).
  2. This calculated address is pushed onto the stack as the return address.
  3. Finally, the CPU updates EIP to point to the entry address of the called function, so execution jumps there.

So the return address stored on the stack is the address the caller should resume executing at after the callee finishes (via the ret instruction). At the moment the return address is pushed, EIP has already been updated to this next instruction’s address—but to put it simply: the return address isn’t the EIP value before the call ran, it’s the EIP value that would have been used if the call hadn’t triggered a jump.

For a concrete example:

  • Suppose call my_func is at memory address 0x1000 and takes up 5 bytes.
  • Before call runs, EIP is 0x1000.
  • The CPU first computes 0x1000 + 5 = 0x1005 (the next instruction’s address), pushes 0x1005 to the stack.
  • EIP is then set to my_func’s address (say 0x2000), so execution starts there.
  • When my_func runs ret, it pops 0x1005 back into EIP, and execution resumes at that instruction.

内容的提问来源于stack exchange,提问作者Dusan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:43:56