Erlang Driver Errors: undefined symbol : main

I have to write an Erlang driver so I started by triying a little one : driverc.c , the code is simple, the Driver Erlang Data is the address of a long number and each call to the driver is to increment this number by n or multipliying this number by n, n is just one digit number (0,…, 9) and is passed as char , the code is:

#include<stdio.h>
#include<stdlib.h>
#include<erl_driver.h>
#include<ei.h>
#include<string.h>

#define SOMME 1
#define PRODUIT 2

static ErlDrvData start(ErlDrvPort port, char *command) ;

static void stop(ErlDrvData data) ;

static ErlDrvSSizeT control(ErlDrvData data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen);

static long *number;

static ErlDrvEntry myentry={
NULL,
start,
stop,
NULL,
NULL,
NULL,
"driverc",
NULL,
NULL,
control,
NULL,
NULL,
NULL,
NULL,
NULL
};

DRIVER_INIT(driverc)
{return &myentry;}

ErlDrvData start(ErlDrvPort port, char *command)
{number=(long*)driver_alloc(sizeof(long)) ;
*number=0;
set_port_control_flags(port,PORT_CONTROL_FLAG_BINARY);
return (ErlDrvData)number;
}

void stop(ErlDrvData data)
{long *olddata;
 olddata=(long*)data;
 driver_free(olddata);
}

ErlDrvSSizeT control(ErlDrvData data, unsigned int command, char *buf, ErlDrvSizeT len, char ** rbuf, ErlDrvSizeT rlen)
{long reply, m, *olddata, newdata;
 int index, result, n;
 char c;

 c=*buf;
 n=c-'0'; /* convert character representation to true integer digit */
 m=(long)n;
 olddata=(long*)data;
 index=0;
 switch (command) {
 case SOMME : newdata=(*olddata)+m;
              *olddata=newdata;
              reply=newdata;
              break;

 case PRODUIT : newdata=(*olddata)*m;
                *olddata=newdata;
                reply=newdata;
                break;

 default : reply=-1;
                 break;
                   }
result=ei_encode_long(*rbuf,&index,reply) ;
 /* this function convert a long number (reply) to binary and write it in *rbuf */
return sizeof(reply) ;
} 

When I tried to compile it, it returns errors that I can’t know where is the problem, the shell output is below.

cc driverc.c  -I/usr/local/lib/erlang/usr/include

error : undefined symbol : main
referenced by .... 
error : undefined symbol : driver_alloc
referenced by .... 
error : undefined symbol : set_port_control_flags
referenced by .... 
error : undefined symbol : ei_encode_long
referenced by .... 
error : linked command failed with exit code 1 .... 
3 Likes

Aside from some problems I see in the code itself (typos, etc.) I don’t believe you’re including all the headers needed.

If I take your code and try to compile it on my mac like so :

 gcc -o driver.o -I /opt/homebrew/Cellar/erlang/24.1.4/lib/erlang/usr/include -I /opt/homebrew/Cellar/erlang/24.1.4/lib/erlang/lib/erl_interface-5.1/include/   -I /opt/homebrew/Cellar/erlang/24.1.4/lib/erlang/erts-12.1.4/include -fPIC   driver.c
foo.c:52:39: error: unknown type name 'insigned'; did you mean 'unsigned'?
ErlDrvSSizeT control(ErlDrvData data, insigned int command, char *buf, ErlDrvSizeT len, char **buf, ErlDrvSizeT rlen)
                                      ^~~~~~~~
                                      unsigned
foo.c:52:96: error: redefinition of parameter 'buf'
ErlDrvSSizeT control(ErlDrvData data, insigned int command, char *buf, ErlDrvSizeT len, char **buf, ErlDrvSizeT rlen)
                                                                                               ^
foo.c:52:67: note: previous declaration is here
ErlDrvSSizeT control(ErlDrvData data, insigned int command, char *buf, ErlDrvSizeT len, char **buf, ErlDrvSizeT rlen)
                                                                  ^
foo.c:52:96: warning: omitting the parameter name in a function definition is a C2x extension [-Wc2x-extensions]
ErlDrvSSizeT control(ErlDrvData data, insigned int command, char *buf, ErlDrvSizeT len, char **buf, ErlDrvSizeT rlen)
                                                                                               ^
foo.c:76:24: error: use of undeclared identifier 'rbuf'
result=ei_encode_long(*rbuf,&index,reply) ; /* this function convert a long number (reply) to binary and write it in *rbuf */
                       ^
1 warning and 3 errors generated.

We can see it does almost compile, just have to fix up the typos and other issues.

The paths to the includes might be different on your os than it is on mine, unless you’re compiling on mac against an installed version of erlang via homebrew of course :slight_smile:

3 Likes

Thank you for answer, It’s just some mistakes because I type from my phone but I didn’t any of these errors in my program I will edit it now and you can try

2 Likes

In Unix Systems all headers that can be needed to implement a dynamically loaded driver are found in the folder /usr/local/lib/erlang/usr/include

2 Likes

I do believe you will need the other include paths, at least I do on a linux a system. I think you’ll also need -shared -fpic as well.

gcc -o driver.o -I/usr/local/lib/erlang/usr/include -I/usr/local/lib/erlang/erts-11.1.8/include  -I/usr/local/lib/erlang/lib/erl_interface-4.0.2/include driver.c -shared -fpic

^ that compiles.

3 Likes

but why I need -fpic and - shared to resolve these errors ?

2 Likes

See here : Erlang -- How to Implement a Driver

3 Likes

Thank you, I read it before but I just want to know what is the relation between -fpic / -shared and errors in the program ? for example undefined symbol : driver_alloc ?

2 Likes

Because you are attempting to compile without linking to the libraries you are referencing, see here for further explanation Link Options (Using the GNU Compiler Collection (GCC)) Look for -shared

Long story short, the problems you are encountering don’t have much to do with erlang, erts, drivers, etc. and more to do with compiled shared objects, more specifically shared / objects that reference other shared / objects.

2 Likes

Yes I understand the idea the flag -shared is to get the c program shared between codes to be linked anywhere this is the meaning of a shared library but “inside” the code how can this flag causes changes to compilation ?

1 Like

You have right, all errors depends of functions imported from the included libraries (driver_alloc, set_port_control_flag,…), but how about simple C code that include “stdlib.h” or “strings.h” or… why we don’t use -shared and no problem ?

1 Like

You don’t get any issues with stdlib because your code automatically depends on libc, which provides those functions. However, if you were to use any code that is not provided by libc you will have similar problems.

5 Likes

I think I understand but it’s still unclear when using the header file ei.h to exploit of it’s functions I see that the source code for all it’s functions doesn’t contain any body just the function declaration, what is this ? and how can the program use these functions without body ?

2 Likes

What you see there are C prototypes. They define an interface, but not what the function does. The actual code is located in the library that implements that prototype.

I would recommend that you learn more about C before venturing forwards. As someone else mentioned here, all of the problems you have seem to be due to not knowing how C works. Try building some small example executables and libraries and getting them to link and work together without involving Erlang. There are plenty of resources only to teach you how to do that.

4 Likes

Thank you, I know that it’s just an API but since I know that it’s not a part of Standard C Library can you tell me which library will implement these functions ?

3 Likes

It is part of the Erl_interface application. You can read more about how to link and use that here: Erlang -- Erl_Interface User's Guide

4 Likes

Exactly I try to understand how C works so I created one final program that include ei.h in it’s main and still undefined functions

3 Likes

Thank you so much @garazdawi you are great and you helped me a lot

3 Likes

Ooookey okey, I think I started to understand your precendent reply @garazdawi, there is no issue when using headers from Standard C Library because function’s implementations is statically loaded from the language’s core itself but here when I don’t use -shared that means that it’s a final C program and since no function’s implementation I had the “undefined symbol” error but when using -shared the compiler still wait another C program ( that came later from Erlang) to implements functions, Iam in the right way ?

3 Likes

That’s correct :slight_smile: All the undefined symbols will end up in the dynamic symbol table of your shared object (it’s more nuanced than that, but it’s a decent way to put it I suppose), they are expected to be present before your shared object is loaded. They will be present per being in beam.smp, which will load your shared object when instructed to.

There’s a whole lot to unpack in all this, but I think all this is still unrelated to Erlang or OTP, they are more questions about C in general and belong on another forum, such as stackoverflow.

Let’s try to stay on topic going forward! :hugs:

4 Likes