::malloc has not been declared

Question

What does it mean when I encounter the compiler error “::malloc has not been declared”? This appears to occur in a C++ standard library header file! Is the standard library broken?

Answer

No, but your configure.ac probably is. While there may be other causes of this same error, here’s what happened for me.

First, the short answer is that the problem is the AC_FUNC_MALLOC directive in the project’s autoconf configuration (configure.ac). If you simply remove that line, this error will go away. But there is an underlying issue that is causing this directive to do the wrong thing, and you would be better off addressing that instead.

Before I get to that, let’s explain why AC_FUNC_MALLOC can break your code in this bizarre way. (Or, you can skip this and go directly the the root cause.)

The purpose of the AC_FUNC_MALLOC directive is to test if you have a GNU-compatible malloc function that returns a non-NULL result for malloc(0). If your system appears to return NULL for this call, your autoconf-generated config.h will include the line

#define malloc rpl_malloc

And you are expected to create an rpl_malloc function that behaves in the way that you expect.

If you were writing a C program, the error would look different. You’d probably get a complaint about an undefined rpl_malloc function at link-time. However, if you use C++ headers, you may directly or indirectly end up including the header cstdlib. One of the things that this header does is import C standard library functions such as malloc, exit, rand, and the like into the std:: namespace (so that you can write std::malloc or std::exit). The way it does this (at least in the GNU C++ libraries) is as follows:

#include 

#undef malloc
#undef exit
#undef rand
// ... etc ...

namespace std {
  using ::malloc;
  using ::exit;
  using ::rand;
  // ... etc ...
}

Note that it explicitly gets rid of any redefinitions or macro implementations of these functions so that the versions in the std:: will be the “real” C library functions. Here’s where this breaks: suppose you have a source file that starts like this:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include 

// ... etc ...

And suppose that config.h had the redefinition of malloc as rpl_malloc in it, due to a faulty use of AC_FUNC_MALLOC. Well, the C++ header cstdlib includes the C header stdlib.h, but the prototype for malloc that appears in stdlib.h was replaced by a prototype for rpl_malloc, courtesy of the pre-processor. As I said, in a C program, this would lead to the more understandable error about an undefined rpl_malloc function at link-time.

However, before the using ::malloc; line in cstdlib is reached, cstdlib #undefs the malloc to rpl_malloc replacement, and so the using ::malloc line does not get replaced by using ::rpl_malloc. So the C++ using directive is trying to reference a function whose prototype has been changed to use a different name, and so the compiler correctly (albeit befuddlingly) reports that ::malloc was never declared.

You can observe the problem using this test program:

// This is what config.h might do:
#define malloc rpl_malloc

// This is what cstdlib does:
#include 

#undef malloc

namespace std {
  using ::malloc;
}

// Your program:
int main()
{
  int* i = static_cast(std::malloc(sizeof(int)));
  return 0;
}

This gives the “::malloc has not been declared” compiler error on line 10. If you comment out or remove the #undef malloc line, you will instead see a linker error about an undefined reference to rpl_malloc.


Now that you understand what AC_FUNC_MALLOC is supposed to do and why it breaks your C++ code when it detects a non-GNU malloc() implementation, you need to figure out why it doesn’t think you have a GNU-compatible malloc() (assuming you actually do).

In my case, the problem was having a library in a nonstandard directory. Among other dependencies, my application Foo had a dependency on libbar, a library that I had written, and my configure.ac was set up to detect the presence of libbar. The caveat was that libbar was not installed in /usr/lib, it was installed in an alternate prefix, let’s say /opt/test/lib.

When configure tries to test for the presence or GNU-compatibility of malloc and other functions, it compiles a small test program and then tries to run it to see if it works. The problem is that if it fails, configure isn’t smart enough to understand if the reason for failure was related to what it was trying to test or not. These programs are also linked with the same linker flags that configure will give to your application. So in particular, configure compiled the malloc test program with -lbar. Then, when it tried to execute the test program, the program failed, because even though /opt/test was given in the configure --prefix argument (which means it gets into the include and link paths), /opt/test/lib was not in the library load path, and so the test program could not find libbar to load. You can see this in config.log:

configure:17635: checking for GNU libc compatible malloc
configure:17669: g++ -o conftest -g -O2 -I/opt/test/include -L/opt/test/lib conftest.cpp -lbar >&5
configure:17672: $? = 0
configure:17678: ./conftest
./conftest: error while loading shared libraries: libbar.so.1: cannot open shared library object file: No such file or directory
configure:17681: $? = 127
configure: program exited with status 127
configure: failed program was:
[program listing omitted]
configure:17697: result: no

And this “no” result causes the dreaded #define malloc rpl_malloc in config.h. Incidentally, this same issue was also causing tests related to closedir, lstat and stat to fail spuriously, but without breaking my build in the process.

The moral of the story? If you’re using autoconf with an alternate --prefix argument, and your configure.ac uses AC_FUNC_ directives, be sure that your prefix’s lib directory is in your LD_LIBRARY_PATH when configuring. One way to achieve this is by executing

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/test/lib

(replacing /opt/test/lib with whatever is appropriate) at the command line of the shell that you’re compiling from.

You may also consider, for a C++ application, just removing the AC_FUNC_ directives entirely. Clearly (from the explanation above), if they do indeed correctly detect a non-GNU implementation, the C-oriented workaround that they provide for you may not even be capable of working with C++ standard library headers, depending on how exactly the headers are implemented on the target platform.

It’s worth noting that this problem does not break autoconf’s library detection mechanism (the AC_CHECK_LIB directives) because during those tests, the configure script is only interested in whether or not the test program compiles, not whether or not it runs.


Share this content on:

Facebooktwittergoogle_plusredditpinterestlinkedinmailFacebooktwittergoogle_plusredditpinterestlinkedinmail

4 comments

  1. Thank you for the very detailed explanation. I’ve also hit these type of errors, and sometimes, such as this one, this fix also works for me:

    ac_cv_func_realloc_0_nonnull=yes
    ac_cv_func_malloc_0_nonnull=yes
    ./configure …

    This prevents the tests for malloc and realloc from running, and assuming you do have GNU malloc and realloc, everything seems fine.

  2. So, there’s another side of the problem…

    When cross compiling, the configure test just *assumes* there is no compatibility between your malloc and GNU’s (i.e. malloc() returns 0 with no parameters). Check out this bit of code from configure:


    if test “$cross_compiling” = yes; then :
    ac_cv_func_malloc_0_nonnull=no

    Crap! That’s not necessarily true, AutoTools! 🙁

    But what are we to do? That’s definitely the safer option, but setting ac_cv_func_[m|re]alloc_0_nonnull=yes doesn’t really feel right either…

    I think it means that you should only use the AC_FUNC_[M|RE]ALLOC macros if you plan on calling [m|re]alloc() with a null pointer. *More so*, you should only use these macros (and their friends???) if you are comfortable providing a replacement implementation (as noted in autotools documentation), because false negatives are absolutely bound to happen.

Leave a Reply

Your email address will not be published. Required fields are marked *