Naked Function Calls In C++ - C++ Programming Tutorial
C++ Course / Functions / Naked Function Calls In C++

Naked Function Calls In C++

BLUF: Mastering Naked Function Calls In C++ is a critical step in becoming a proficient C++ developer. This lesson provides a deep dive into the syntax, performance considerations, and real-world applications of this concept.
Key Performance Insight: Naked Function Calls In C++

C++ is renowned for its efficiency. Learn how Naked Function Calls In C++ enables low-level control and high-performance computing in the tutorial below.

Hello all! In this session, we'll delve into the concept of Bare Function Invocations in C++. You might wonder why these are referred to as "naked" in the realm of C++. But before addressing that, let's first understand the essence of a function call.

Function Call in C++

Initiating the function and enabling its execution at a specific point in a C++ program is referred to as invoking the function in C++.

There are two categories of function invocations in C++:

  • Function Call with Argument

In this function invocation, we neither assign nor pass any arguments to the C++ Program. These inputs are essential for the program's execution.

  • Function Call without Parameters

In this function invocation, we either assign or pass parameters to the C++ program. These arguments are essential for the execution of the program.

Example

File name: withParameters1. cpp

Example

/* This is a program written to find the factorial of a number using function call which is parameterized or we can say that parameters are passed into the program */

#include <iostream>

#include <cstdio>

using namespace std;

int factorial (int number) 

{

    int i;

    int f=1;

    for(i = 1; i <= number; i++)

    {

        f = f * i;

    }

    return f;

}

int main()

{

    int n;

    cout <<"Enter any number: " << endl;

    cin >>n;

    int fact;

    fact = factorial(n);

    printf("%d is the factorial of %d",fact,n);

    return 0;

}

Input:

Example

Enter any number: 

10

Output:

Output

3628800 is the factorial of 10

Example

File name: withoutParameters1. cpp

Example

/* This is a program written to find the factorial of a number using function call which is not parameterized or no parameter passing */

#include <iostream>

#include <cstdio>

using namespace std;

void factorial () 

{

    int number;

    cout <<"Enter any number: " << endl;

    cin >>number;

    int i;

    int f=1;

    for(i = 1; i <= number; i++)

    {

        f = f * i;

    }

    printf("%d is the factorial of %d",f,number);

}

int main()

{

    

    int fact;

    factorial();

    

    return 0;

}

Input:

Example

Enter any number: 

15

Output:

Output

2004310016 is the factorial of 15

Naked Function Calls

When a function is defined with the naked attribute, it omits the generation of standard prolog or epilog code, enabling you to employ inline assembler to craft custom prolog/epilog sequences as needed.

An advanced capability is the inclusion of bare functions. These functions offer the ability to define a function that is invoked from a context other than C or C++, enabling you to make assumptions about the parameter location or register preservation in a unique manner.

Routines such as interrupt handlers serve as illustrations. Those developing virtual device drivers (VxDs) will particularly appreciate this feature for its convenience and ease of implementation.

The naked attribute is employed to define functions, where the resulting code does not contain prologue and epilogue sections.

Applying inline assembly code allows you to craft custom prolog/epilog code sequences. Naked functions are particularly beneficial when developing virtual device drivers. It's important to note that while the naked attribute is supported on x86 and ARM architectures, it is not compatible with the x64 platform.

Syntax

Example

_delspec (naked)

Naked functions are required to make use of the extended attribute syntax and the __declspec keyword since the naked attribute specifically impacts the declaration of a function and does not act as a modifier for types.

Even when a function is marked with the __force inline keyword and the naked attribute, the compiler is unable to generate an inline function for it.

If the naked attribute is applied to any context beyond defining a non-member method, the compiler will generate an error.

Examples

Example

_declspec (naked) float fun ( initial parameters ) { }

			Or

#define Naked _declspec (naked)

Naked float fun ( initial parameters ) { }

The naked attribute only affects the structure of the prolog and epilog sequences generated by the compiler.

It does not affect the code when calling these functions. Therefore, function pointers are unable to include the naked attribute as it is not considered part of the function's type. Furthermore, the naked attribute cannot be utilized in a data definition.

Rules and Limitations

Following are the rules and limitations of Naked Function Call in C++:

  • The return keyword cannot be used here in this type of function call
  • _alloca function cannot be used in this type of function calls
  • Initialized local variables are not allowed at function scope to guarantee that no initialization code for local variables enters before the prolog sequence. Particularly, function scope does not allow the definition of C++ objects. A nested scope could, however, include initialized data.
  • Naked Function Calls in C++ must unwind over the stack frame. So, C++ exception handling constructs and structured exception handling are not allowed.
  • Naked Function Calls in C++ must unwind over the stack frame. So, setjmp cannot be used for Naked Function Calls in C++.
  • Every time one of the register parameters for __fastcall bare functions is referenced in C/C++ code, the prolog code should save the value of that register onto the stack location for that variable.
  • If the Naked function call is in lexical scope, C++ class objects cannot be declared. But you are still allowed to define objects in nested blocks.
  • Although it is not advised, the frame pointer optimization (the / Oy compiler option) is automatically suppressed for a naked function.
  • When Naked Function Call building is done with / clr, the naked keyword can be ignored.

Example

Example

// nkdfastcl.cpp

// compile with: /c

// processor: x86

__declspec(naked) int __fastcall  power(int i, int j) {

   // This code is written to calculate the value of x EXOR y, assumes that j >= 0

   // prolog

   __asm {

      push ebp

      mov ebp, esp

      sub esp, __LOCAL_SIZE

// Place ECX and EDX in the x and y stack places.

     mov x, ecx

     mov y, edx

   }

   {

      int s = 1;   // return value

      while (y-- > 0)

         s * = x;

      __asm {

         mov eax, k

      };

   }

   // epilog

   __asm {

      mov esp, ebp

      pop ebp

      ret

   }

}

Ideas to Keep in Mind While Writing Prolog/Epilog Code

It is essential to grasp the organization of the stack frame prior to developing your custom prologue and epilogue code sequences. Familiarity with the implementation of the __LOCAL SIZE token can also be advantageous.

Stack Frame Layout

This diagram showcases standard Prolog code commonly employed in a 32-bit operation:

Example:

Example

push        ebp                    ; Save ebp

sub         esp, localbytes        ; Allocate space for locals

mov         ebp, esp               ; Set stack frame pointer

push        <registers>      ; Save registers

The variable "registers" serves as a placeholder representing the register list to be preserved on the stack, while the "localbytes" variable specifies the amount of stack space needed for local variables. Additional relevant data can be stored on the stack subsequent to pushing the registers. Below is the corresponding epilogue code snippet:

Epilog Code

Example

pop         <registers>   ; Restore registers

pop         ebp           ; Restore ebp

mov         esp, ebp      ; Restore stack pointer

ret                       ; Return from function

The stack decreases in size as it moves from higher to lower memory addresses. The value pushed onto the stack at ebp represents the location to which the base pointer (ebp) is pointing. The area where local variables are stored begins at ebp-4. To access local variables, determine the offset from ebp by subtracting the appropriate value from ebp.

LOCAL SIZE

For implementation within the inline assembler section of the function prolog code, the compiler provides a symbol known as __LOCAL SIZE. When crafting custom prolog code, this symbol serves the purpose of reserving memory for local variables within the stack frame.

The compiler determines the LOCAL SIZE value, which consists of the total of user-defined local variables and temporary variables generated by the compiler. LOCAL SIZE can only be used as an immediate operand and cannot be part of an expression. It is crucial to retain the original meaning of this symbol without any modifications or reinterpretations. For example:

Example

mov        eax, [ebp - __LOCAL_SIZE]   ;Error

mov        eax, __LOCAL_SIZE           ;Immediate operand--Okay

The __LOCAL SIZE symbol is utilized within the prolog sequence of the naked function, which implements distinct prolog and epilog sequences in the following manner:

Example

Example

// the__local_size_symbol.cpp

// processor: x86

__declspec ( naked ) int main() {

   int x;

   int y;

   __asm {      /* prolog */

      push   ebp

      mov      ebp, esp

      sub      esp, __LOCAL_SIZE

      }

   /* Function body */

   __asm {   /* epilog */

      mov      esp, ebp

      pop      ebp

      ret

      }

}

This tutorial focuses on Direct Function Invocations in the C++ Programming Language.

Input Required

This code uses input(). Please provide values below:

Logic Practice
Install Logic Practice
Add to home screen for a faster app-like experience