Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Inline::C & Strawberry: can't free a buffer allocated with asprintf

by vr (Curate)
on Jan 01, 2020 at 18:30 UTC ( [id://11110841]=perlquestion: print w/replies, xml ) Need Help??

vr has asked for the wisdom of the Perl Monks concerning the following question:

Having learned about asprintf, I was trying to improve/tidy a couple C fragments, but here's a problem with Strawberry:

use strict; use warnings; use Inline C => <<'END_OF_C'; #define _GNU_SOURCE void foo() { char *s; asprintf(&s, "%s", "foo"); free(s); } END_OF_C foo(); __END__ Free to wrong pool 6334e0 not 8c00140021160806 at asprintf.pl line 13.

This number is frightening. With "free" line disabled, it runs OK, but leaks. In Linux it runs OK as is. Simple and obvious C program compiles and runs OK under same gcc i.e. part of MinGW/Strawberry.

Not sure if some configuration flag can solve the problem, therefore hesitate to report it as "Inline::C" bug, esp. since my C is rudimentary. Didn't find any mention of "asprintf" in whole Inline or Inline::C distributions. I understand "asprintf" is not necessary, but avoiding it doesn't feel right.

Replies are listed 'Best First'.
Re: Inline::C & Strawberry: can't free a buffer allocated with asprintf
by bliako (Monsignor) on Jan 01, 2020 at 22:43 UTC

    You must use the same family of malloc()/free() functions.

    What your program does, I guess, is that asprintf() mallocs with one memory system and frees with another. The culprit, as Corion said, is Perl re-definining free() to use Perl's memory pools. But you are not allocating memory with Perl's (same-family as your free()) malloc. Because memory allocation in your case happens within asprintf() which is in a library which is not affected by macro definitions (the #define construct) in the calling program.

    I guess, if you had the following code it would have worked (because it uses the same malloc/free family - whatever that family is):

    use strict; use warnings; use Inline C => <<'END_OF_C'; void foo() { char *s = (char *)malloc(100); sprintf(s, "%s", "hello there"); printf("'%s'\n", s); free(s); } END_OF_C foo(); __END__

    What can you do?: See Threads: Free to wrong pool for a possible solution.

    Your question opens a new one (for me): Why doesn't Perl use perl_free() and perl_malloc() (hypothetical names) internally instead of hijacking C's function names? Somewhat relevant is my problem Inline::CPP + OpenCV = problems (edit: which still seeks a robust solution). Can Inlining not be so tightly bound to Perl's internals?

    Edit: this also may be of use: Make Perl use real malloc

    More Edits 12hrs after:

    This C program overrides C's (or shall I say the compiler's) free() implementation. It works because the linker in this case first checks the program to resolve symbols and then resorts to external libraries (can you rely that this is common across all compilers?)

    // gcc y.c #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> void free(void *ptr); void free(void *ptr){ printf("my free called\n"); } void foo() { char *s = (char *)malloc(100); sprintf(s, "%s", "hello there"); printf("'%s'\n", s); free(s); } void main(void){ foo(); }
    % a.out 'hello there' my free called

    And this also works (as a standalone C program - and not via Inline::C)

    // gcc x.c #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #define free myfree void myfree(void *ptr); void myfree(void *ptr){ printf("my free called\n"); } void foo() { char *s = (char *)malloc(100); sprintf(s, "%s", "hello there"); printf("'%s'\n", s); free(s); } void main(void){ foo(); }

    However, when inserting the first code above into Perl via Inline::C it does not work. That is, my free called is not printed. This is because Inline::C compiles inline C code into a library which resides into the _Inline dir in current dir (for my linux system). Now it depends solely on the linker (or shall I say Inline::C's Makefile) which library to look first in order to resolve free(): (1) our library just created or (2) compiler's runtime. In my system it goes first to the runtime. But the linker can be instructed to do otherwise by LD_PRELOAD for GCC/Linux. syphilis below shows how to do that. BTW: this is how I made RealPlayer to save radio streams to files as well as playing them, some time in the last century... happy hacking

    bw, bliako

Re: Inline::C & Strawberry: can't free a buffer allocated with asprintf
by Corion (Patriarch) on Jan 01, 2020 at 19:08 UTC

    Most likely, free() has been replaced/redefined to another function, maybe because this Perl was compiled with -Dusemymalloc. I'm not sure how you can get back the "original" free(), but maybe even a simple #undefine free already is enough?

      Thank you everyone for answers. Either of #undef free or __mingw_str_free(s); indeed work as expected (don't die, do de-allocate). In the end, asprintf brings more mess than convenience in this environment, so I'd rather revert to manual allocation/concatenation etc. :)

        asprintf is just one of many functions in C which will allocate memory and expect the caller to free it, so this is bound to happen again with other functions. Using __mingw_str_free() will cause portability issues.

Re: Inline::C & Strawberry: can't free a buffer allocated with asprintf
by syphilis (Archbishop) on Jan 02, 2020 at 01:53 UTC
Re: Inline::C & Strawberry: can't free a buffer allocated with asprintf
by syphilis (Archbishop) on Jan 02, 2020 at 03:51 UTC
    For an alternative solution, you can create your own library that provides the free() implementation:
    C:\pscrpt> type free.c #include <malloc.h> void C_free(void * ptr) { free(ptr); } C:\pscrpt> gcc -c free.c C:\pscrpt> ar rcs libC_free.a -o free.o C:\pscrpt> type try2.pl use strict; use warnings; BEGIN{ use Cwd; $m::cwd = getcwd() } use Inline C => Config => LIBS => "-L$m::cwd -lC_free"; use Inline C => <<'END_OF_C'; #define _GNU_SOURCE void foo() { char *s; asprintf(&s, "%s", "foo"); C_free(s); } END_OF_C foo(); __END__ C:\pscrpt> perl try2.pl C:\pscrpt>
    The success of that script (try2.pl) relies on it being in the same directory as libC_free.a.
    Otherwise, modify the LIBS arg in the Inline Config section as necessary.

    Cheers,
    Rob

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11110841]
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (6)
As of 2024-03-28 19:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found