简体   繁体   中英

Saving Status register when calling a function

As I understand, when I'm calling a function, based on GCC Calling Convention, this is what happens:

Caller saves values of AX, CX and DX registers. Arguments and returning address are pushed on stack. Also, calle must preserve values of SI, DI, BX and BP registers.

But, what about Status register? Who saves it?

Also, is value of returning address which is pushed on stack actually value of Instruction register?

The status register is not preserved across function calls. If there's something important in the status register it needs to be copied elsewhere (generally with SETcc), but the calling convention doesn't require the calling function to do this, just as it doesn't require the calling function to save and restore AX et al. if there's nothing important in them.

Answering your second question:

Also, is value of returning address which is pushed on stack actually value of Instruction register?

You mean the value pushed by call instruction? Yes, that's current rip ( eip/ip in 32/16 bit modes) value during call internal execution (as rip points to next instruction).

And the ret instruction will pop whatever value is on top of stack, and set that into rip , changing the code-execution flow for next instruction (away from the instruction next after the ret to the address/value which was in stack). So the value from stack becomes content of ip register, after ret is finished. The ret is like (non-existent) pop ip , but it has its own mnemonics to make it stand out in source better when reading it by human, and also it has completely different opcode, so the HW implementation in the transistors is completely specific to it (which makes sense on modern x86 where the ret implementation is using many additional tricks for better performance, but I'm sort of curious why 8086 would not encode it as pop ip , like just another register for pop , was probably somewhat special in some detail even back then).

GCC Calling Convention

gcc uses the standard calling convention on whatever platform it's targeting. It sounds like you're describing the i386 System V calling convention / ABI used on Linux, and/or some Windows calling conventions. (some of which pass args differently, but make the same choice of registers that can be clobbered).

You're using 16-bit register names, but gcc barely supports 16-bit x86. It basically generates 32-bit code and then assembles it with .code16 so most instructions have operand-size and/or address-size prefixes.

Caller saves values of AX, CX and DX registers

No, the caller does that only if it had any data in them that it wants to preserve across the call . The normal case is that the caller lets those values die. "caller-saved" vs. "callee-saved" is bad terminology because it implies that all registers actually get saved somewhere.

Much easier to understand, IMO, is

  • call-clobbered : EAX ECX EDX and condition codes (part of EFLAGS), all xmm regs
  • call-preserved : EBX, ESI EDI, EBP, ESP.

DF must be 0 at call and return, so string instructions go upwards. (DF is another bit in EFLAGS). The x87 stack must be empty on call and ret , except for functions which return an FP value (in which case st0 has the return value, and the rest of the x87 stack is empty).

Call-clobbered means that after a call , the caller has to assume the register holds garbage, whether or not the callee actually used the register. If there was anything in that register the caller needed for later, it has to move it somewhere else. But if not, it's totally fine to let the value die. eg to compile something like rv = foo(a + b + c) , the caller would calculate a+b+c in a register. But if it doesn't also need that value after the function call, it doesn't have any need to preserve it.

Call-preserved means the caller can assume the register value hasn't changed, whether the callee simply avoided touching that register, or the callee saved / restored it. (Or for ESP, it's common for the callee to restore it with an add esp, 28 or similar, to reverse whatever changes it made with push and sub . It doesn't matter how the callee manages to return with call-preserved registers still holding the caller's values, just that it does. This is why "callee-saved" is also not the clearest term: it implies the callee explicitly saves them.

But, what about Status register? Who saves it?

Nobody saves it, except in very rare cases . The caller could save it if desired, but usually it's far easier and cheaper to to just redo a compare ( popf is slow, and the pushf to save EFLAGS in the first place isn't free).

Or more often there wasn't any useful data in the condition codes, just integer values in integer registers. Most instructions write EFLAGS, but most of the time you never read those results. You usually use add , imul , and so on for the integer result and ignore the flag result.

Fun fact: 64-bit OS X system calls set CF on error, otherwise they clear CF . No common 32 or 64-bit function-calling conventions return anything in EFLAGS; they're just clobbered. (For Linux system calls, EFLAGS / RFLAGS is preserved. It's typical for system calls to not clobber any registers, other than the return value, partly because this avoids leaking kernel information back to user space.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM