Undefined error of "enif_make_string" happens to erlang nif function

I am checking Nif function following Erlang erl_nif document and having an issue of compile error.
Could you please help me to solve this issue?

/* niftest.c */
#include <erl_nif.h>
static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
      { return enif_make_string(env, "Hello world!", ERL_NIF_LATIN1); }
static ErlNifFunc nif_funcs[] = { {"hello", 0, hello} };
ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)

compiling

$> gcc -fPIC -shared -o niftest.so niftest.c -I $ERL_ROOT/usr/include/
Undefined symbols for architecture x86_64:
 “_enif_make_string”, referenced from:
   _hello in niftest-cb9456.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation) 

machine: macOS Monetery
gcc: Apple clang version 13.1.6 (clang-1316.0.21.2)
erlang: 24.3
include file: ERL_ROOT=~/.asdf/installs/erlang/24.3

It is where undefined symbol “enif_make_string” is defined.
erl_nif_api_funcs.h

#ifdef ERL_NIF_API_FUNC_DECL
  ERL_NIF_API_FUNC_DECL(
      ERL_NIF_TERM,
      enif_make_string,
      (ErlNifEnv* env, const char* string, ErlNifCharEncoding)
  );
#endif

#ifdef ERL_NIF_API_FUNC_MACRO
    #define enif_make_string ERL_NIF_API_FUNC_MACRO(enif_make_string)
#endif

The following is where two switching Macros is defined.
ERL_NIF_API_FUNC_DECL, ERL_NIF_API_FUNC_MACRO
erl_nif.h

#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
  #define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct {
    #include "erl_nif_api_funcs.h"
    void* erts_alc_test;
  } TWinDynNifCallbacks;
  extern TWinDynNifCallbacks WinDynNifCallbacks;
  #undef ERL_NIF_API_FUNC_DECL
#endif
#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) 
        && !defined(STATIC_ERLANG_DRIVER) && !defined(STATIC_ERLANG_NIF)
  #define ERL_NIF_API_FUNC_MACRO(NAME) (WinDynNifCallbacks.NAME)
  #include "erl_nif_api_funcs.h"
  /* note that we have to keep ERL_NIF_API_FUNC_MACRO defined */
#else /* non windows or included from emulator itself */
  #define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) extern RET_TYPE NAME ARGS
  #include "erl_nif_api_funcs.h"
  #undef ERL_NIF_API_FUNC_DECL
#endif

I couldn’t find switching macros of “__WIN32__” anywhere although this looks WindowsOS macro.

This issue happened on Erlang 23.x and I installed 24.3. So installation doesn’t cause this.

Please help me to find what I am missing.
Thanks.

On MacOS you also need to pass -bundle -bundle_loader $ERL_ROOT/erts-*/bin/beam.smp to gcc/clang in order for it to work.

1 Like

Thanks a lot.

However, I still have an error.

 gcc -fPIC -shared -o niftest.so niftest.c -I $ERL_ROOT/usr/include/ -bundle -bundle_loader $ERL_ROOT/erts-12.3/bin/beam.smp

clang: error: invalid argument '-bundle' not allowed with '-dynamiclib'

I think this comes from using clang instead of gcc although I don’t know how -bundle works. I will check the option.

After checking again I think the -bundle is supposed to replace the -shared for clang. Does that work for you?

Thank garazdawi for your help.

The issue has been solved.

$ gcc -fPIC -o niftest.so niftest.c -I $ERL_ROOT/usr/include/ -bundle -bundle_loader $ERL_ROOT/erts-12.3/bin/beam.smp

$ ll niftest.so
-rwxr-xr-x  1 mickeyoh  staff    48K  6 Apr 13:09 niftest.so*

options (man ld - linker)

-bundle Produce a mach-o bundle that has file type MH_BUNDLE.
-bundle_loader executable
This specifies the executable that will be loading the bundle output file being linked. Undefined symbols from the bundle are checked against the specified executable like it was one of the dynamic libraries the bundle was linked with.