C API malloc hassle

I was adding a section to the docs about why we have to jump through pd->system->realloc instead of calling malloc() directly, and included macros to hide it away:

PlaydateAPI* pd; // global, must be set at load time
#define malloc(size) pd->system->realloc(NULL,size)
#define realloc(ptr,size) pd->system->realloc(ptr,size)
#define free(ptr) pd->system->realloc(ptr,0)

and Marc pointed out that if I made those functions instead it would still work and malloc() calls in the game would call those instead of the newlib versions. Whoa. I guess I'd thought they would conflict instead of ours overriding newlib? I poked around some more and discovered that if we instead implement that at _malloc_r() etc. then printf() will also use our allocator instead of giving the dreaded _sbrk error. I added this to setup.c, and it seems to work fine..? :exploding_head:

I'll get this into 1.10, but if you want to test it out now, here's the updated C_API/buildsupport/setup.c:
setup.c.zip (1.0 KB)

11 Likes

This is what I have been doing in my own apps as well, nice to see it become part of the SDK :smiley: also means we will get more accurate heap alloc data in the simulator by default!

1 Like

Oh my! No more _sbrk nightmare :partying_face:
That’s really awesome!!!

1 Like

Actually.. in the simulator those malloc calls will go straight to the system malloc and we won't be able to track them. Hopefully there's some way to tell the loaders to resolve malloc calls in the pdex.dylib/so/dll to our version.

1 Like

Indeed, forgot that simulator also doesn't use setup.c. Implementing the calls in my own main.c results in the pd-realloc stuff being used.

1 Like

I wonder if something similar could be done for the file API.

I have these macros set which make it easier to port C libraries, but obviously not having to do this at all would be easiest. :slight_smile:

#define GKFile SDFile
#define kGKFileRead kFileRead
#define kGKFileReadData (kFileRead | kFileReadData)
#define kGKFileWrite kFileWrite
#define kGKFileAppend kFileAppend
#define kGKSeekSet 0
#define kGKSeekCurrent 1
#define kGKSeekEnd 2

#define GKFileOpen(path, mode) playdate->file->open((path), (mode))

#define GKFileClose(f) playdate->file->close((f))
#define GKFileSeek(f, position, whence) playdate->file->seek((f), (position), (whence))
#define GKFileRead(buffer, size, f) playdate->file->read((f), (buffer), (size))
#define GKFileWrite(buffer, size, f) playdate->file->write((f), (buffer), (size))
#define GKFileTell(f) playdate->file->tell((f))

playdate being a global pointer to the playdate object.

I'd take printf(...) too. :wink:

#define GKLog(...) playdate->system->logToConsole(__VA_ARGS__)

Can I request adding support for the .__init section? It requires only modifying setup.c to call __init/__fini as described here: [CPP] Guide: C++ on Playdate -- this is nice because even in C sometimes libraries and code expects .__init to work, such as when using __attribute__((constructor)).

It would also be nice (but less pressing) to add the C++ exception frame symbols to the linker map: [CPP] Guide: C++ on Playdate

1 Like

I don't expect that would cause any problems, sure. I've filed that, should see it in the next release (after today's).

I could have sworn I did that, but I don't see it in there. Maybe I couldn't get the varargs to work right?

Bumbled into this because a library I'm pulling in uses calloc instead of malloc, and setup.c doesn't override that one. That didn't work well on device, or in the simulator with the 16MB malloc pool enabled. Since the library already has its own alias to calloc, I just redefined that alias to hit my own "pdcalloc" instead of calloc, where pdcalloc just calls malloc (which in turn calls pdrealloc) and memset. This fixes everything but it's not really the most beautiful approach to mess with a library that I don't own and I might want to take updates to someday.

void * pdcalloc(size_t count, size_t size) {
	void * retval = malloc(count * size);
	memset(retval, 0, size * count);
	return retval;
}
  1. Would it be worthwhile for setup.c to handle calloc as well?
  2. Is there a more elegant way to do this in my project, outside the library? Just directly redefining calloc in my glue code that gets built before Chipmunk works in the simulator, but not on device:
void* calloc(size_t count, size_t size) {return pdcalloc(count, size);}

Presumably that's because there's a _calloc_r that's what I have to redefine if TARGET_PLAYDATE (along the lines of _malloc_r et al) but that stuff has extra entanglements that look a bit above my paygrade. I've lost what little C/C++ fluency I had years ago, and am really in a banging-rocks-together mode.

You may want to check for overflow before making the multiplication. That's the main reason why count and size are separate arguments coming in.

Something like count <= (SIZE_MAX / size) for example.

Oh yeah. Nice catch. Like I said, banging rocks together. I didn't even write the method, I stole it from @maximile's old Chipmunk Playdate demo. I'm lucky that putting together Lua bindings for a C library doesn't take a tremendous level of virtuosity.

1 Like