Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a pointer to an assembly function

Tags:

x86

assembly

att

I'm rather new to assembly programming. I'm using the x86 platform with GCC (Linux).

I have a function that I want to call from C as:

myfunc ( unsigned char * s1, unsigned char * s2, int someint );

The function will take the s1 and s2 memory locations and compare them, then increment and compare, etc., doing some processing as it goes. This is sort of like memcmp but I'm doing more.

My question: if I pass a pointer into an assembly function? And then how do I say "give me the value stored at this memory address"?

Here's what I have so far:

To get the first function arg ("s1") off the stack, I do this (someaddress is a 32-bit integer, and I'm working on a 32-bit processor):

movl  8(%esp), %ecx
movl  %ecx, someaddress

If I put somevar into %eax (or %ebx, etc.) and then printf it with %p, I see that its address and the address of the unsigned char pointer "s1" I passed it are the same. But I suspect that what I've actually done is taken the memory address, converted it to an integer, and then put that integer in someaddress.

For example, if I then do this:

movl  pos1, %eax
movl  pos2, %ebx
cmp   (%eax),(%ebx)

I get "Error: too many memory references for `cmp'". I'm not entirely certain what that means, except "you screwed up" ;-)

So...

  • how to pass a pointer in and keep it as a pointer?
  • how to use the value of said pointer in assembly? (e.g., like *ptr in C)

Do I want to look at the LEA operand?

I'm using Richard Blum's "Professional Assembly Programming" as my guide, but Blum doesn't seem to cover this case.

Update

Thank you very much for your learned response!

Unfortunately, I'm still not able to dereference.

Here is a simplified example. The assembly function takes a pointer and should echo it back. Instead I get:

first_ptr points to 81 (should be 81) <-- from C program
the value is -1543299247 <-- printf called from within assembler
the value is -6028513 <-- printf called from within assembler
my function returned -6028513 <-- return value printed from C program

C Program:

#include <stdio.h>
#include <string.h>

int main (void) {
        unsigned char first;
        unsigned char * first_ptr;

        first = 'Q';
        first_ptr = &first;

        printf ("first_ptr points to %i (should be 81)\n",*first_ptr);

        printf ("my function returned %i\n", myfunc(first_ptr));
        return 0;
}

Assembly program:

.section .data

msg:
  .asciz "the value is %i\n"

.section .bss
.lcomm str, 8

.section .text
.type myfunc, @function
.globl myfunc
myfunc:

  # save stack
  pushl %ebp
  movl  %esp, %ebp

  # save string arg from stack to "str"
  movl  8(%esp), %ecx
  movl  %ecx, str

  # let's try printing the ecx dereference

  pushl (%ecx)
  pushl $msg
  call printf

  # put the value of str on the stack 
  # and call printf

  pushl (str)
  pushl $msg
  call printf

  # now return the character at pos1
  movl  (str), %eax

  # restore the stack
  movl  %ebp, %esp
  popl  %ebp

  ret
like image 218
raindog308 Avatar asked Mar 04 '11 00:03

raindog308


People also ask

Can you pass a pointer to a function?

Pass-by-pointer means to pass a pointer argument in the calling function to the corresponding formal parameter of the called function. The called function can modify the value of the variable to which the pointer argument points. When you use pass-by-pointer, a copy of the pointer is passed to the function.

How do function pointers work in assembly?

In assembly, a function pointer is just a normal pointer. You call the function with "call" as usual. (Try this in NetRun now!) Since this function takes no parameters, the function can't tell anything about the struct it's running from.

How are pointers implemented in assembly?

Pointers in assembly language have much simpler syntax: BYTE [rax] means go out to memory and grab one byte at the address stored in register rax. That address is always measured in bytes, and is called a "pointer", but it's just a number in rax.

How do you pass a pointer to a function in C++?

C++ allows you to pass a pointer to a function. To do so, simply declare the function parameter as a pointer type.


1 Answers

At least one of the operands to cmp has to be a register. If you're trying to compare the contents of two memory locations, you'll need to put one of them in a register. How to get it into a register you ask? Well you've done just that already with your example code. This line:

movl  8(%esp), %ecx

Takes the 4 bytes at %esp+8 and puts them into %ecx. In a C-like psuedocode:

ecx = *(esp + 8);

Hopefully that makes sense. You can do similar operations to get your pointers off of the stack and into registers, and then dereference them, compare the dereferenced values, and so on. Let me know if you have more questions!

Edit - your broken out questions:

  1. how to pass a pointer in and keep it as a pointer?

    You're already doing that, and your movl 8(%esp), %ecx instruction, or something like it will do everything you need.

  2. how to use the value of said pointer in assembly? (e.g., like *ptr in C)

    You need to use the () again - to load the first byte out of the pointer in %ecx from your instruction above, for example:

    movb (%ecx), %edx
    

    In the C-like pseudocode similar to how I used it above, this instruction is:

    edx = *(unsigned char *)ecx;
    
  3. Do I want to look at the LEA operand?

    Probably not, based on the description of your problem you've provided. It is always possible, though. lea works something like like the & operator in C. As an example, this instruction:

    lea 12(%ecx), %edx
    

    can be translated into our pseudocode as:

    edx = &(*(ecx + 12))
    

    or more simply:

    edx = ecx + 12
    

    This example is a bit silly, since we're using a relatively uncomplicated addressing mode, but how about something like this:

    lea 1(%edx,%ecx,4), %eax
    

    which means:

    eax = &(edx[ecx * 4] + 1)
    

Often the easiest solution to these sorts of problems is to write your routine in C, then compile it and disassemble the results.

Edit 2:

Your example program seems almost right, but you're trying to dereference pointers in memory - get those pointers into registers first and you should be ok.

like image 161
Carl Norum Avatar answered Sep 24 '22 10:09

Carl Norum