Programing

x86 어셈블리의 레지스터에 사용되는 푸시 / 팝 명령어의 기능은 무엇입니까?

lottogame 2020. 10. 18. 08:22
반응형

x86 어셈블리의 레지스터에 사용되는 푸시 / 팝 명령어의 기능은 무엇입니까?


어셈블러에 대해 읽을 때 나는 종종 프로세서의 특정 레지스터를 푸시 하고 나중에 이전 상태로 복원하기 위해 다시 한다고 쓰는 사람들을 만납니다 .

  • 레지스터를 어떻게 밀 수 있습니까? 어디로 밀려나? 왜 이것이 필요한가요?
  • 이것이 단일 프로세서 명령으로 귀결됩니까 아니면 더 복잡합니까?

값을 푸시 (반드시 레지스터에 저장되지 않음)하는 것은 스택에 쓰는 것을 의미합니다.

팝핑 은 스택 맨 위에있는 모든 것을 레지스터 복원하는 것을 의미 합니다. 다음은 기본 지침입니다.

push 0xdeadbeef      ; push a value to the stack
pop eax              ; eax is now 0xdeadbeef

; swap contents of registers
push eax
mov eax, ebx
pop ebx

레지스터를 푸시하는 방법은 다음과 같습니다. x86에 대해 이야기하고 있다고 가정합니다.

push ebx
push eax

스택에 푸시됩니다. ESP레지스터 의 값은 x86 시스템에서 스택이 아래로 커짐에 따라 푸시 된 값의 크기로 감소합니다.

가치를 보존하는 것이 필요합니다. 일반적인 사용법은

push eax           ;   preserve the value of eax
call some_method   ;   some method is called which will put return value in eax
mov  edx, eax      ;    move the return value to edx
pop  eax           ;    restore original eax

A push는 x86의 단일 명령어로 내부적으로 두 가지 작업을 수행합니다.

  1. 푸시 된 값을 ESP레지스터의 현재 주소에 저장합니다 .
  2. 감분 ESP푸시 값의 크기를 레지스터.

어디로 밀려나?

esp - 4. 더 정확하게:

  • esp 4를 뺀다
  • 값이 푸시됩니다 esp

pop 이것을 뒤집습니다.

System V ABI는 rsp프로그램이 실행될 때 적절한 스택 위치를 가리 키도록 Linux에 지시 합니다. 프로그램이 시작될 때 기본 등록 상태 (asm, linux)는 무엇입니까? 일반적으로 사용해야하는 것입니다.

레지스터를 어떻게 밀 수 있습니까?

최소 GNU GAS 예 :

.data
    /* .long takes 4 bytes each. */
    val1:
        /* Store bytes 0x 01 00 00 00 here. */
        .long 1
    val2:
        /* 0x 02 00 00 00 */
        .long 2
.text
    /* Make esp point to the address of val2.
     * Unusual, but totally possible. */
    mov $val2, %esp

    /* eax = 3 */
    mov $3, %ea 

    push %eax
    /*
    Outcome:
    - esp == val1
    - val1 == 3
    esp was changed to point to val1,
    and then val1 was modified.
    */

    pop %ebx
    /*
    Outcome:
    - esp == &val2
    - ebx == 3
    Inverses push: ebx gets the value of val1 (first)
    and then esp is increased back to point to val2.
    */

The above on GitHub with runnable assertions.

Why is this needed?

It is true that those instructions could be easily implemented via mov, add and sub.

They reason they exist, is that those combinations of instructions are so frequent, that Intel decided to provide them for us.

The reason why those combinations are so frequent, is that they make it easy to save and restore the values of registers to memory temporarily so they don't get overwritten.

To understand the problem, try compiling some C code by hand.

A major difficulty, is to decide where each variable will be stored.

Ideally, all variables would fit into registers, which is the fastest memory to access (currently about 100x faster than RAM).

But of course, we can easily have more variables than registers, specially for the arguments of nested functions, so the only solution is to write to memory.

We could write to any memory address, but since the local variables and arguments of function calls and returns fit into a nice stack pattern, which prevents memory fragmentation, that is the best way to deal with it. Compare that with the insanity of writing a heap allocator.

Then we let compilers optimize the register allocation for us, since that is NP complete, and one of the hardest parts of writing a compiler. This problem is called register allocation, and it is isomorphic to graph coloring.

When the compiler's allocator is forced to store things in memory instead of just registers, that is known as a spill.

Does this boil down to a single processor instruction or is it more complex?

All we know for sure is that Intel documents a push and a pop instruction, so they are one instruction in that sense.

Internally, it could be expanded to multiple microcodes, one to modify esp and one to do the memory IO, and take multiple cycles.

But it is also possible that a single push is faster than an equivalent combination of other instructions, since it is more specific.

This is mostly un(der)documented:


Pushing and popping registers are behind the scenes equivalent to this:

push reg   <= same as =>      sub  $8,%rsp        # subtract 8 from rsp
                              mov  reg,(%rsp)     # store, using rsp as the address

pop  reg    <= same as=>      mov  (%rsp),reg     # load, using rsp as the address
                              add  $8,%rsp        # add 8 to the rsp

Note this is x86-64 At&t syntax.

Used as a pair, this lets you save a register on the stack and restore it later. There are other uses, too.


Almost all CPUs use stack. The program stack is LIFO technique with hardware supported manage.

Stack is amount of program (RAM) memory normally allocated at the top of CPU memory heap and grow (at PUSH instruction the stack pointer is decreased) in opposite direction. A standard term for inserting into stack is PUSH and for remove from stack is POP.

Stack is managed via stack intended CPU register, also called stack pointer, so when CPU perform POP or PUSH the stack pointer will load/store a register or constant into stack memory and the stack pointer will be automatic decreased xor increased according number of words pushed or poped into (from) stack.

Via assembler instructions we can store to stack:

  1. CPU registers and also constants.
  2. Return addresses for functions or procedures
  3. Functions/procedures in/out variables
  4. Functions/procedures local variables.

참고URL : https://stackoverflow.com/questions/4584089/what-is-the-function-of-the-push-pop-instructions-used-on-registers-in-x86-ass

반응형