1

TL;DR: I'm working on a C exercise that uses dlfcn.h to open shared libraries. Despite adding (what I think are) the correct flags based on other posts, I'm still getting undefined reference to errors for dlopen, dlsym and a few other functions defined in dlfcn.h (error message and make-file are included below).

I've obv included the following at the start of my .c file: #include <dlfcn.h>

What am I doing wrong?

Detailed version:

I'm working on exercise 30 of Learn C The Hard Way and am puzzled as to what else I need to do in order to get the libex29_tests.c program (very last section of code on the page) to compile correctly. As you can probably tell, shared libraries/makefiles/compiler flags are new to me at the moment.

What I've tried so far: Based on the following posts, I've tried adding the -ldl flag by adding LIBS=-ldl fPIC and/or LDFLAGS+=-ldl to various parts of my Makefile but am still having issues. The version of the makefile in the book (included below, as reference) does include an -ldl flag albeit via slightly different syntax. In any case, I continue to get the same error message.

Any suggestions?


These are the errors that I get. These errors assume the version of the makefile included below. However, as I'd mentioned, changing the syntax for the -ldl flag results in virtually the same error message(s).

~/.../lchw/ex30_automated$ make
cc -std=gnu99 -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  -fPIC   -c -o src/libex29.o src/libex29.c
src/libex29.c: In function ‘fail_on_purpose’:
src/libex29.c:42:33: warning: unused parameter ‘msg’ [-Wunused-parameter]
 int fail_on_purpose(const char *msg)
                                 ^
ar rcs build/libYOUR_LIBRARY.a src/libex29.o
ranlib build/libYOUR_LIBRARY.a
cc -shared -o build/libYOUR_LIBRARY.so src/libex29.o
cc -std=gnu99 -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG  build/libYOUR_LIBRARY.a    tests/libex29_tests.c   -o tests/libex29_tests
In file included from tests/libex29_tests.c:1:0:
tests/libex29_tests.c: In function ‘main’:
src/minunit.h:14:38: warning: parameter ‘argc’ set but not used [-Wunused-but-set-parameter]
 #define RUN_TESTS(name) int main(int argc, char *argv[]) {\
                                      ^
tests/libex29_tests.c:64:1: note: in expansion of macro ‘RUN_TESTS’
 RUN_TESTS(all_tests);
 ^
/tmp/dchaudh/ccwzxpC3.o: In function `check_function':
/home/dchaudh/Dropbox/dchaudh/wc/lchw/ex30_automated/tests/libex29_tests.c:10: undefined reference to `dlsym'
/home/dchaudh/Dropbox/dchaudh/wc/lchw/ex30_automated/tests/libex29_tests.c:11: undefined reference to `dlerror'
/tmp/dchaudh/ccwzxpC3.o: In function `test_dlopen':
/home/dchaudh/Dropbox/dchaudh/wc/lchw/ex30_automated/tests/libex29_tests.c:23: undefined reference to `dlopen'
/tmp/dchaudh/ccwzxpC3.o: In function `test_dlclose':
/home/dchaudh/Dropbox/dchaudh/wc/lchw/ex30_automated/tests/libex29_tests.c:46: undefined reference to `dlclose'
collect2: error: ld returned 1 exit status
make: *** [tests/libex29_tests] Error 1

Here's the latest makefile as per the book. It does include the -ldl flag, as you can see on the second line.

CFLAGS=-std=gnu99 -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
PREFIX?=/usr/local

SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))

TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))

TARGET=build/libYOUR_LIBRARY.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))

# The Target Build
all: $(TARGET) $(SO_TARGET) tests

dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all

$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
    ar rcs $@ $(OBJECTS)
    ranlib $@

$(SO_TARGET): $(TARGET) $(OBJECTS)
    $(CC) -shared -o $@ $(OBJECTS)

build:
    @mkdir -p build
    @mkdir -p bin

# The Unit Tests
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
    sh ./tests/runtests.sh

valgrind:
    VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE)

# The Cleaner
clean:
    rm -rf build $(OBJECTS) $(TESTS)
    rm -f tests/tests.log
    find . -name "*.gc*" -exec rm {} \;
    rm -rf `find . -name "*.dSYM" -print`

# The Install
install: all
    install -d $(DESTDIR)/$(PREFIX)/lib/
    install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/

# The Checker
BADFUNCS='[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)'
check:
    @echo Files with potentially dangerous functions.
    @egrep $(BADFUNCS) $(SOURCES) || true
Community
  • 1
  • 1
iceman
  • 2,020
  • 2
  • 17
  • 24

1 Answers1

7

${LIBS} (and your -ldl) is nowhere used in the makefile. It seems, you are using builtin rules for compiling and can use the LDLIBS instead of LIBS for the name of this variable.

Beside this, the

  tests: CFLAGS += $(TARGET)

in your example is wrong because TARGET is a library which must go into (LD)LIBS.

$(TARGET): build $(OBJECTS)
build:
    @mkdir -p build

is not sane either. This will break in parallel builds ($(OBJECTS) should depend on build) and will cause unneeded builds (build is a directory and alterered by every compile/link operation). Plain build will be problematic for VPATH build and I suggest to use

$(OBJECTS): | build/.dirstamp
build/.dirstamp:
    mkdir ${@D}

here.

ensc
  • 6,704
  • 14
  • 22
  • 1
    I managed to get this example to work by changing line 2 to `DLIBS=-ldl $(OPTLIBS)` and `tests: CFLAGS += $(TARGET)` to `tests: LDLIBS += $(TARGET)`. Make executes compile command assembled like this: "cc {CFLAGS} program.c {LDLIBS} -o program", in this exampple this is `cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG tests/libex29_tests.c -ldl build/libYOUR_LIBRARY.a -o tests/libex29_tests` – Luke 10X Nov 02 '14 at 00:05