📜 ⬆️ ⬇️

Calling a function with an “unknown” name in C ++. Part 1 - cdecl

Formulation of the problem


What did I mean when I wrote the “unknown” function name? This means that the function name, its parameters and, finally, the calling convention, become known only during the execution of the program. Let's take her call! =)

Now we will try to call the function according to the cdecl standard.
Excerpt from Wikipedia:
CD system for the x86 architecture. In cdecl, right-to-left order. The EAX register (except for the floating point values) has been returned. Registers EAX, ECX, and EDX are available for use in the function.

In general, parameters are passed through the stack in the reverse order, the resulting value will be in EAX except for floating-point numbers — they will be in the x87 pseudo-stack.

Make a plan of work:
1) Generate a buffer in memory that can be unchanged, word for word (4 bytes) pushed onto the stack.
2) Find out the address of the function that will be called
3) Put a word buffer on the stack.
4) Call the function
5) pull the result

Go!


')
What we have:
1) char * sName - here is the name of the function
2) int N - the number of parameters
3) enum CParamType {cptNone = 0, cptPointer, cptInt, cptDouble} - possible data types - for now let's confine ourselves to these
4) CParamType Params [] - list of parameter types
5) void * ParamList [] - in fact, pointers to variables with parameters
6) CParamType RetType - result data type
7) void * Ret - a pointer to the memory where you want to throw the result
8) enum CCallConvention {cccNone = 0, cccCDecl, cccStdCall, cccFastCall} - types of calling conventions
9) CCallConvention conv - calling convention. First we will call only cdecl functions

This is a necessary and sufficient list of ads that we need to call.
C / C ++ does not have the means to perform this operation, so you have to turn to assembler.

1. Create a buffer


First, let's count the number of words. Everything is simple - void *, int - 4 bytes - 1 word, double - 8 bytes - 2 words.
Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  1. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  2. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  3. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  4. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  5. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  6. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  7. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  8. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  9. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  10. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  11. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  12. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  13. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }
  14. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }


Counted. Memory allocation:
void* Buffer = new char[4*WordCount];

Fill the buffer: void *, int - put it unchanged, and swap words in double.
Copy Source | Copy HTML
  1. int offset = 0 ;
  2. double x;
  3. for ( int i = 0 , i <n, i ++)
  4. {
  5. switch (Params [i])
  6. {
  7. case cptPointer:
  8. case cptInt:
  9. * ( int *) (buf + offset) = * (( int *) (ParamList [i]));
  10. offset + = 4 ;
  11. break ;
  12. case cptDouble:
  13. x = * (( double *) (((DTMain *) (v-> T)) -> pData));
  14. memcpy (buf + offset + 4 , & x, 4 );
  15. memcpy (buf + offset, ( char *) & x + 4 , 4 );
  16. offset + = 8 ;
  17. break ;
  18. }
  19. }


I think there is nothing to comment on. offset - offset buffer.

2. We learn the function address


It's all quite simple.
void* addr = dlsym(NULL,sName);
Where the first parameter is the library descriptor. NULL to search in current context.
We connect dlfcn.h and do not forget to add -ldl to the linking parameters.

3. We put the buffer on the stack by words


Fuh. The most interesting.
To work with the stack, we naturally need an assembler. I use the gnu compiler, so the assembler with the AT & T syntax does not kick my feet, I don’t really like it myself, but I don’t have to choose.
Copy Source | Copy HTML
  1. asm ( "\ <br/> movl $ 0, %% eax; \ <br/> movl% 2, %% ebx; \ <br/> movl% 3, %% ecx; \ <br/> l1: cmpl% % ecx, %% eax; \ <br/> je l2; \ <br/> pushl (%% ebx, %% eax, 4); \ <br/> addl $ 1, %% eax; \ <br/> jmp l1; "
  2. : "= r" (b)
  3. : "r" (addr), "r" ( Buffer ), "g" (WordCount)
  4. : "% eax"
  5. );


We do a cycle: until ecx (WordCount) becomes 0, put the word on the stack and reduce ecx.

4. Call the function



Do
l2: call *%1;
after filling the stack. % 1 is a pointer to the function (addr).

5. Return the result



There are 2 options: whole result or fractional. According to the agreement, by default the result will be in% eax, but if with a floating point, it will be x87 in the vsevdo-stack.
1) Whole result
movl %%eax, %0;
where% 0 is the result variable.

2) Floating point option
The idea here is to remove the answer from the ST (0). So far I have not managed to do this. I would like to see possible solutions in the comments. Thank you in advance.

Well that's all! The task was really not trivial. I hope someone will need this post.

PS You need all this to write an interpreter.
_________
The text was prepared in Habra Editor

UPD: Highlight the source

Source: https://habr.com/ru/post/78886/


All Articles