2

Sometimes I use the following code to avoid stack overflow when taking part in coding competition.

int main()
{
  static const int _STACK_SIZE = MAXN*10;
  static int _STACK[_STACK_SIZE*2], _ESP;
  __asm__ __volatile__
  (
      "movl %%esp, %0\n" 
      "movl %1, %%esp\n":
      "=g"(_ESP):
      "g"(_STACK + _STACK_SIZE):
  );


  // Do Something..


  __asm__ __volatile__
  (  
      "movl %0, %%esp\n":
      :
      "g"(_ESP):
  );
}

As far as I know, this asm code backups %esp and moves the stack to _STACK[].


My Question: Why this code cause SIGSEGV on a x86-64 Linux Server(It runs well on my own x86 Linux)? And how to fix it?

I guess, maybe it's because %esp is a 64-bit pointer??

I tried to delete the __asm__ __volatile__("movl %0, %%esp\n": : "g"(_ESP):); and it seems runs well?

nrz
  • 10,435
  • 4
  • 39
  • 71
abcdabcd987
  • 2,043
  • 2
  • 23
  • 34

1 Answers1

4

It's because in x86-64 assembly modifying a 32-bit register such as esp zeroes the highest 32 bits of the corresponding 64-bit register, rsp in this case. In x86-64 the stack pointer rsp is a 64-bit register.

x86-64 Linux always puts the stack pointer near the top of the user-space range of virtual address space, like 0x7fffffffe6d0 for example, so it's always outside the low 32 bits of virtual address space. The high half of RSP is non-zero and ESP != RSP.

So by modifying esp you make rsp point to an address where your program has no access rights, and thus cause a segmentation fault. In x86-64 code you don't normally use esp at all, and you should replace all instances of esp with rsp in your x86-64 code.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
nrz
  • 10,435
  • 4
  • 39
  • 71
  • 2
    Thanks! Got it! So, Change `esp` to `rsp` && `int` to `long` ? – abcdabcd987 Mar 27 '13 at 11:00
  • 1
    "The problem should occur only in computers that have less that 4 GiB of total memory" You mean "_more than_ 4 GiB"..? – Michael Mar 27 '13 at 11:02
  • 4
    Standard myth about demand-paged virtual memory operating systems. You are mixing up "total memory" with the size of the virtual memory address space. The latter is *much* greater, the amount of RAM on the machine is irrelevant. – Hans Passant Mar 27 '13 at 11:04
  • @Michael Obviously, thanks. "more" and "less" and so difficult concepts ;) – nrz Mar 27 '13 at 11:04
  • @HansPassant I meant virtual memory address space (including swap), but didn't catch up the right word. Thanks, corrected as I meant it. Feel free to correct it if my wording is still inadequate. – nrz Mar 27 '13 at 11:07
  • 1
    @abcdabcd987 In Linux `long` should be the correct type, you may want to check also http://stackoverflow.com/questions/384502/what-is-the-bit-size-of-long-on-64-bit-windows – nrz Mar 27 '13 at 11:08
  • 2
    @abcdabcd987 - changing to `rsp` is still inadequate. The x86-64 ELF ABI specifies a 16-byte aligned stack. Furthermore, gcc isn't aware of the changes to stack, so any use of `rbp` (the frame pointer) may fail. – Brett Hale Mar 27 '13 at 11:16