0

I am developing an embedded system with GCC, and would like to only use a few symbols from libc. For instance, I would like to use the basic memcpy, memmove, memset, strlen, strcpy, etc. However, I would like to provide my own (smaller) printf function, so I do not want libc to privide printf. I don't want dynamic allocation in this platform, so I do not want malloc to resolve at all.

Is there a way to tell GCC "only provide these symbols" from libc?

edit: To be clear, I am asking if there is a way I can only provide a few specific symbols from a library, not just override a library function with my own implementation. If the code uses a symbol that is in the library but not specified, the linker should fail with "unresolved symbol". If another question explains how to do this, I haven't yet seen it.

FazJaxton
  • 7,544
  • 6
  • 26
  • 32
  • [related question](http://stackoverflow.com/questions/29391965/what-is-partial-linking-in-gnu-linker) and [another related question](http://stackoverflow.com/questions/270984/g-partial-linking-instead-of-archives) – Kenney Feb 28 '16 at 20:18
  • 1
    Possible duplicate of [How to replace C standard library function ?](http://stackoverflow.com/questions/9107259/how-to-replace-c-standard-library-function) – Mike Kinghan Feb 28 '16 at 22:49
  • I don't consider this a duplicate, as my question is "can I tell the linker to only use a subset of symbols from a library", not "can I replace this specific function." The main difference is that in my question, the linker should bail out with an error if a symbol is used that has not been explicitly named from the library. – FazJaxton Feb 29 '16 at 16:52

2 Answers2

2

This should happen "automatically" as long as your libc and linker setup supports it. You haven't told what your platform is, so here is one where it does work.

So, let's create a silly example using snprintf.

/*
 * main.c
 */
#include <stdio.h>

int main(int argc, char **argv) {
  char l[100];
  snprintf(l, 100, "%s %d\n", argv[0], argc);
  return 0;
}

try to compile and link it

$ CC=/opt/gcc-arm-none-eabi-4_7-2013q3/bin/arm-none-eabi-gcc
$ CFLAGS="-mcpu=arm926ej-s -Wall -Wextra -O6"
$ LDFLAGS="-nostartfiles -L. -Wl,--gc-sections,-emain"
$ $CC $CFLAGS -c main.c -o main.o
$ $CC $LDFLAGS main.o -o example
/opt/gcc-arm-none-eabi-4_7-2013q3/bin/../lib/gcc/arm-none-eabi/4.7.4/../../../../arm-none-eabi/lib/libc.a(lib_a-sbrkr.o): In function `_sbrk_r':
sbrkr.c:(.text._sbrk_r+0x18): undefined reference to `_sbrk'
collect2: error: ld returned 1 exit status

It needs _sbrk because newlib *printf functions use malloc which needs a way to allocate system memory. Let's provide it a dummy one.

/* 
 * sbrk.c
 */
#include <stdint.h>
#include <unistd.h>
void *_sbrk(intptr_t increment) {
  return 0;
}

and compile it

$ $CC $CFLAGS -c sbrk.c -o sbrk.o
$ $CC $LDFLAGS -Wl,-Map,"sbrk.map" main.o sbrk.o -o with-sbrk
$ /opt/gcc-arm-none-eabi-4_7-2013q3/bin/arm-none-eabi-size with-sbrk
   text    data     bss     dec     hex filename
  28956    2164      56   31176    79c8 with-sbrk

Well, that's the reason you'd like to get rid of printf and friends, isn't it? Now, replace snprintf with our function

/* 
 * replace.c
 */
#include <stdio.h> 
#include <string.h>
int snprintf(char *str, size_t size, const char *format, ...) {
  return strlen(format);
}

then compile

$ $CC $CFLAGS -c replace.c -o replace.o
$ $CC $LDFLAGS -Wl,-Map,"replace.map" main.o replace.o -o with-replace
$ /opt/gcc-arm-none-eabi-4_7-2013q3/bin/arm-none-eabi-size with-sbrk
   text    data     bss     dec     hex filename
    180       0       0     180      b4 with-replace

Note that we did not use the _sbrk stub at all. As long as you don't provide _sbrk, you can be sure that malloc is not (can't be) linked and used.

-1

The simplest solution is probably to use a wrapper which defines the symbols and resolves them at runtime using dlfcn:

#include <dlfcn.h>

void* (*memcpy)(void *dest, const void *src, size_t n);
char* (*strncpy)(char *dest, const char *src, size_t n);
...

void init_symbols (void) {
    void *handle = dlopen("/lib/libc.so.6", RTLD_LAZY);

    memcpy = dlsym(handle, "memcpy");
    strncpy = dlsym(handle, "strncpy");
    ...
}

and link your binary with -nostdlib. This gives you the best control on which symbols to use from which source.

Ctx
  • 18,090
  • 24
  • 36
  • 51
  • The question is tagged embedded. There is likely not even a filesystem available (OP is clearly after shrinking the code, which is a clear hint). How do you think dynamically loading a library would work? – too honest for this site Feb 29 '16 at 00:10