-1

I'm trying to call a C function foo() from a JNI native-lib.cpp file. foo() is declared in foo.h and defined in foo.c. native-lib.cpp already included "foo.h", so Android Studio seems to recognize that the function exists, but upon build, I get the following error:

error: undefined reference to 'foo(unsigned char const*, unsigned int*, int)'

foo() is declared as such in foo.h:

void foo(const unsigned char key[], unsigned int w[], int keysize);

foo() is called in native-lib.cpp as follows:

static unsigned char byteArray[32];
unsigned int intArray[64];
foo(byteArray, intArray, 256);

Am I missing anything? Why am I getting the error?

I also notice that when I open my foo.c, Android Studio informs me that "This file is not part of the project. Please include it in the appropriate build file (build.gradle, CMakeLists.txt or Android.mk etc.) and sync the project." How do I do that?

halfer
  • 19,824
  • 17
  • 99
  • 186
user1118764
  • 9,255
  • 18
  • 61
  • 113
  • *I'm trying to call a C function* -- Did you forget `extern "C"`? Otherwise that function is a C++ function, not a C function, and the linker is trying to find the C++ function called `foo()`, not the `C` function called `foo()`. – PaulMcKenzie Dec 05 '18 at 02:35
  • How do I use extern "C"? – user1118764 Dec 05 '18 at 02:59
  • [See this](https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c), and is probably a dup if it solves your issue. – PaulMcKenzie Dec 05 '18 at 03:00
  • Hi, I'm still unsure of where and how to place the extern C" statement. I tried enveloping my foo.c source with extern "C" { /* all C code here*/ } but I still get the same error. – user1118764 Dec 05 '18 at 03:04
  • Only the declaration needs to have `extern "C"`. The function declaration as you have it now is not correct for a C++ module if the function is actually a `C` function. – PaulMcKenzie Dec 05 '18 at 03:10
  • I also notice that when I open my foo.c, Android Studio informs me that "This file is not part of the project. Please include it in the appropriate build file (build.gradle, CMakeLists.txt or Android.mk etc.) and sync the project." How do I do that? – user1118764 Dec 05 '18 at 03:11
  • I do not do Android programming (have done JNI though), so I can't answer you on your project situation. What I do know is that the `C` function needs to be recognized as such, and the way you do that in a mixed C and C++ app is to have `extern "C"` used to declare the function. See my answer. – PaulMcKenzie Dec 05 '18 at 03:20

1 Answers1

1

The probable issue is that your foo() function is being recognized by the linker as a C++ function, and not a C function.

Since C++ functions can be overloaded, the C++ compiler "mangles" the name so that the linker has a unique key to the function. Any C++ module that encounters foo.h will assume the function is a C++ function, thus name mangling will be done on the function name.

For example, possibly the mangled name looks something like this in the compiled object code:

foo@ucharp@uintp@int

(each brand of C++ compiler will have its own naming scheme).

On the other hand, when the C compiler encounters foo.h, the compiled object code produced a symbol representing an "unmangled" foo name, more or less, this:

foo or _foo

So now at link time, since you called the foo function from C++ code, the linker will attempt to find the mangled version, and since there is no C++ function named foo, you get the linker error.

So basically your foo.h header or your usage of it needs to be smart and work for C modules and C++ modules, where the C++ modules are aware that foo is referring to a C function and thus will not mangle the name.


One way to do this is to change foo.h to this:

#ifdef __cplusplus
extern "C" {
#endif

    void foo(const unsigned char key[], unsigned int w[], int keysize);

#ifdef __cplusplus
}
#endif

So basically, when the header is seen by the C compiler the __cplusplus doesn't exist. For C++ modules, the __cplusplus is defined, and thus the extern "C" takes effect, thus the C++ modules recognize foo() as a C function.

If you can't change foo.h, then you can surround it with the extern "C" in your C++ modules:

extern "C" {
  #include <foo.h>
}

See this on more information of __cplusplus

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
  • I tried that, but now instead of undefined reference to foo(argumentlist), I get undefined reference to foo, without the arguments. I suspect it has something to do with the Android Studio message informing me that foo.c is not part of the project. I googled a bit and the solution is to add it to Android.mk, but I did a search for Android.mk and there are many such files so I'm not sure which one to change. – user1118764 Dec 05 '18 at 03:23
  • 1
    The new error indicates that the `extern C` did work for you, believe it or not. The `foo` with no arguments error indicates the linker now sees `foo` as a `C` function. The issue now is an Android project one, which unfortunately I can't answer or comment on. – PaulMcKenzie Dec 05 '18 at 03:28
  • Thanks. You're right. I fixed the Android Studio project configuration by modifying my CMakeLists.txt file to include the .c file and it built successfully. – user1118764 Dec 05 '18 at 06:09