Search code examples
macosclangdylib

Compilation and linking issues on MacOS for canonical/libco - "Undefined symbols for architecture"


I'm on MacOS Catalina 10.15.3. I'm trying to build Caonical's dqlite as I want to try the experimental features of k3s which uses it as distributed database instead of etcd or others.
The error I'm seeing is with one of the dependencies, libco.

I built the library with the following commands:

clang libco.c -g -O2 -Wall -fPIC -c -DLIBCO_MP
clang libco.o -dynamiclib -Wl,-install_name,libco.dylib -o libco.dylib

and I have moved these files to /usr/local/lib/libco.dylib and /usr/local/include/libco.h respectively.

The issue is raised when I try to build a simple test file (please ignore errors in this file, I tried adapting this one as stand-alone test):

#include <libco.h>
#include <stdlib.h>
#include <assert.h>

/* Execution context of a test coroutine, passed using the global ctx
* variable. */
struct ctx
{
  cothread_t main; /* Reference to the main coroutine */
  int v1;
  int v2;
};

static struct ctx *ctx; /* Argument for test coroutines */

struct fixture
{
  cothread_t main;  /* Main coroutine */
  cothread_t coro1; /* First coroutine */
  cothread_t coro2; /* Second coroutine */
  struct ctx ctx1;  /* Context for first coroutine */
  struct ctx ctx2;  /* Context for second coroutine */
};

static void coro()
{
  struct ctx *c = ctx;
  c->v1 = 1;
  co_switch(c->main);
  c->v2 = 2;
  co_switch(c->main);
}

int main()
{
  struct fixture *f = malloc(sizeof *f);
  f->main = co_active();
  f->coro1 = co_create(1024 * 1024, coro);
  f->coro2 = co_create(1024 * 1024, coro);
  f->ctx1.main = f->main;
  f->ctx2.main = f->main;

  /* Start executing coro1 */
  ctx = &f->ctx1;
  co_switch(f->coro1);

  /* The v1 field of the context has been initialized, but v2 has not. */
  assert(f->ctx1.v1 == 1);
  assert(f->ctx1.v2 == 0);

  /* Start executing coro2 */
  ctx = &f->ctx2;
  co_switch(f->coro2);

  /* The v1 field of the second context has been initialized, but v2 has
   * not. */
  assert(f->ctx2.v1 == 1);
  assert(f->ctx2.v2 == 0);

  /* The fields of the first context are still the same. */
  assert(f->ctx1.v1 == 1);
  assert(f->ctx1.v2 == 0);

  /* Resume execution of coro2 */
  co_switch(f->coro2);

  /* The v2 field of the second context has been initialized too, but the
   * one of the first context still hasn't. */
  assert(f->ctx2.v1 == 1);
  assert(f->ctx2.v2 == 2);
  assert(f->ctx1.v1 == 1);
  assert(f->ctx1.v2 == 0);

  /* Resume execution of coro1 */
  co_switch(f->coro1);

  /* The v2 field of the first context has been initialized too now. */
  assert(f->ctx1.v1 == 1);
  assert(f->ctx1.v2 == 2);

  co_delete(f->coro1);
  co_delete(f->coro2);
  free(f);
  return 0;
}

Trying to compile using clang test.c -o test results in the following:

Undefined symbols for architecture x86_64:
  "_co_active", referenced from:
      _main in test-b98908.o
  "_co_create", referenced from:
      _main in test-b98908.o
  "_co_delete", referenced from:
      _main in test-b98908.o
  "_co_switch", referenced from:
      _main in test-b98908.o
      _coro in test-b98908.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Upon further inspection, however, I find that those symbols seem to be defined in the libco.dylib library:

$ lipo -info /usr/local/lib/libco.dylib
Non-fat file: /usr/local/lib/libco.dylib is architecture: x86_64

$ nm /usr/local/lib/libco.dylib
                 U ___assert_rtn
0000000000005058 d __dyld_private
                 U __tlv_bootstrap
0000000000001d60 T _co_active
0000000000005040 s _co_active_buffer
0000000000005070 s _co_active_buffer$tlv$init
0000000000005028 s _co_active_handle
0000000000005060 s _co_active_handle$tlv$init
0000000000001e60 T _co_create
0000000000001f20 T _co_delete
0000000000001da0 T _co_derive
0000000000001f50 T _co_serializable
0000000000005270 b _co_swap
0000000000002000 s _co_swap_function
0000000000001f30 T _co_switch
0000000000001f60 t _crash
0000000000001f70 t _crash.cold.1
                 U _free
                 U _malloc
                 U _mprotect
                 U _sysconf
                 U dyld_stub_binder

I guess the only option remaining is that the compiler isn't finding the library, so the issue could be a name or library path resolution issue but I can't seem to figure it out.
What am I missing?

Thank you.


Solution

  • You need to tell clang where the include files are, which directory the libraries are in and which libraries you want to use:

    clang program.c -o program -I/usr/local/include -L/usr/local/lib -lco