Variable argument function: Bad access with va_arg at iOS arm64 - c++

I have a function like this:
typedef long long myint64;
typedef enum {
INT32_FIELD,
CHARP_FIELD,
INT64_FIELD,
} InfoType;
int32_t ReadInfo(void *handle, InfoType info, ...)
{
va_list arg;
va_start(arg, info);
void *argPtr = va_arg(arg, void*);
va_end(arg);
int32_t ret = 0;
int32_t *paramInt = NULL;
char **paramCharp = NULL;
myint64 *paramInt64 = NULL;
switch (info) {
case INT32_FIELD:
paramInt = static_cast<int32_t*>(argPtr);
*paramInt = functionWhichReturnsInt32();
break;
case CHARP_FIELD:
paramCharp = static_cast<char**>(argPtr);
*paramCharp = functionWhichReturnsCharPtr();
break;
case INT64_FIELD:
paramInt64 = static_cast<myint64*>(argPtr);
*paramInt64 = functionWhichReturnsInt64();
break;
default:
ret = -1;
break;
}
return ret;
}
Call this function like this from separated c file. This file does not include definition of ReadInfo function:
extern "C" {int32_t CDECL ReadInfo(intptr_t, int32_t, int32_t*);}
int32_t readInt()
{
int32_t value = 0;
int32_t *ptr = &value;
ReadInfo(handle, INT32_FIELD, ptr);
return value;
}
This call fails only under iOS arm64. arm7s and win32 work fine with this call. (Yes, our only 64 bit target platform is iOS arm64.)
In debugger I found that address of ptr in readInt function is different from what I got with:
void argPtr = va_arg(arg, void);
Am I working wrong with arg_list?
P.S. It is not a plain Objective C application. It is part of native Unity plugin. But in iOS Unity code is just transformed into Objective C/C++ from C#. That is why you can see second declaration:
extern "C" {int32_t CDECL ReadInfo(intptr_t, int32_t, int32_t*);}

It's not an issue of IL2CPP but an issue of iOS, or maybe the compiler.
The following code could reproduce the issue even on the latest Xcode (10.1) and iOS (12.1)
typedef int __cdecl (*PInvokeFunc) (const char*, int);
int test()
{
PInvokeFunc fp = (PInvokeFunc)printf;
fp("Hello World: %d", 10);
return 0;
}
The expected output is: Hello World: 10 but it will give Hello World: ??? (Random number) on iOS however.
I tried the same code on macOS and Linux and both of them work well.
I'm not sure if it relates to the Apple document or not:
Variadic Functions
The iOS ABI for functions that take a variable number of arguments is entirely different from the generic version.
Stages A and B of the generic procedure call standard are performed as usual—in particular, even variadic aggregates larger than 16 bytes are passed via a reference to temporary memory allocated by the caller. After that, the fixed arguments are allocated to registers and stack slots as usual in iOS.
The NSRN is then rounded up to the next multiple of 8 bytes, and each variadic argument is assigned to the appropriate number of 8-byte stack slots.
The C language requires arguments smaller than int to be promoted before a call, but beyond that, unused bytes on the stack are not specified by this ABI.
As a result of this change, the type va_list is an alias for char * rather than for the struct type specified in the generic PCS. It is also not in the std namespace when compiling C++ code.
https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html
Updates:
The reply for Apple engineer:
Casting function pointers to add a different calling convention doesn’t change how the callee is represented, it only changes how the caller performs its call. printf already has a calling convention, and what you’re doing might happen to work for some combinations on some platforms, while not working on others. You want to declare a wrapper function instead, which has the desired calling convention, and which calls the function you want. You’ll need to marshal the arguments manually.
That is to say the variadic function can't be direct p/invoke unless IL2CPP generate wrapper function for it. Only a function pointer is not enough.

The reason of this problem was in IL2CPP, which generates calls of function with variable argument. And it does not use my types like InfoType, myint64. It uses platform specific types for info variable. And size maybe different I guess.
I just add 3 new function for Unity API:
int32_t ReadInfoInt(void *handle, InfoType info, int *ret);
int32_t ReadInfoInt64(void *handle, InfoType info, myint64 *ret);
int32_t ReadInfoStr(void *handle, InfoType info, char **ret);
In this function I just call ReadInfo.
It is workaround 100%, but it is better then fight with IL2CPP.

Related

C++ trying to cast address to void* and assign to function pointer (so that my bootloader can jump to actual app)

I am writing a bootloader for an STM32, where I need to jump from bootloader to the real app.
In C this works, because I can cast an address to a void pointer and assign that to a function pointer, and call the function pointer as follows:
void jump_to_firmware(uint32_t address)
{
uint32_t reset_handler_add = *((volatile uint32_t *)(address + 4));
void (*app_reset_handler)(void) = (void *)reset_handler_add;
SCB->VTOR = address;
uint32_t msp_value = *((volatile uint32_t *)address);
__set_MSP(msp_value);
app_reset_handler();
}
If I use the same implementation in a C++ the gnu compiler will give an error on the cast to void pointer.
include/bootloader.hpp:58:39: error: invalid conversion from 'void*'
to 'void (*)()' [-fpermissive]
After googling I found this SO page, which I tried and came to the following implementation in my class:
void JumpToApp()
{
// Quick test if C style cast does work
//jump_to_firmware(_appStartAddress);
uint32_t mspAdress = *((volatile uint32_t *)_appStartAddress);
uint32_t resetHandlerAddress = *((volatile uint32_t *)(_appStartAddress + sizeof(uint32_t)));
// https://stackoverflow.com/questions/1096341/function-pointers-casting-in-c
typedef void (*functionPointer)();
functionPointer resetHandler = 0;
reinterpret_cast<void*&>(resetHandler) = (void*)resetHandlerAddress;
SCB->VTOR = _appStartAddress;
__set_MSP(mspAdress);
resetHandler();
}
In the C++ implementation:
functionPointer resetHandler is assigned with 0x8035065
SCB->VTOR is assigned with 0x08020000
mspAddress is assigned with `0x20020000
then the function pointer resetHandler is called
In the C implementation:
app_reset_handler is assigned with 0x8035065
SCB->VTOR is assigned with 0x08020000
mspAddress is assigned with `0x20020000
then the function pointer app_reset_handler is called
The C implementation works, it jumps to my app, the app runs without issues.
The C++ implementation ends up nowhere. It hangs/crashes on the following (to me meaningless) address:
I am trying to keep the amount of source files to a minimum, so I would like to keep the logic in the single class definition.
My questions:
Did I misunderstand the linked SO page and can somebody see where I went wrong in the C++ implementation?
Is there a better/easier way to cast an address to a function pointer in C++?
Or is there a technical reason why it simply can't be done in C++?
PS: The bootloader code is the same in both cases. The only difference I made to test either implementation is to comment out the code in Bootloader::JumpToApp and call the C function jump_to_firmware(uint32_t) instead.
PPS: all peripherals are deinitialized properly. Again, it works fine, the problem only appears when I use this C++ implementation.
The same code will compile in C and C++. You simple has to cast to the correct cast (in C++ you cant assign a void * to non void * pointer. It is much more strict than in C.
void jump_to_firmware(uint32_t address)
{
uint32_t reset_handler_add = *((volatile uint32_t *)(address + 4));
void (*app_reset_handler)(void) = (void (*)(void))reset_handler_add;
/* ... */
}
If you do not like those weird casts you can typedef the function.
typedef void handler(void);
void jump_to_firmware(uint32_t address)
{
uint32_t reset_handler_add = *((volatile uint32_t *)(address + 4));
handler *app_reset_handler = (handler *)reset_handler_add;
/* ... */
}

Passing member function that accepts void *

I am working on code that needs to send a member function pointer to a logger method that accepts a void * as the parameter. I cannot change it from void *. I cannot use c++11 either. Is there a way to get it to work without any warning. For example:
logger.h
#ifndef _LOGGER_H
#define _LOGGER_H
void logger( void *func );
#endif /* _LOGGER_H */
logger.cpp
#include <cstdio>
#include "logger.h"
void logger( void *func )
{
printf("%lx\n", (unsigned long)func);
}
testCase.cpp
#include "logger.h"
class myClass
{
public:
void testCase( void );
};
void myClass::testCase( void )
{
/* This works on my compiler, but gives warning */
/* warning: converting from 'void (myClass::*)()' to 'void*' */
/* I know this is bad and wrong. */
logger((void *)&myClass::testCase);
/* This compiles without warning */
/* But doesnt work the way I need, gives ffff*/
void (myClass::*ptr)( void ) = &myClass::testCase;
void *m_ptr = ptr;
logger(m_ptr);
}
logger.h and logger.cpp cannot be changed.
This is being run a VxWorks and I need the address to look up in the symbol table. When I try the second way I get ffff. Although I get a real address when using other compilers, its different for VxWorks.
Can you think of another way to get this to work.
References
No, you can't make it happen. Standard prohibits converting pointers-to-members to void*. The reason for this is their incompatibility with void* - they are usually double the size of the void*.
Your code has other issues as well, for example, (unsigned long)func is converting a void* to unsigned long, and this is undefined as well. For example, on many systems long is 32 bits in length, while void* is 64bit. To reliably convert void* to integer type, you need to use uintptr_t (provided your implementation has it).
I don't know about "double the size"; pointers are pointers and in my understanding, all pointers on a given system are the same size, regardless of what they point to...
However, looking in my copy of The C++ Programming Language, 3rd Edition, by Stroustrup (page 101):
Occurrances of void*s at higher levers of the system should be viewed with suspicion because they are likely indicators of design errors. Where used for optimization, void* can be hidden behind a type safe interface (§13.5, §24.4.2).
Pointers to functions (§7.7) and pointers to members (§15.5) cannot be assigned to void*s.

Using var_arg to pass parameters for function calls

I am writing an adapter to combine two APIs (one in C and another in C++).
If a function is called on the one API I need to pass the callers ID and the function's arguments to an adapter and call the according function with this information passed.
Now aparently they can not be mapped directly as one interface requires C++ compilation and the name mangling would screw the other so that is why I am using a set of adapters in the first place.
As the number of arguments varies, I looked up variadic functions and found the idea pretty useful, however I am operating on POD only and have to deal with structs, enums and a lot of different arguments per call, which might need to be put back into a struct before feeding it to the target function.
Every example I stumbled upon was far simpler and involved mostly arithmetic operations like summing stuff up , finding largest numbers or printing. Mostly done with for loops on the var_list.
Maybe I got stuck on the idea and it won't work at all, but I am just curious...
Say I wanted to assign the arguments from the list to my target functions parameters (the order of the arguments passed is the correct one), what would be a good way?
BOOL Some_Function(
/* in */ CallerId *pObjectId,
/* in */ someDataType argument1 )
{
BOOL ret = Adapter_Call(pFunction, pObjectId, argument1);
return ret;
}
and so once I made it to the right adapter I want to do
BOOL Adapter_Call(*pFunction, *pObjectId, argument1, ...)
{
va_list args;
va_start(args, argument1);
/*go over list and do `var_list[i] = pFunctionArgList[i]` which is
of whatever type so I can use it as input for my function */
va_end(args);
pObjectId.pFunction(arg1,...,argn);
}
Can I access the input parameters of a function to perform assignments like this?
Has anyone done something like this before? Is there a conceptual mistake in my thinking?
All I found on the net was this, http://www.drdobbs.com/cpp/extracting-function-parameter-and-return/240000586but due to the use of templates I am not sure if it wouldn't create another problem and so in the end implementing an adapter for each and every single functioncall may be simpler to do.
A SO search only returned this: Dynamic function calls at runtime (va_list)
First, you should heed Kerrek's advice about extern "C". This is C++'s mechanism for giving an identifier C linkage, meaning that the name won't be mangled by the C++ compiler.
Sometimes, and adapter still needs to be written for a C++ interface, because it manipulates objects that do not map to a C POD. So, the adapter gives the C interface a POD or opaque pointer type to manipulate, but the implementation of that interface converts that into an C++ object or reference and then calls the C++ interface. For example, suppose you wanted to provide a C interface for C++ std::map<int, void *>, you would have a common header file in C and C++ that would contain:
#ifdef __cplusplus
extern "C" {
#endif
struct c_map_int_ptr;
// ...
// return -1 on failure, otherwise 0, and *data is populated with result
int c_map_int_ptr_find (struct c_map_int_ptr *, int key, void **data);
#ifdef __cplusplus
}
#endif
Then, the C++ code could implement the function like:
typedef std::map<int, void *> map_int_ptr;
int c_map_int_ptr_find (struct c_map_int_ptr *cmap, int key, void **data) {
map_int_ptr &map = *static_cast<map_int_ptr *>(cmap);
map_int_ptr::iterator i = map.find(key);
if (i != map.end()) {
*data = i->second;
return 0;
}
return -1;
}
Thus, there is no need to pass the arguments passed via the C interface through a variable argument adapter. And so, there is no need for the C++ code to tease out the arguments from a variable argument list. The C code calls directly into the C++ code, which knows what to do with the arguments.
I suppose if you are trying to implement some kind of automated C adapter code generator by parsing C++ code, you could think that using variable arguments would provide a regular mechanism to communicate arguments between the generated C code interface and the generated C++ adapter code that would call the original C++ interface. For such a scenario, the code for the above example would look something like this:
// C interface
typedef struct c_map_int_ptr c_map_int_ptr;
typedef struct c_map_int_ptr_iterator c_map_int_ptr_iterator;
//...
c_map_int_ptr_iterator c_map_int_ptr_find (c_map_int_ptr *map, int key) {
c_map_int_ptr_iterator result;
cpp_map_int_ptr_adapter(__func__, map, key, &result);
return result;
}
// C++ code:
struct cpp_adapter {
virtual ~cpp_adapter () {}
virtual void execute (va_list) {}
};
void cpp_map_int_ptr_adapter(const char *func, ...) {
va_list ap;
va_start(ap, func);
cpp_map_int_ptr_adapter_method_lookup(func).execute(ap);
va_end(ap);
}
//...
struct cpp_map_int_ptr_find_adapter : cpp_adapter {
void execute (va_list ap) {
map_int_ptr *map = va_arg(ap, map_int_ptr *);
int key = va_arg(ap, int);
c_map_int_ptr_iterator *c_iter = va_arg(ap, c_map_int_ptr_iterator *);
map_int_ptr::iterator i = map->find(key);
//...transfer result to c_iter
}
};
Where cpp_map_int_ptr_adapter_method_lookup() returns an appropriate cpp_adapter instance based on a table lookup.

How can I port DeviceIOControl to ioctl?

In my understanding, DeviceIOControl and ioctl are the same functions. They both send control codes to the hardware and return the responses. In an effort to reuse code, I am trying to create a function which will work in a cross-platform manner. Therefore, I've decided to use the DeviceIOControl api since it is fixed and specific. The problem is: how do I map ioctl to that?
I currently have:
int DeviceIoControl_issueCommand(DeviceHandle handle, int command, void *input, ssize_t sizeof_input, void *output, ssize_t sizeof_output, uint32_t *bytes_written){
#if SYSTEMINFORMATION_ISWINDOWS
int result = DeviceIoControl(handle,command,input,sizeof_input,output,sizeof_output,bytes_written,0);
if (result == 0){
result = -1; //-1 is the new error return
}
return result;
#else
int result = ioctl(handle, command, input); //this doesnt work!
return result;
#endif
}
Any help is greatly appreciated!
What you are asking is not possible without a lot of internal translation in DeviceIoControl_issueCommand. The function calls are completely different and expect different parameters and data. You can work around this by declaring an IOControl class and adding member functions for each type of IO functionality you want to support.
class IOControl
{
public:
void DoIoControlX();
void DoIoControlY(int param1, int param2);
};
Then provide an impelementation for each platform you need to support. One for Windows DeviceIOControl calls and one for systems that support ioctl
I actually found that there is an IOCTL which does pass raw data to and from the driver (at least for the hard drive): HDIO_DRIVE_TASKFILE (http://www.mjmwired.net/kernel/Documentation/ioctl/hdio.txt)

callback functions and static_cast for wrapping class methods

I'm having some trouble making a callback wrapper class method that needs to be used by a third party library; the JackAudio library.
I have been able to make a wrapper for a JackAudio callback function that needs two arguments.
I'm just having trouble creating a callback function for a particular function that needs a const char * as an argument.
So far I have been able to make the JackAudio library jack_set_sample_rate_callback function use a custom class and can be executed like so:
SoundClass Sound;
SoundClass * SoundPointer = &Sound;
jack_set_sample_rate_callback(
client,
SoundClass::SampleRateCallbackWrapper,
SoundPointer
);
And the class looks something like this:
SoundClass
{
int SampleRateCallback( jack_nframes_t nframes )
{
//executes some code when called.
}
static int SampleRateCallbackWrapper( jack_nframes_t nframes, void * arg )
{
return static_cast < SoundClass* > ( arg )->SampleRateCallback( nframes );
}
};
All of the above works well, with no issues.
The problem I'm having now is with the JackAudio callback function jack_set_error_function
This is what I tried:
static void ErrorCallbackWrapper( const char * arg )
{
return static_cast < SoundClass*>( arg )->SomeErrorFunction();
}
But I get error: invalid static_cast from type ‘const char*’ to type ‘SoundClass*’
I get the gist why this is happening, I just have no idea what to do for a solution.
Thanks in advance for any help guys.
Assuming the Jack API is written for the C language, there is a formal problem already with the working callback that you have. Namely that it then needs to be extern "C", and that as a static member function it cannot be. So formally it needs to be a free-standing function.
The documentation that you link to for the jack_set_error_function gives this signature, presumably expressed in C:
void jack_set_error_function( void(*)(const char *) func);
For C++ the callback must be assumed to be extern "C", so,
extern "C" void MyErrorFunction( char const* errorMessage )
{
// Whatever, e.g. post a message to the GUI event queue, or terminate.
}
If you want this function to in turn call a method on an object, then unless the library provides some special mechanism to help you, you will just have to use one of the following techniques:
a namespace scope variable accessed by the callback, or
a dynamically generated callback.
C++ does not as of yet support the second approach, at all, so the first one is strongly indicated – if you want a callback on a method of an object.
EDIT: Sorry, I forgot to mention,
the function declarations in the API documentation are syntactically invalid.
E.g. the documentation’s signature
void jack_set_info_function( void(*)(const char *) func );
simply won’t compile with a standard-conforming compiler. Not as C, and not as C++. It’s syntactically invalid in both languages.
Instead it should be
void jack_set_info_function( void(*func)(const char *) );
Since the documentation apparently is generated by DOxygen, it stands to reason that it's been generated from source code that compiles. If so then this is a bug in DOxygen, and a problem with the quality assurance of the library provider. However it might be a problem that lies solely with the library provider, or, I might be mistaken in the assumption that this is a C library?