Alex K
Alex K

Reputation: 171

Making a function that defaults to aliasing an externally defined symbol in gcc/ld

I have a header-only library that's currently calling malloc and free

This header is included in a lot of different static libraries, which are used to build differently configured programs.

I would like to be able to replace those calls with calls into another allocator, at link time -- based on whether that allocator library is included in the link step, without affecting other calls to malloc and free.

My idea is to have the library call customizable_malloc and customizable_free and have those symbols resolve to malloc and free "by default" -- then the allocator library can provide alternate definitions for customizable_malloc and customizable_free

However, I messed around with weak/alias/weakref attributes and I can't seem to get anything to work. Is there a way to do this?

Note: I know I can create an extra layer of indirection: customizable_malloc could be a weak alias to a function that calls malloc. But that adds a level of indirection that seems unnecessary.

Ideally, here's the steps I want the linker to take when it comes across a call to customizable_malloc:

  1. Check if a definition for customizable_malloc exists
  2. If it does, call it
  3. If it does not, behave as if the call was to regular malloc.

Clarifying note: In a single-target scenario, this could be done with #define. The library could create macros customizable_malloc and customizable_free that default to malloc and free. However, this doesn't work in this case since things are being built into static libraries without knowledge of whether there's an override.

Upvotes: 0

Views: 1518

Answers (2)

Employed Russian
Employed Russian

Reputation: 213526

You can achieve desired outcome using GNU-ld --defsym option.

Example:

#include <malloc.h>
#include <stdio.h>

void *custom_malloc(size_t sz);

int main()
{
  void *p = custom_malloc(1);
  void *q = malloc(42);  // important: malloc needs to be referenced somewhere
  printf("p = %p, q = %p\n", p, q);
  return 0;
}

Compiling this with gcc -c t.c will (naturally) fail to link with unresolved reference to custom_malloc (if the library providing custom_malloc is not used):

$ gcc t.o 
/usr/bin/ld: t.o: in function `main':
t.c:(.text+0xe): undefined reference to `custom_malloc'
collect2: error: ld returned 1 exit status

Adding --defsym=custom_malloc=malloc solves this:

$ gcc t.o -Wl,--defsym=custom_malloc=malloc && ./a.out
p = 0x558ca4dc22a0, q = 0x558ca4dc22c0

P.S. If malloc is not linked into the program (i.e. if I comment out the // important line), then --defsym fails:

$ gcc t.c -Wl,--defsym=custom_malloc=malloc && ./a.ou
/usr/bin/ld:--defsym:1: unresolvable symbol `malloc' referenced in expression
...

But that is (I believe) not very relevant to your scenario.

P.P.S. As R correctly stated, the "extra level of indirection" could be a single unconditional JMP malloc instruction, and the overhead of such indirection is unlikely to be measurable.

Upvotes: 2

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215259

The extra level of indirection is the only way to do it. ELF (and other real-world binary format) symbol definition syntax (including for weak symbols) does not provide any way to provide a definition in terms of a reference to an external definition from somewhere else.

Just do the wrapper approach you're considering. It's simple, clean, and relative to the cost of malloc/free it's not going to make any big difference in performance.

Upvotes: 2

Related Questions