0

I'm working on a project on windows phone 8 and I'm calling static functions by pointers, but without the type of the parameters (I have the number of parameters and a function type that has int or float as parameters).

I've checked this thread What is the Windows RT on ARM native code calling convention?

And I've also looked at the assembly and it seems it treats differently floats from int (floats passed in S0).

Well my question, is there any way to force some functions (or even all) to be called the old fashion ?

Community
  • 1
  • 1
Sulea Cosmin
  • 598
  • 1
  • 11
  • 24
  • 1
    Well, the MSVC compiler only has calling convention options for x86, not x86-64 or ARM. Given that WP8 only supports Snapdragon, which guarantees the presence of VFP, I see no reason why it would bother with anything other than hard-float, anyway. Obvious question is, why do you want soft-float? – Notlikethat Jan 26 '14 at 16:29
  • Well ... I'm porting a virtual machine that makes calls to native code and I only have the function pointer and the number and type of each parameter, but I have no function type. Meanwhile I managed to trick it to load the S registers with the float parameters (with a dummy function) and luckily the registers are preserved. However, a cleaner solution would be to have an external asm helper function to make the real call. – Sulea Cosmin Jan 27 '14 at 08:04
  • It would help if you explained the actual situation in the question: Is this "native code" yours, or some 3rd-party library? Why can't your VM use the system calling convention? If you don't know whether a function has returned float or int, how would you know what to do with it regardless of which register it's in? – Notlikethat Jan 27 '14 at 08:54
  • Yes, the "native code" is mine, but is too big to consider changing it. When a function is exported for visibility in scripts, a list of parameters is generated (the type of each), the return type and the pointer of the function. However I cannot define a "typedef something ..." to have a way to call it. I have some generic 'typedef (void*)()' 'typedef (void*)(int p1)' 'typedef (void*)(int p1, int p2)' that are used with the functions exported. – Sulea Cosmin Jan 27 '14 at 09:32
  • So basically your code calling the functions is wrong. Casting `void *` to `float` is undefined. Casting the function pointer to `(float)(...)` _may_ work, but calling an incompatibly-cast function pointer is also undefined. Hacking calling conventions is _not_ the way to fix this. – Notlikethat Jan 27 '14 at 09:47
  • no, I'm not casting (void*) to float, just the parameters that I pass to the function instead of passing (void)(float, float) as the function is defined I call it (void)(int, int) but the values are actually floats (int p1 = *(int*)&f1 where f1 is float) – Sulea Cosmin Jan 27 '14 at 12:23
  • well ... I don't want to hack the calling convention, one option would have been to use _cdecl (not an option as you've said before) and another option would be to properly load s0-s14 registers with my parameters – Sulea Cosmin Jan 27 '14 at 12:24

1 Answers1

0

OK, I'm going to clarify things by pretending you asked the underlying question:

How can I call a (void *)(float) function when I only have a genericised (void *)(int) pointer to it?

Jumping directly to asking how to change the calling convention sounds like this:

How can I make one implementation's undefined behaviour behave like another implementation's undefined behaviour, because I've written a ton of code that relies on this and don't want to have to fix it?


First, let's see what the C standard says...

If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.

Yup, there's the problem. Sad face :(

But wait, what's that the previous sentence says?

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer

Hooray, we can do it in a well-defined way without resorting to shims and assembly hacks, simply by converting back to the proper function signature when we call!

In short; cast the function pointer, not the arguments, and not only will it work, it will be correct. Since you must know the true signature of the function you're calling (else you couldn't pass the appropriate arguments), you can always make the correct cast, and the intermediate pointer type becomes a non-issue.

Here's a demonstration:

#include <stdio.h>

void fn(float x){
    printf("%.1f\n", x);
}

int main(void) {
    float f = 27.3;
    void (*fptr)(float) = fn;
    void (*iptr)(int) = fn;     /* LA LA COMPILER WARNING I CAN'T HEAR YOU */

    printf("expected:\t");
    fptr(f);
    printf("casting arg:\t");
    iptr(*(int *)&f);           /* double-undefined behaviour */
    printf("casting iptr:\t");
    ((void(*)(float))iptr)(f);
}

Note that *(int *)&f is itself undefined, but in this case (VS2013) the "expected" code (float used as int without conversion) does get generated in both builds. Hey, it's a valid form of undefined behaviour (as is anything else...)

And the output:

expected:       27.3
casting arg:    27.3
casting iptr:   27.3
Press any key to continue . . .

Huh? Why bother? Both methods "work". Let's try the x64 build...

expected:       27.3
casting arg:    0.0
casting iptr:   27.3
Press any key to continue . . .

Ah. There it is. In fact, if you'd written the original code on any of the other 3 currently-supported Windows platforms you wouldn't have this problem, because it wouldn't have worked in the first place - x86-64, IA-64 and ARM all use register-based calling conventions with floats and ints handled separately, whilst x86 is the stack-based anachronism.

Or, if you really really really want a portable attempt at exploiting undefined behaviour, try rewriting all the functions to take a load of dummy ints and floats before the real arguments, and a similar load of ints and floats afterwards, to ensure the ones in the middle get spilled to the stack regardless of evaluation order. Yes, that would be insane...

Notlikethat
  • 20,095
  • 3
  • 40
  • 77