[SOLVED] Stumped while learning ASM. I know what to do to make it work, but can't grasp why.
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Stumped while learning ASM. I know what to do to make it work, but can't grasp why.
Hi everybody.
I am learning x86_64 Linux assembly language. I got through the basics, making syscalls, comparisons and conditional jumps, accessing memory, registers, all that stuff. However, now I am trying to learn to write programs which use external libraries. Calling other functions is no problem, but when another piece of code is calling my function, that is where I am confused.
According to the amd64 ABI and Linux calling convention, I need to save some specific registers including RBP and RSP, which are the stack base pointer and stack pointer.
But if I don't use the stack, and all the functions that I call are responsible to save those registers correctly, shouldn't everything work if I never push or pop anything or modify those registers?
Even if something goes wrong, I think it would segfault on the ret instruction, but that is not what happens, my program segfaults at the first function call instead. I can't understand why that happens.
Also, I can get everything to run properly by simply pushing RBP onto the stack at the beginning of the function and popping it off at the end. So does the segfault have something to do with the state of the stack?
I have been searching for the answer, and some resources come close, but nothing explains why that needs to be done.
The best one I found so far was this: http://cs.nyu.edu/courses/fall11/CSC.../x64-intro.pdf
It almost explains it, but doesn't quite get there.
Also, compiling a small C program and examining the resultant ASM code shows the same thing: PUSH RBP and then set RBP to RSP. Which I think I understand, but I can't figure out why it segfaults before the RET instruction if I don't do that.
But if I don't use the stack, and all the functions that I call are responsible to save those registers correctly, shouldn't everything work if I never push or pop anything or modify those registers?
Yes. If you never modify those registers (even if you do use the stack) then you don't need to save/restore them. If other functions you call modify those registers, it is their responsibility to restore them.
Quote:
my program segfaults at the first function call instead. I can't understand why that happens.
At the function call? Or do you mean somewhere inside that function?
Quote:
Also, I can get everything to run properly by simply pushing RBP onto the stack at the beginning of the function and popping it off at the end. So does the segfault have something to do with the state of the stack?
Sounds like stack alignment. In x86_64, you shouldn't simply call another function on entry to your function. The call into your function misaligned the stack by 8 bytes. You need to push another 8 bytes (or any odd multiple of 8 bytes) onto the stack before calling another function.
Quote:
I can't figure out why it segfaults before the RET instruction if I don't do that.
Very few functions would crash due to stack misalignment. Some would just run slower. Others wouldn't care at all. But I don't have a guess other than stack misalignment based on your description. If stack misalignment is the problem, it should crash deep inside the other function you called on some operation that depends on stack alignment.
You "fixed" the problem by saving and restoring rbp, creating what I think is an illusion that you needed to save/restore rbp. Try pushing/popping r11 instead of rbp. We know nothing cares about saving/restoring r11, so when that also "fixes" the problem, you have pretty much confirmed it was stack alignment.
Yes. If you never modify those registers (even if you do use the stack) then you don't need to save/restore them. If other functions you call modify those registers, it is their responsibility to restore them.
Ok, sounds good.
Quote:
Originally Posted by johnsfine
At the function call? Or do you mean somewhere inside that function?
Probably inside the function.
Quote:
Originally Posted by johnsfine
Sounds like stack alignment. In x86_64, you shouldn't simply call another function on entry to your function. The call into your function misaligned the stack by 8 bytes. You need to push another 8 bytes (or any odd multiple of 8 bytes) onto the stack before calling another function.
Stack alignment. Good call. You are sharp my friend.
Quote:
Originally Posted by johnsfine
Very few functions would crash due to stack misalignment. Some would just run slower. Others wouldn't care at all. But I don't have a guess other than stack misalignment based on your description. If stack misalignment is the problem, it should crash deep inside the other function you called on some operation that depends on stack alignment.
After reading your comment, I did some research and found that some SSE instructions will segfault if the stack is misaligned. I am calling SDL, which is built with SSE and SSE2 instructions enabled. So that sounds plausible. Awesome.
Quote:
Originally Posted by johnsfine
You "fixed" the problem by saving and restoring rbp, creating what I think is an illusion that you needed to save/restore rbp. Try pushing/popping r11 instead of rbp. We know nothing cares about saving/restoring r11, so when that also "fixes" the problem, you have pretty much confirmed it was stack alignment.
I think you are right. I did try your suggestion of using r11 instead and it worked fine, so think your answer is correct. Thank you. I understand much better now.
However, this seems a bit awkward now, I need to align the stack by pushing some random data onto, even though I don't need the data. Is there a more graceful way to align the stack?
Personally I prefer push/pop for stack alignment (preferably with a comment saying just for stack alignment).
Maybe you think it is more graceful to use
Code:
sub $8,%rsp
...
add $8,%rsp
That is a valid opinion if you like.
Sometimes that code is also faster than push/pop but very unlikely to make a difference that matters and there are many factors (instruction alignment etc.) that might overwhelm the superficial time difference and even reverse which version is slightly faster.
I haven't used x86 assembly in a while, but you don't align a stack by adding a constant value to it - what if it's already aligned? You usually OR the stack pointer - e.g. OR it with 0xfffffff0 to align a 32 bit stack pointer by 16 bytes. And IIRC, you save %esp as %ebp on function entry for x86, so you shouldn't need undo what you've done at the end (again, my x86 assembler is rusty...).
Last edited by JohnGraham; 06-27-2013 at 03:18 PM.
I haven't used x86 assembly in a while, but you don't align a stack by adding a constant value to it - what if it's already aligned? You usually OR the stack pointer - e.g. OR it with 0xfffffff0 to align a 32 bit stack pointer by 16 bytes. And IIRC, you save %esp as %ebp on function entry for x86, so you shouldn't need undo what you've done at the end (again, my x86 assembler is rusty...).
Your discussion (excepting some details) applies when you want higher alignment than is standard for the environment in which your code will be run.
The situation of this thread was standard x86_64 ABI, in which the stack is 16 byte aligned before each call, so on entry to a function it is assumed to be 8 bytes off from 16 byte aligned. So you can subtract 8 from rsp in order to restore 16 byte aligned.
I haven't used x86 assembly in a while, but you don't align a stack by adding a constant value to it - what if it's already aligned? You usually OR the stack pointer - e.g. OR it with 0xfffffff0 to align a 32 bit stack pointer by 16 bytes.
If I have to restore rsp at the end, isn't it just as graceful to save rsp on the stack and pop it off at the end?
SUB and ADD is faster then PUSH and POP, and besides, even though PUSH RSP and POP RSP will work correctly, they look a bit weird (at least to me) since PUSH and POP respectively decrement and increment RSP register.
Your discussion (excepting some details) applies when you want higher alignment than is standard for the environment in which your code will be run.
The situation of this thread was standard x86_64 ABI, in which the stack is 16 byte aligned before each call, so on entry to a function it is assumed to be 8 bytes off from 16 byte aligned. So you can subtract 8 from rsp in order to restore 16 byte aligned.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.