NO

Author Topic: Missing return in __declspec(naked) functions  (Read 19652 times)

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« on: February 08, 2005, 02:19:55 PM »
Hi Pelle,
Look this code  :? :
Code: [Select]
/* FILE: naked.c */

int t,v;

__declspec(naked) int __fastcall Tnaked1(int, int);
__declspec(naked) int __fastcall Tnaked(int, int);

__declspec(naked) int __fastcall Tnaked(int k, int m)
{
t=k;
v=m;

return 1;
}

int __stdcall XlatChr(int code, int flags)
{
if ( code > 256 )
return 0;

// t=Tnaked(1,2); //enable one of this and you miss both returns
// t=Tnaked1(1,2);

return code*2;
}

__declspec(naked) int __fastcall Tnaked1(int key, int mode)
{
t=key;
v=mode;

return 1;
}

If you compile with:
Quote
pocc /Ze /Zl /Ot naked.c

Then look the disassembly with
Quote
podump /disasm naked.obj

In the first function, "Tnaked", the return instruction is missing  :-s , while in the second we have a strange
Quote
ret 8
:(
Last if you call one of the functions both ret's disappears  :cry: .
Any Idea?
F.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #1 on: February 08, 2005, 03:34:08 PM »
Pelle,
look what happen if you add a ret instruction via __asm keyword
Code: [Select]
/* naked.c with ret */

int t,v;
__declspec(naked) int __fastcall Tnaked1(int, int);
__declspec(naked) int __fastcall Tnaked(int, int);

__declspec(naked) int __fastcall Tnaked(int k, int m)
{
t=k;
v=m;

return 1;
__asm ret;
}

int __stdcall XlatChr(int code, int flags)
{
if ( code > 256 )
return 0;

// t=Tnaked(1,2); //enable one of this and you miss both returns
// t=Tnaked1(1,2);

return code*2;
}

__declspec(naked) int __fastcall Tnaked1(int key, int mode)
{
t=key;
v=mode;

return 1;
__asm ret;
}

 :?  ????
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline Pelle

  • Administrator
  • Member
  • *****
  • Posts: 2266
    • http://www.smorgasbordet.com
Missing return in __declspec(naked) functions
« Reply #2 on: February 08, 2005, 09:15:29 PM »
__declspec(naked) creates a completely empty function, and is supposed to be filled only with a __asm block. Using a ret instruction in the __asm block should work, using a return statement will give unpredictable results. I don't think I will accept return statements in 'naked' functions in the future. Assignments should work, but a __asm block is the preferred way with 'naked' functions...

Pelle
/Pelle

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #3 on: February 09, 2005, 09:56:24 AM »
Hi Pelle,
Sorry I was not very clear in my description, I'll try to explain betterder.
Bill defines the naked functions as standard functions where are simply missed the prologue and epilogue. Some statements are not allowed as: return, and also some stack manipulation functions like alloca.
This means that the compiler have to generete errors for not allowed statements, and locally define a constant "__LOCAL_SIZE" which value is the space allocated on the stack for local variables to be used to clean stack before exit.
The problem with POCC is that the return statement is sometime generated, in wrong way, and the function arguments are used in non consistent way respect the function declaration (if you declare the function __fastcall happen that the parameters are considered on the stack and the reverse). All this is showed in the first sample code.
But what is worst is that, as showed in the second sample code, if you define your return via __asm statement, the compiler generates a jump over the return instructions  :? !!!
I plain words it seems that the code generated for naked functions is absolutely casual  :( .
I discovered this becouse using the compiler for low level code I needed that possibility, but experienced a lot of errors.
Anyway if you could fix it this will help use of compiler for windows drivers and so kind of code.
Cordially
F.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline Pelle

  • Administrator
  • Member
  • *****
  • Posts: 2266
    • http://www.smorgasbordet.com
Missing return in __declspec(naked) functions
« Reply #4 on: February 09, 2005, 03:33:56 PM »
Hello Frankie,

OK - I see. My only ambition with __declspec(naked), so far, is to generate an empty function that should only be filled with a __asm block, and nothing else. The __asm block should contain all instructions, including the ret instruction. Used in this way it works, AFAIK.

The compiler would be more MS compatible if I added support for C statements in __declspec(naked) functions, and I should do it, but so far all the cases that needed the 'naked' attribute also needed a specialized function body, so a __asm block seemed like the only option.

Pelle
/Pelle

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #5 on: February 09, 2005, 06:38:01 PM »
Pelle,
maybe that it's more simple than what we can immagine.
The following link explains MS definition:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_core_rules_and_limitations_for_naked_functions.asp
As far as I can understand everything remains equal to standard functions, but epilogue and prologue are not emitted.
Moreover return and setjump and alloca are not permitted as is not permitted to initialize variables in the main scope, while they are permitted in nested scopes.
Of course the space allocated for local variables should be assigned in "__LOCAL_SIZE" so the user could use it in tailored init/closing sequences.
I see that this is a very unsecure feature, where is at your option, with __asm blocks, to initialize and close the function code.
As you can see is probably the only mode to handle in "C" interrupt or exception code, or very particular procedures.
In short what I understood is: don't emit prologue/epilogue, code as a standard "C" function, check for not allowed statements/calls and variables initialization and all the rest is coder responsability.

looking forward for your considerations.
F.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline Pelle

  • Administrator
  • Member
  • *****
  • Posts: 2266
    • http://www.smorgasbordet.com
Missing return in __declspec(naked) functions
« Reply #6 on: February 10, 2005, 01:16:05 AM »
Right now I reject _alloca() and SEH (__try, __except ...) for naked functions. I should also reject return statements since they by default jump to the epilogue code - which is not emitted. Initializers at function scope should also be rejected. This should be done to be consistent.

Not sure how to handle naked fastcall functions. Incoming arguments must be stored on the stack like in the MS compiler, but this seems somewhat useless. Otherwise the registers must be processed from assembly code, in which case we are back to __asm blocks.

The __LOCAL_SIZE causes several problems since I have to pass it around long enough to know the size of local variables - this is calculated long after the compiler sees the inline code. This is not trivial.

At the moment, register variables are most likely turned off in naked functions. Not sure how to inform the programmer which registers to preserve (of EBX, EDI, ESI). Assuming the worst might be OK for interrupt functions, but maybe not in other cases. Back to __asm blocks again?

Pelle
/Pelle

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #7 on: February 10, 2005, 11:36:40 AM »
Pelle,
Bill says:
Quote
The naked attribute affects only the nature of the compilers code generation for the functions prolog and epilog sequences. It does not affect the code that is generated for calling such functions. Thus, the naked attribute is not considered part of the functions type, and function pointers cannot have the naked attribute. Furthermore, the naked attribute cannot be applied to a data definition.

(Link: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang98/html/_clang_naked_functions.asp)
As far I can understand we don't have to worry about parameters and local variables handling, just produce code as per the specific function definition, the duty of placing the values at the right place is a programmer problem. It's coder responsability, in the custom epilog code, to move the function parameters to the right registers, if __fastcall convention declared, or on the stack, if _cdecl or __stdcall. Also the cleaning duty upon function exit is programmer responsability, He/she have to clean around and make code consistent.
As I have already told to me this is a very unsafe option, where the user knows that the compiler control is limited and that any problem remains to user charge.
The compiler could check just some basic errors, as you have already done, all the rest is programmer responsability (to cross-check with assembler generated code and similiar low level tracing).
Moreover in the documentation you will see that is uncorrect to put the naked attribute in the function prototype, meaning that when you declare a naked functions you can choose how the "C" code will handle the parameters, the other points: register and context saving, stack consistency, cleaning and so on are programmer responsability.
The programmer when declare a naked function knows what the generated "C" code will expect for parameters and variables handling, and MUST insert code for this requirements. The reverse is not true: the code generator CANNOT know from where, and in which ways, parameters, stack and context will be organized, so simply don't care about.
As an example immagine following function as an exception handler:
[code__declspec(naked) void __fastcall PageFault(DWORD address, DWORD code)
{
   /*
      USER PROLOGUE
   */
   __asm pusha
   __asm push ebp;
   __asm mov ebp,esp;
   __asm sub esp,8
   __asm mov ecx,cr2   //page fault address
   __asm mov edx,[ebp+ERROR_CODE]
   
   /*
      USER CODE
   */
   switch (code)
   {
      case 0:
         ...............
         break;
      case 1:
         ...............
         break;
      ...................
      ...................
      default:
         ...............
         break;
   }

   /*
      USER EPILOGUE
   */
   __asm mov esp,ebp
   __asm pop ebp
   __asm popa
   __asm add esp,+4   //Skip error code
   __asm iret   
}
[/code]
It is surely not correct becouse I wrote it on the fly but should give you the sense.
Thank-you
F.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #8 on: February 10, 2005, 12:02:47 PM »
Sorry,
More readable now:
Code: [Select]

__declspec(naked) void __fastcall PageFault(DWORD address, DWORD code)
{
/*
USER PROLOGUE
*/
__asm pushal
__asm push ebp;
__asm mov ebp,esp;
__asm sub esp,8
__asm mov ecx,cr2 //page fault address
__asm mov edx,[ebp+ERROR_CODE]

/*
USER CODE
*/
switch (code)
{
case 0:
...............
break;
case 1:
...............
break;
...................
...................
default:
...............
break;
}

/*
USER EPILOGUE
*/
__asm mov esp,ebp
__asm pop ebp
__asm popal
__asm add esp,+4 //Skip error code
__asm iret
}
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #9 on: February 10, 2005, 01:06:01 PM »
Pelle,
I forget to expose my understanding about the __stacall function declaration. In my opinion it is useful when the function access data on the stack previously pushed by other functions and/or processor mechanisms.
As a sample consider again the previous sample code for the page fault exeption, we could rewrite it as:
Code: [Select]

__declspec(naked) void __stdcall PageFault(DWORD eip, DWORD cs)
{
DWORD address, code;

/*
USER PROLOGUE
*/
__asm pushal
__asm push ebp;
__asm mov ebp,esp;
__asm sub esp,8
__asm mov [ebp-_address],cr2 //local variable
__asm mov edx,[ebp+ERROR_CODE]
__asm mov [ebp+_code],edx

/*
USER CODE
*/
switch (code)
{
case 0:
if ( eip > 0xff800000)
...............
break;
case 1:
if (cs != 8 )
...............
break;
...................
...................
default:
...............
break;
}

/*
USER EPILOGUE
*/
__asm mov esp,ebp
__asm pop ebp
__asm popal
__asm add esp,+4 //Skip error code
__asm iret
}

In this case we access the long return address (eip and cs) on the stack as parameters, and we init the local variables address and error code with the values get from exception stack.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #10 on: February 10, 2005, 05:47:24 PM »
Ok, I think we get it.
Look at the following links:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_core_Rules_and_Limitations_for_Naked_Functions.asp
http://www.microsoft.com/msj/0698/bugslayer0698.aspx
This definitely explain that the standard types for naked extended attribute are: _cdecl and __stdcall.
The __fastcall type could apply if you assign the register contents (ecx,edx) to the related stack arguments (something strange and also dangerous, if you don't have them preallocated on stack probably you will over write something  ](*,) .
So code inside a naked function must act as _cdecl or __stdcall, which means no difference at all considering that the difference between the two is who clean the stack on exit in the epilogue, but the epilogue is written by the programmer who can do wathever he wants.
Looking forward for your comments.
F.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline Pelle

  • Administrator
  • Member
  • *****
  • Posts: 2266
    • http://www.smorgasbordet.com
Missing return in __declspec(naked) functions
« Reply #11 on: February 10, 2005, 07:08:03 PM »
Quote from: "frankie"

So code inside a naked function must act as _cdecl or __stdcall, which means no difference at all considering that the difference between the two is who clean the stack on exit in the epilogue, but the epilogue is written by the programmer who can do wathever he wants.

The problem is that if you use __declspec(naked) together with __fastcall, and the C code in that function reference one of the first two arguments (in processor registers), I will most definitely generate the wrong code. This is not acceptable. I could do it like the Microsoft compiler, and assume that the users prologue stores ECX and EDX as the first two locals, and reference them as [ebp-4] and [ebp-8], but this seems both dangerous and somewhat stupid. The point with __fastcall is to make it somewhat faster by using registers rather than the stack. Here you are forced to use the stack anyway - I you want to use C code in the naked function. My feelings about this right now is to reject the combination __declspec(naked) + __fastcall - what do you think?

My second question to you: how useful is __LOCAL_SIZE? It's a bit of a problem implementing it - especially for two target machines (X86 and ARM).

Pelle
/Pelle

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #12 on: February 11, 2005, 09:49:00 AM »
Hi Pelle,
I agree with you about the naked + __fastcall, it seems stupid and very dangerous unless they (MS crew) know it is useful someway. As far as I can see I could not imagine a practical use, but we have to consider that such a construct is definetely created to handle low level code that inevitabily link with assembler special constructs. What I mean is that maybe a very special case in a very special condition, as processor exception, task switch or interrupt handling, such a case could be useful  :roll: .
The key question is: blindly follow Bill and keep the compiler compliant, or decide to move away when something seems really crazy   :x .
MS specify that these are their extensions, you are the creator of PELLESC and can consider to use your criticism to decide if adopt this  :-k , giving one step more to standardize what is actually a personal extension, or to drop it  [-X .
Of course the general form is obviously useful.
Cordially
F.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #13 on: February 11, 2005, 09:54:54 AM »
Pelle,
About the __LOCAL_SIZE, it is absolutely necessary, it have to be used in the prologue to commit local variables space as in the following MS sample:
Code: [Select]

// nkdfastcl.cpp
__declspec(naked) int __fastcall  power(int i, int j)
{
    /* calculates i^j, assumes that j >= 0 */

    /* prolog */
    __asm {
        push   ebp
        mov      ebp, esp
        sub      esp, __LOCAL_SIZE
       // store ECX and EDX into stack locations allocated for i and j
        mov   i, ecx
        mov   j, edx
    }
     
    {
        int k=1; // return value
        while (j-- > 0) k *= i;
        __asm { mov eax, k };
    }

    /* epilog */
    __asm  
    {
        mov      esp, ebp
        pop      ebp
        ret
    }
}

Consider that MS restrict the use of __LOCAL_SIZE only as immediate operand, not as a real constant, so you can use internally as keyword for the calculated local variables space (the same you use for standar prologue).
F.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Missing return in __declspec(naked) functions
« Reply #14 on: February 11, 2005, 12:55:08 PM »
Pelle,
I get just now aware that posting a sample use of __LOCAL_SIZE I get a __fastcall sample code  :lol: .
cheers
F.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide