0

This question is related to this question: How can I access arguments 7+ using inline assembly?

I understand that accessing the pointers myself is non-standard, potentially unsafe, and not portable.

That being said, I'd like to get the stack pointer %rsp and transfer its value into a C variable. Is it possible to do this with the following command?

int main()
{
  void extra_args;
  __asm__("mov %%rsp, %0": "=m"(extra_args));
  return 0;
}

I've tried this, but it doesn't seem to point to the right place. Does the above command actually transfer %rsp to the extra_args variable?

phuclv
  • 37,963
  • 15
  • 156
  • 475
Connor
  • 867
  • 7
  • 18
  • The code does not compile, you have a `void` variable. Please provide a minimal complete verifiable example that demonstrates _why_ it "doesn't seem to point to the right place". – yeputons Apr 16 '23 at 13:46
  • 2
    Code like this should get you the stack pointer, but it's not very useful as the size of the stack frame is unpredictable. Instead use [`__builtin_frame_address`](https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html) to get the frame pointer, which always points to the top of the stack frame. – fuz Apr 16 '23 at 14:08
  • @fuz I'm afraid that doesn't work either! It's a coding challenge and the `builtin`s have been switched off! – Connor Apr 16 '23 at 14:14
  • 2
    @Connor Then there is no reliable way I know of to make it work, short of writing the entire function in assembly. Historically, you could also take the address of the leftmost function argument, but that's no longer guaranteed to be on the stack. – fuz Apr 16 '23 at 14:32
  • 1
    Assuming you meant `void* extra_args` then yes. `rsp` will be stored in `extra_args`. Since this is a code challenge you probably are allowed to write code tailored for a specific compiler. [godbolt.org may prove a valuable tool](https://godbolt.org/z/K5f8Kzr8P). – Margaret Bloom Apr 16 '23 at 18:50
  • 2
    `void *local_var = &local_var;` is one way to get a pointer to somewhere inside your function's stack frame. As fuz says, there's no reliable way to get the value or RSP as it existed before the function prologue. Before your asm statement runs, the compiler will usually modify RSP by some amount to reserve space for locals. Or maybe not in this case as it can keep `void *extra_args` in the red-zone below RSP. And with optimization enabled, it wouldn't need to `push` anything. – Peter Cordes Apr 17 '23 at 01:40
  • 1
    If you're doing silly stuff with debug builds only, then just read RBP and access relative to it. Assume the compiler will use RBP as a frame pointer. (You can make that more likely by using `alloca` or a variable-length array in the function, and use `__attribute__((noinline))` so even with optimization, your frame pointer is very likely to be pointing to the traditional location in a stack frame for a stand-alone asm implementation of this C function. But you'd only ever do that for a hacky use-case like you describe where your source has to get compiled by an online judge, not for real.) – Peter Cordes Apr 18 '23 at 02:30
  • Thank you for coming back to this over and over Peter! That sounds like a great idea! – Connor Apr 18 '23 at 15:12
  • @PeterCordes Unfortunately, neither of those things have worked. For whatever reason, the arguments aren't being passed onto the stack! Do you have any idea where they might be put otherwise? If not, then this challenge is starting to look impossible. – Connor Apr 23 '23 at 14:07
  • 1
    The 7th and later args are definitely on the stack, unless they're FP. Up to 6 integer/pointer *and* 8 floating-point args are passed in registers on x86-64 System V, the rest on the stack right above the return address. (Or for Windows x64, the 5th and later arg are on the stack above the shadow space, regardless of whether it's a mix of integer and FP.) – Peter Cordes Apr 23 '23 at 14:13
  • 1
    If you want to continue with this ridiculous exercise that's supposed to "teach you" about the stack by writing code that makes assumptions about compiler internals without being able to see the compiler-generated code to see where it puts stuff, you might want to write code that hex-dumps stack space. E.g. start with the address of a local `long` var (so it'll be aligned), or with RBP if you want to assume it's a frame pointer. Then `for (int i=0 ; i<16 ; i++) { printf(" %16x", ptr[i]); }` or something. – Peter Cordes Apr 23 '23 at 14:15
  • @PeterCordes I've been printing things out to see what's happening, which is why I think the arguments are not on the stack. However, I haven't used the `ptr[i]` notation. Is that better than using `*ptr`? Will it make a difference? Because otherwise, I've been printing things out to check, and I can't see the values on there! What does being aligned mean? The ptr will jump the correct amount along each time? Why do you think `%rbp` isn't a pointer? When I assigned the VLA, I could clearly see the values contained between `%rbp` and `%rsp`. – Connor Apr 23 '23 at 14:24
  • 1
    If you compile with optimization enabled (and don't use a VLA or over-aligned local var), GCC won't use RBP as a frame pointer. That's part of what makes this assignment ridiculous, teaching you a historical thing. [x86\_64 : is stack frame pointer almost useless?](https://stackoverflow.com/q/31417784) (yes). Also [Are there different ways to set RBP? Why does Windows x64 not point RBP at a saved-RBP right below the return address?](https://stackoverflow.com/q/75722486) since I don't remember your question specifically say anything about using the x86-64 System V ABI. – Peter Cordes Apr 23 '23 at 21:59
  • 1
    `ptr[i]` is *exactly* equivalent to `*(ptr+i)` in C. Neither is better. Being aligned by 8 means being a multiple of 8. i.e. the low 3 bits of the address are zero. If you dump in 8-byte chunks starting at an address that isn't a multiple of 8, none of those 8-byte chunks will be the integer value of a stack arg. RBP itself will be aligned, if it's being used as a frame pointer. – Peter Cordes Apr 23 '23 at 22:00

0 Answers0