Search code examples
clinuxgccmakefile

Compilation error with a shared library in C


I'm currently coding a shared library in C, I'm trying to compile with a test hand next to it but I get the following error:

gcc -Wall -Wextra -Werror -g3 -I../includes -L../build -lc main.o -o test
/usr/bin/ld: main.o: in the "main" function :
main.c:(.text+0x27): undefined reference to "my_strlen".
collect2: error: ld returned output status 1
make: *** [Makefile:12: test] Error 1

Here's my project tree :

.
├── build
│   └── vlibc.so
├── includes
│   └── vlibc.h
├── Makefile
├── src
│   ├── memory
│   │   ├── memdump.c
│   │   ├── memdump.o
│   │   ├── my_free.c
│   │   ├── my_free.o
│   │   ├── my_memcmp.c
│   │   ├── my_memcmp.o
│   │   ├── my_memcpy.c
│   │   ├── my_memcpy.o
│   │   ├── my_memove.c
│   │   ├── my_memove.o
│   │   ├── my_memset.c
│   │   └── my_memset.o
│   └── string
│       ├── my_strlen.c
│       └── my_strlen.o
└── tests
    ├── main.c
    ├── main.o
    └── Makefile

Here is the Makefile of the test binary :

INC = -I../includes
EXE_NAME = test
SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)
CC = gcc
CFLAGS = -Wall -Wextra -Werror -g3
LFLAGS = -L../build -lc

all: $(EXE_NAME)

$(EXE_NAME): $(OBJ)
    $(CC) $(CFLAGS) $(INC) $(LFLAGS) $^ -o $@

%.o: %.c
    $(CC) $(CFLAGS) $(INC) -c $< -o $@

clean:
    rm -f $(OBJ)

clean-all: clean
    rm -f $(EXE_NAME)

re-lib:
    $(MAKE) -C .. re
    $(MAKE) -C .. clean

re: re-lib all clean

My lib's Makefile :

CC              =   gcc
CFLAGS          =   -Wall -Wextra -Werror -g3
INC             =   -I./includes
SRC             :=  $(shell find src/ -type f -name '*.c')
OBJ             :=  $(SRC:.c=.o)
BUILD_DIR       =   build/
LIB_NAME        =   vlibc.so

START_TIME      :=  $(shell date +%s%3N)

FORCE_REBUILD   ?=  0

GREEN = \033[1;32m
YELLOW = \033[1;33m
CYAN = \033[1;36m
RESET = \033[0m

ifeq ($(FORCE_REBUILD),1)
    OBJ :=
endif

$(BUILD_DIR)$(LIB_NAME): $(OBJ)
    @$(CC) -fno-builtin -shared -o $@ $(OBJ) $(CFLAGS) $(INC) $(args)
    @END_TIME=$$(date +%s%3N); \
    echo -e "$(GREEN)[VLibC]$(RESET) - $(YELLOW)[INFO]$(RESET) "\
    "Compilation done in $(CYAN)$$(($$END_TIME - $(START_TIME))) ms$(RESET)"

%.o: %.c
    @$(CC) $(CFLAGS) $(INC) -fPIC -c $< -o $@

clean:
    @rm -f $(OBJ)

fclean: clean
    @rm -f $(BUILD_DIR)$(LIB_NAME)

re: FORCE_REBUILD = 1
re: fclean $(BUILD_DIR)$(LIB_NAME)

.PHONY: clean fclean re

I'd like to point out that I don't encounter any errors when compiling my lib. Also, my_strlen() is included in the header file.

vlibc.h :

#ifndef VLIBC_VERBOSE
    #define VLIBC_VERBOSE 0
#endif

#ifndef VLIBC_H_
    #define VLIBC_H_

/* Includes */
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>

/* Colors */
#define COLOR_SIZE 8
#define COLOR_RESET_SIZE 5
#define BLACK "\033[0;30m"
#define RED "\033[0;31m"
#define GREEN "\033[0;32m"
#define YELLOW "\033[0;33m"
#define BLUE "\033[0;34m"
#define RESET "\033[0m"

/* Log's prompt*/
#define ERROR "\033[0;32m[VLibC]\033[0m - \033[0;31m[ERROR]\033[0m"
#define WARNING "\033[0;32m[VLibC]\033[0m - \033[0;33m[WARNING]\033[0m"
#define INFO "\033[0;32m[VLibC]\033[0m - \033[0;34m[INFO]\033[0m"

/* System's macros*/
#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})

/* Magic numbers */
#define JPEG_MAGIC_NUMBER_SIZE 3
#define JPEG_MAGIC_NUMBER {0xFF, 0xD8, 0xFF}
#define ELF_MAGIC_NUMBER_SIZE 16
#define ELF_MAGIC_NUMBER {0x7f, 0x45, 0x4c, 0x46, \
    0x01, 0x01, 0x01, 0x00, \
    0x00, 0x00, 0x00, 0x00, \
    0x00, 0x00, 0x00, 0x00}
/* Typedef */
typedef unsigned char byte;
typedef byte *byte_ptr;

/* Memory */
void memdump(void *p, size_t size, bool color);
void my_free(void **ptr);
int my_memcmp(const void *s1, const void *s2, size_t n);
void *my_memcpy(void *dest, const void *src, size_t n);
void *my_memmove(void *dest, const void *src, size_t n);
void *my_memset(void *s, int c, size_t n);

/* String */
size_t my_strlen(const char *str);

#endif

main.c :

#include <vlibc.h>
#include <stdio.h>
#include <string.h>

int main(int ac, char **av, char **env)
{
    (void) ac, (void) av, (void) env;
    const char *str = "Hello, World!\n";
    printf("GLIBC strlen : %zu, VLIBC strlen : %zu\n", strlen(str), my_strlen(str));
    return (0);
}

[UPDATE]

Following the comments I have replaced LFLAGS = -L../build -lc with LFLAGS = -L../build -lvlibc I now get the following error:

gcc -Wall -Wextra -Werror -g3 -I../includes -L../build -lvlibc main.o -o test
/usr/bin/ld: can't find -lvlibc: No such file or folder
collect2: error: ld returned output status 1
make: *** [Makefile:12: test] Error 1

Yet vlibc.so exists in ../build


Solution

  • The original problem:

    gcc -Wall -Wextra -Werror -g3 -I../includes -L../build -lc main.o -o test
    
    1. You have -lc in the wrong place (it should follow main.o, not precede it).
    2. You are not linking against vlibc.so

    The new problem:

    gcc -Wall -Wextra -Werror -g3 -I../includes -L../build -lvlibc main.o -o test
    

    The argument -lvlibc tells the linker: find a library named libvlibc.a or libvlibc.so and link against that. Since no such library exists, the linker complains: can't find -lvlibc.

    Solution: don't name your library vlibc.so, name it libvlibc.so, so you don't have to do anything special at link time.

    If for some reason you insist on naming the library without lib prefix, you can still link against it with either:

    gcc -Wall -Wextra -Werror -g3 -I../includes -L../build main.o -l:vlibc.so -o test
    

    or

    gcc -Wall -Wextra -Werror -g3 -I../includes main.o ../build/vlibc.so -o test
    

    Note: you will probably also need to either add -rpath (preferred) or set LD_LIBRARY_PATH so the dynamic linker can find {lib}vlibc.so at runtime.