-5

I have currently practiced about C language.

Everything seems reasonable but when I faced these initialization and function

unsigned long get_sp(void) {
    __asm__("movl %esp,%eax\n\t"
            "and $0xff000000, %eax"
           );
}

int  (*fp)(char *)=(int(*)(char *))&puts;

I dont really know what do these lines mean.

What is the real variable? what type is it? ...

Can someone explain me in depth?

Andrew
  • 3,272
  • 2
  • 25
  • 26
Yura
  • 17
  • 8
  • 1
    Possible duplicate of [How do function pointers in C work?](http://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work) – Failed Scientist Jun 06 '16 at 00:18
  • 2
    @TalhaIrfan : That question only answers half the question. The function `get_sp()` is not explained and IMHO not a duplicate of this. – Michael Petch Jun 06 '16 at 02:15
  • @MichaelPetch I think you are right Michael. I am editing the question to make it related to get_sp() – Failed Scientist Jun 06 '16 at 03:37
  • This is 2 totally separate questions, one about the function-pointer, and one about the stack-pointer function (which will break if it's ever inlined, because it doesn't declare `%eax` as an output). – Peter Cordes Jun 06 '16 at 06:03

2 Answers2

3

1st, you are defining a function get_sp() which returns an unsigned long. The content of that function is some inline assembly which gets the stack pointer address, puts it in register eax, and then ands it with 0xff000000. IE: gets a value in eax that has set any of the top 8 bits of the address of the stack pointer. The eax register is used for the return value, so this masked stack pointer is returned.

The 2nd line is assigning to fp the address of the function puts. puts is a function that returns an int, and expects a char * input. Hence the type/name int (*fp)(char *).

After that line, you could could call the puts function as fp("hello");

Nick Matteo
  • 4,453
  • 1
  • 24
  • 35
lostbard
  • 5,065
  • 1
  • 15
  • 17
2

The second part is just a function pointer with an initializer (casting &puts).

The first part is more interesting:

It gets %esp into a C variable, and masks off the low 24 bits. i.e. rounds down to a 16MiB boundary. IDK what this is for, but a 4B offset from a non-inlined function call might or might not matter (if %esp was very near a 16MiB boundary, or that's explicitly what you're trying to detect.)

The version posted in the question will break your code in non-obvious ways if the compiler ever gets a chance to inline it (e.g. with cross-file inlining). Instead of properly declaring an output operand from the asm statement, it just modifies %eax inside a function with no return statement. This is really dumb and has zero advantages compared to telling the compiler about what you're returning.

/********* Safe version of the function *************/
// Actually unsigned long was fine, since this asm only works on 32bit anyway
static inline uintptr_t get_sp(void) {
    uintptr_t result;
    __asm__("movl %%esp, %0\n\t"
            : "=g" (result)
           );
    result &= 0xff000000;  // do this outside the inline asm so the compiler knows that the low 24b are always zero.
    return result;
}

This compiles to the same asm when not inlined, but can be safely inlined. (e.g. put it in a header with static inline). It also of course avoids compiler warnings about functions with missing return values.

As Michael Petch points out in comments, making this an always-inline function, or even a macro, is probably a good idea for consistency. (Although optimized vs. un-optimized builds will consume different amounts of stack space anyway.)


See the tag wiki for more about how to write GNU C inline asm that doesn't suck.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Only comment about this is what value of _ESP_ is he looking for? for example what happens if this code is compiled without optimization. The function call itself will now have produced a return address and possible the previous value of _EBP_ on the stack (shifting ESP) if there is a stack frame present. I'd probably declare this as `static inline` or at worst a _C_ MACRO that is guaranteed to always be inlined. As well you sure about an output constraint of `=g` ? That would imply that `i` is valid, but you wouldn't be able to use an `i` (imm. value) as a target. Maybe `=rm`? – Michael Petch Jun 06 '16 at 15:14
  • 1
    @MichaelPetch: I considered the issue of what `esp` you'd get. Since `get_sp` masks off all but the high byte, I decided it probably didn't matter whether it was inlined or not. For the output constraint, I think gcc will always pick `r`, because it needs to mask the result. I should probably have just done that. I was thinking maybe in some circumstances it could just store `%esp` somewhere and not need to mask, like if it later loaded just the high byte. I don't think there's any risk that `i` could ever match an output-only constraint, so I didn't need to manually exclude it. – Peter Cordes Jun 06 '16 at 15:27
  • 1
    It is technically possible to be right on the boundary of 16MB alignment with _ESP_ in which case the extra pushes would yield a different value. Of course the likelihood that _ESP_ is near that boundary is slim but not necessarily impossible. I just didn't see anyone in an answer or comment that the 'AND` is effectively rounding the stack pointer down to the nearest 16MB boundary (alignment?). – Michael Petch Jun 06 '16 at 15:30
  • @MichaelPetch: good point, I updated my answer to talk some about what it's actually doing, and the near-a-boundary implications. Thanks. – Peter Cordes Jun 06 '16 at 15:52
  • No problem, I was already your upvoter before the changes. – Michael Petch Jun 06 '16 at 15:53
  • 1
    I suspect though that this code is part of a buffer exploit. I've seen it before. I don't think whoever is the source of the code has much need to know that the inline assembler is effectively crap and that they are just lucky it happens to work the way they want. – Michael Petch Jun 06 '16 at 15:54