Get float value from drwrap_get_arg

38 views
Skip to first unread message

Balaji Balasubramaniam

unread,
Jul 8, 2024, 10:45:40 AMJul 8
to DynamoRIO Users
Hi Derek Bruening,

I just started using DynamoRIO; thanks for making this Dynamic Binary Instrumentation tool and for the continued support.

I am trying to retrieve a float value from a function argument. I am modifying the callstack.cpp file inside the wrap_pre to achieve this.

I reviewed your GitHub issue about the topic "add complex type support to drwrap_get_arg #1104". For your reference, this is the link I am referring to https://github.com/dynamorio/dynamorio/issues/1104

I have tried a few ways, also with the debug option, but so far, no luck. Let us say I have the below function:

float myFunc(float firstArg, float secondArg){
       //do something;
       return retArg;
}

In wrap_pre, my first approach was assuming I got the memory address. Below is the code.

static void wrap_pre(void *wrapcxt, OUT void **user_data)
{
app_pc firstArg_pc=(app_pc)drwrap_get_arg(wrapcxt, 0);
DR_ASSERT(firstArg_pc);
dr_fprintf(STDERR, "firstArg:%f \n", *(float*)firstArg_pc);
//tried a few other prints with no luck
//dr_fprintf(STDERR, "firstArg:%f \n", firstArg_pc);
}

Another approach I tried using was the casting of void pointer as follows:

static void wrap_pre(void *wrapcxt, OUT void **user_data)
{
void* firstArg_vp=drwrap_get_arg(wrapcxt, 0);
void* secondArg_vp=drwrap_get_arg(wrapcxt, 1);

dr_fprintf(STDERR, "firstArg : %f \n", *(float *)firstArg_data );
dr_fprintf(STDERR, "secondArg : %f \n",  *(float *)secondArg_data );
}

The last approach, I tried was using the casting and assuming continuous memory location. I believe this is wrong; just thought of putting it so no one else makes this mistake.

static void wrap_pre(void *wrapcxt, OUT void **user_data)
{
void* firstArg_vp=drwrap_get_arg(wrapcxt, 0);

float* firstArg_dataStartLocation = (float*) firstArg_vp; // point at start of data
 float firstArg_data = *firstArg_dataStartLocation; // read first float
 firstArg_dataStartLocation++; // now pointing at next float in data
 float secondArg_data = *firstArg_dataStartLocation; // read second float

dr_fprintf(STDERR, "firstArg : %f \n", firstArg_data );
dr_fprintf(STDERR, "secondArg : %f \n", secondArg_data );

}

Any help would be greatly appreciated.



Derek Bruening

unread,
Jul 8, 2024, 11:00:16 AMJul 8
to Balaji Balasubramaniam, DynamoRIO Users
What you need to do is look up the calling convention for your platform and figure out how floating-point parameters are passed.  If it's x86-64 it should be in the xmm registers.  It is not possible to get a float arg value from an integer arg value as you will see when you look at the convention as they are in completely different registers.

It would be great if you could contribute the code to do this by sending a pull request.  You can see the integer code at https://github.com/DynamoRIO/dynamorio/blob/master/ext/drwrap/drwrap.c#L705.  There would have to be a parameter type interface added with a dispatch on type.

--
You received this message because you are subscribed to the Google Groups "DynamoRIO Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dynamorio-use...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dynamorio-users/3d21eee0-9457-43d7-9960-56ca4d3e0830n%40googlegroups.com.

Balaji Balasubramaniam

unread,
Jul 9, 2024, 12:41:35 PMJul 9
to DynamoRIO Users
Thanks for replying. The information and reference you provided was helpful. I would be happy to contribute the code. You are correct, I am using the Ubuntu 22.04 LTS (x86_64), Intel processor. I confirmed the float arg storage in Ghidra; it uses xmm registers. For reference, I am providing the code analysis below.

                            **************************************************************
                             * DWARF original prototype: float myFunc(myFunc * thi... *
                             **************************************************************
                             float __thiscall myFunc(myFunc * this, float target,
             float             XMM0_Da:4      <RETURN>
             myFunc *          RDI:8 (auto)   this
             float             XMM0_Da:4      firstArg
             float             XMM1_Da:4      secondArg
             float             XMM2_Da:4      thirdArg
             bool              SIL:1          fourthArg
             float             XMM3_Da:4      fifthArg

I am referring to this guide https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf for calling convention. The type float is 4 bytes (each byte is 8-bit object), uses stack that grows downwards from high addresses. Arguments of types float, double, _Decimal32, _Decimal64 and __m64 are in class SSE. SSE class consists of types that fit into a vector register, the registers are taken in the order from %xmm0 to %xmm7. On page 18, they discuss the stack.

I need some guidance regarding what you said earlier " parameter type interface added with a dispatch on type".

Derek Bruening

unread,
Jul 9, 2024, 7:25:32 PMJul 9
to Balaji Balasubramaniam, DynamoRIO Users
On Tue, Jul 9, 2024 at 12:41 PM Balaji Balasubramaniam <balaji...@gmail.com> wrote:
Thanks for replying. The information and reference you provided was helpful. I would be happy to contribute the code. You are correct, I am using the Ubuntu 22.04 LTS (x86_64), Intel processor. I confirmed the float arg storage in Ghidra; it uses xmm registers. For reference, I am providing the code analysis below.

                            **************************************************************
                             * DWARF original prototype: float myFunc(myFunc * thi... *
                             **************************************************************
                             float __thiscall myFunc(myFunc * this, float target,
             float             XMM0_Da:4      <RETURN>
             myFunc *          RDI:8 (auto)   this
             float             XMM0_Da:4      firstArg
             float             XMM1_Da:4      secondArg
             float             XMM2_Da:4      thirdArg
             bool              SIL:1          fourthArg
             float             XMM3_Da:4      fifthArg

I am referring to this guide https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf for calling convention. The type float is 4 bytes (each byte is 8-bit object), uses stack that grows downwards from high addresses. Arguments of types float, double, _Decimal32, _Decimal64 and __m64 are in class SSE. SSE class consists of types that fit into a vector register, the registers are taken in the order from %xmm0 to %xmm7. On page 18, they discuss the stack.

I need some guidance regarding what you said earlier " parameter type interface added with a dispatch on type".

I was thinking out loud about how to add the float support to the API.  Today the drwrap_get_arg() only takes the ordinal, so it would have to augmented with the type, probably as drwrap_get_arg_ex() (or some better name) along with some way to specify the type.  Then the code would have to dispatch on type to get the right register or memory slot.
 

Balaji Balasubramaniam

unread,
Jul 16, 2024, 1:41:06 AMJul 16
to DynamoRIO Users
I have tried several ways, and so far, no luck. I have provided below a couple of techniques that I have tried, and I am confident; please let me know what I am missing.

Quick recall, I am trying to retrieve float values from xmm registers. The first lesson I learned is that you cannot access the xmm registers directly as you do for integer registers. Because integers are stored in general-purpose registers, however, floats use Streamed SIMD Extensions (SSE). These Single Instruction Multiple Data (SIMD) contain 4 floating point values, each 4 bytes, so a total of 16 bytes for each xmm register.

With this knowledge, I tried to access xmm using inline asm code as follows. The SSE instructions have a suffix -ss for scalar operations (Single Scalar) and -ps for packed operations (Parallel Scalar). The following multiplication works. Here is a good resource I found - https://www.songho.ca/misc/sse/sse.html

float rfx;
asm("mulss %%xmm0,%%xmm1" : "=r"(rfx));  multiplication of two working xmm registers

However, data copy does not works. I tried several ways, but many of them ended with errors.

asm("movaps [eax],%xmm0"); //-not working with errors

Also tried data alignment as follows:

__attribute__((aligned (16))) float b[4000];
asm("movaps [b], %xmm0");
dr_fprintf(STDERR, "..reading xmm0 register value.......%f...\n", b );
//dr_fprintf(STDERR, "..reading register value.......%f.....%f....%f.....%f...\n", b[0], b[1], b[2], b[3] ); //another variation

So I was reading a lot of documentation and came across this register save area, providing it below for reference, on page 56 - https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf

Register Offset
%rdi 0
%rsi 8
%rdx 16
%rcx 24
%r8 32
%r9 40
%xmm0 48
%xmm1 64
. . .
%xmm15 288

Hence, I tried to offset the xmm address from register r9 as below. But could not get it working.

   drwrap_context_t *wrapcxt_t = (drwrap_context_t *)wrapcxt;

    __attribute__((aligned (4))) float xmmf1[1000];
    float xmmf1a, xmmf1b, xmmf1c, xmmf1d;
    dr_fprintf(STDERR, ".....wrapcxt context xmm0 f4....: %p\n", &wrapcxt_t->mc->r9 + 0x08);
    dr_fprintf(STDERR, ".....wrapcxt context xmm0 f4....: %f\n", *(float *)(&wrapcxt_t->mc->r9 + 0x08));
    memcpy(&xmmf1, (&wrapcxt_t->mc->r9 + 0x08), sizeof(float));
    dr_fprintf(STDERR, ".....xmmf1=%f\n",xmmf1);
    memcpy(&xmmf1a, (&wrapcxt_t->mc->r9 + 0x08), sizeof(float));
    dr_fprintf(STDERR, ".....xmmf1a=%f\n",xmmf1a);

    dr_fprintf(STDERR, ".....wrapcxt context xmm0 f8....: %p\n", &wrapcxt_t->mc->r9 + 0x0C);
    dr_fprintf(STDERR, ".....wrapcxt context xmm0 f8....: %f\n", *(float *) (&wrapcxt_t->mc->r9 + 0x0C));
    memcpy(&xmmf1b, (&wrapcxt_t->mc->r9 + 0x0C), sizeof(float));
    dr_fprintf(STDERR, ".....xmmf1b=%f\n",xmmf1b);

    dr_fprintf(STDERR, ".....wrapcxt context xmm0 f12....: %p\n", &wrapcxt_t->mc->r9 + 0x10);
    dr_fprintf(STDERR, ".....wrapcxt context xmm0 f12....: %f\n", *(float *)(&wrapcxt_t->mc->r9 + 0x10));
    memcpy(&xmmf1c, (&wrapcxt_t->mc->r9 + 0x10), sizeof(float));
    dr_fprintf(STDERR, ".....xmmf1c=%f\n",xmmf1c);

    dr_fprintf(STDERR, ".....wrapcxt context xmm0 f16....: %p\n", &wrapcxt_t->mc->r9 + 0x14);
    dr_fprintf(STDERR, ".....wrapcxt context xmm0 f16....: %f\n", *(float *)(&wrapcxt_t->mc->r9 + 0x14));
    memcpy(&xmmf1d, (&wrapcxt_t->mc->r9 + 0x14), sizeof(float));
    dr_fprintf(STDERR, ".....xmmf1d=%f\n",xmmf1d);

Any help would be appreciated.

Derek Bruening

unread,
Jul 16, 2024, 1:29:31 PMJul 16
to Balaji Balasubramaniam, DynamoRIO Users
The drwrap argument access is from a clean call.  As you can see for the general-purpose register parameters in the drwrap code I pointed at before (please see my prior email saying " You can see the integer code at https://github.com/DynamoRIO/dynamorio/blob/master/ext/drwrap/drwrap.c#L705"), it accesses them in the dr_mcontext_t saved in the clean call setup, *not* by directly reading raw processor state.

Balaji Balasubramaniam

unread,
Jul 18, 2024, 9:23:49 PMJul 18
to DynamoRIO Users
I traced back the integer code. On line 707, you have the following code

case 0: return &wrapcxt->mc->rdi; 

When I replaced register rdi with xmm register, as below:

case 0: return &wrapcxt->mc->xmm;

I got the following error: ‘dr_mcontext_t’ {aka ‘struct _dr_mcontext_t’} has no member named ‘xmm’; did you mean ‘ymm’?

So, I traced the struct _dr_mcontext_t’ to the build/include/dr_mcxtx.h file. On line 289, there was a definition for simd register. Above line 289, you have detailed comments saying, "We must preserve xmm0-15 if in a 64-bit Linux app (PR 302107)". Since I could not find xmm, and based on the architecture documents I referred to you in my previous chat, xmm falls into the SSE class. SSE is a SIMD; hence, I thought the simd would be the way to go. And I traced the structure to the core/lib/globals_api.h file. In line 750, u32 is used to store the full 128-bit register. To test, I came up with the below code:

case 0: dr_fprintf(STDERR, ".....DRWRAP_CALLCONV_AMD64....0....%f......%f......%f......%f....\n", *(float *)&wrapcxt->mc->simd->u32[0],
                *(float *)&wrapcxt->mc->simd->u32[1],*(float *)&wrapcxt->mc->simd->u32[2],*(float *)&wrapcxt->mc->simd->u32[3] );

No errors and the values I am receiving are not correct. I also tried without *(float *).

On line 655, in globals_api.h file, I can see the definition of dr_xmm_t. But not used in dr_mcxtx.h file. Am I missing anything?

Again, thanks for the help.

Derek Bruening

unread,
Jul 18, 2024, 10:46:47 PMJul 18
to Balaji Balasubramaniam, DynamoRIO Users
Did you request that the SIMD registers be filled in?  They are not by default.  Please see the docs: https://dynamorio.org/dr__ir__utils_8h.html#a9e348b2596b8750ce3fad234ab022ced,
You need to add DR_MC_MULTIMEDIA to the flags: looks like the call to drwrap_get_mcontext_internal() at the top of drwrap_arg_addr().

Balaji Balasubramaniam

unread,
Jul 19, 2024, 1:09:56 PMJul 19
to DynamoRIO Users
Thanks for quickly replying. It works now, however, in the spirit of writing a complete code. How do you retrieve and set the float value in the warp post? I am providing the get float value code below:

static void wrap_post(void *wrapcxt, void *user_data)
{
    void *ret_ptr = drwrap_get_retval(wrapcxt);
    dr_printf("...Function return value PID: %p.....\n", &ret_ptr);
    dr_printf("...Function return value PID: %f.....\n", *(float *)&ret_ptr);
}

There are no errors, but the value is not correct.

Side note: I will clean up my code and upload it to the GitHub repository.

Derek Bruening

unread,
Jul 19, 2024, 5:10:25 PMJul 19
to Balaji Balasubramaniam, DynamoRIO Users
Just like with parameters, the calling convention puts a float return value in a different register.

Balaji Balasubramaniam

unread,
Jul 19, 2024, 6:20:21 PMJul 19
to DynamoRIO Users
Awesome! Thanks for the guidance. Got it working. Now, I am implementing the drwrap_set_retval. Below is the code so far:

float new_return_value=0.000028; //this could hold positive and negative
void *  float_ret_val = &new_return_value;
drwrap_set_retval(wrapcxt,  float_ret_val);

Is this correct? When I try to get_retval I am not getting the new_return value. Am I missing anything?
Reply all
Reply to author
Forward
0 new messages