Issues with using C libraries with C++ files

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Issues with using C libraries with C++ files

Jump to solution
4,703 Views
chadgraham
Contributor V

Hello,

I am having an issue using C libraries in a C++ project and I'd appreciate any suggestions.

I have a set of library files, all written in C, that I know works when being called from a C file in my project.  The library is comprised of multiple sub libraries where each one has a single primary header file.  These primary header files link all of the libraries's files and contains numerous macros to change the library behavior depending on the preprocessor commands.  Additionally, one of these sub libraries could be considered the primary library since the other sub libraries use it for common functions.

In my C++ files, I have included the C libraries using the "extern "C"" functionality and I can call/use the primary library without issue.  Additionally, I can use any of the other libraries provided that they, themselves, are not trying to use any of the functionality of the primary library.  When that happens, the compiler generates an "unrecongized reference" error where the library tries to call the primary library.

Example:

  • C++ calls primary library function == OK
  • C++ calls a non-primary library function == OK
  • C++ calls a non-primary library function that calls a primary library function == unrecognized reference to primary library function

I have confirmed that all of the library header files utilize the "__cplusplus" definition and all of the library files are either .c or .h.  (Although redundant, I also have all of the libraries included as "extern "C"".)  I have also confirmed that if I convert the C++ to a C equivalent and use the exact same library function calls, the project compiles.  (I commented out the library call in the C++ file and changed nothing else in the project.)

Does anyone have any suggestions on what to try?  I've spent several days trying to find the root cause of this and I'm pretty certain that the problem is the C++ compiler changing the C file references.

Labels (1)
Tags (2)
1 Solution
4,268 Views
converse
Senior Contributor V

Well, that was trivial to solve. As I said yesterday

Check the ordering of your libraries if a function in library A calls a function in library B, then the ordering must be A B.

All you need to do, is re-order the libraries in the linker settings. I moved the 'tx' library to the bottom, so the order is now fx, ux, tx:

pastedImage_3.png

To change the order, just select the library and click the down arrow in the top right. As you can see, the ordering of the libraries is critical.

View solution in original post

16 Replies
4,269 Views
chadgraham
Contributor V

ErichS

Hopefully this doesn't confuse things too much, but let me convert to using the real names of the libraries and functions to better explain the linker complaint.  This is all specific to ThreadX by Microsoft.

In my C++ class header, I include the primary library (tx_api.h) and the non-primary library (ux_api.h):

extern "C"
{
   #include "tx_api.h" // Included for other sections of the class that are working
   #include "ux_api.h"

}

In my C++ class, I try to initialize the UX device.  (I replaced all of the typical references and pointer with forced constants to ensure that I was providing legit, known data.)

   ux_system_initialize(( VOID *) 0x81B00000,
         0x19000,
         UX_NULL,
         0);

In the ux_api.h, the function name is converted based upon preprocessor, program specific definitions.  (Note, this file has the extern __cplusplus block around the entire file.)  This is also the location for the library's reference to tx_api.h

  #define ux_system_initialize _ux_system_initialize

   /* Include ThreadX API include file. */

   #include "tx_api.h"

The function _ux_system_initialize is found in ux_system_initialize.c and it is the only function in the file.

   #include "ux_api.h"
   UINT _ux_system_initialize(VOID *regular_memory_pool_start, ULONG regular_memory_size,

            VOID *cache_safe_memory_pool_start, ULONG cache_safe_memory_size)
   {

      /* Some Code */

      /* Create the Mutex object used by USBX to control critical sections. */
      status = _ux_utility_mutex_create(&_ux_system -> ux_system_mutex, "ux_system_mutex");

      /* Little More Code */

   }

The function _ux_utility_mutex_create is found in ux_utility_mutex_create.c and is the only function in the file.

   #include "ux_api.h"
   UINT _ux_utility_mutex_create(TX_MUTEX *mutex, CHAR *mutex_name)
   {

      UINT status;

      /* Call ThreadX to create the Mutex object. */
      status = tx_mutex_create(mutex, (CHAR *) mutex_name, TX_NO_INHERIT);

      /* Some Code */

   }

This is where the error is generated:

   ../..\ux\Debug\libux.a(ux_utility_mutex_create.o): in function `_ux_utility_mutex_create':

   undefined reference to `_txe_mutex_create'

Located inside tx_api.h, the function name is converted based upon preprocessor, program specific definitions.  (Note, this file has the extern __cplusplus block around the entire file.)

   #define tx_mutex_create(m,n,i) _txe_mutex_create((m),(n),(i),(sizeof(TX_MUTEX)))

The function _txe_mutex_create is located in file txe_mutex_create.c and is the only function in the file

   #include "tx_api.h"

   UINT _txe_mutex_create(TX_MUTEX *mutex_ptr, CHAR *name_ptr, UINT inherit, UINT          mutex_control_block_size)
   {

      /* Some Code */

   }

Per my previous post, the library for TX properly shows the _txe_mutex_create function and the preprocessors commands are all defaulted and pointing to the correct macros.

0 Kudos
4,269 Views
converse
Senior Contributor V

Check the ordering of your libraries if a function in library A calls a function in library B, then the ordering must be A B.

Also, in you example, is txe_mutual_create.c being built and included in the linker?

Finally, you mention

this file has the extern __cplusplus block around the entire file.

can you clarify EXACTLY, what you are doing here. You need to use extern “C” Not extern __cplusplus. Take a look at this Mixing C and C++: extern C - Embedded Artistry 

0 Kudos
4,269 Views
chadgraham
Contributor V

Hello,

converse

"Check the ordering of your libraries if a function in library A calls a function in library B, then the ordering must be A B."

Yes, I confirmed that the TX library is listed before the UX library.

"Also, in you example, is txe_mutual_create.c being built and included in the linker?"

As far as I can tell, yes.  I can locate the function in the TX library using both the image info tool and the NM tool Erich asked me to use.

"Finally, you mention 'this file has the extern __cplusplus block around the entire file' can you clarify EXACTLY, what you are doing here. You need to use extern “C” Not extern __cplusplus."

Both of the library header files use the common C file encapsulation:

   #ifdef __cplusplus // Two Underscores

      /* Yes, C++ compiler is present. Use standard C. */
      extern "C" {

   #endif

   /* Library Header File */

   /* Determine if a C++ compiler is being used. If so, complete the standard
   C conditional started above. */
   #ifdef __cplusplus
      }
   #endif

Regarding the libraries, the are included using the extern "C" syntax:

   extern "C"
   {
      #include "tx_api.h"
      #include "ux_api.h"
   }

Based on the compiler information, it looks like all functions are being consistently used with the C name mangling.  Additionally, I am able to use all of the library functions that are only a single library deep and the problem seems to be specifically related to one library calling another library.

Thanks.

0 Kudos
4,269 Views
converse
Senior Contributor V

I think to help any more, we are going to need to reproduce the issue. Can you provide an example that exhibits the same problem?

0 Kudos
4,269 Views
chadgraham
Contributor V

converse

Hello,

Hopefully this works the same for you as it does for me, but here is a much trimmed down version of my project that does not compile.  It produces the same error as my main project and I left the file structure mostly the same.  (I removed includes and libraries not used by this sample.)  You may need to download the RT1060-EVK SDK, but the project is otherwise self supporting.  Please let me know if you have any issues with it.

0 Kudos
4,269 Views
converse
Senior Contributor V

Well, that was trivial to solve. As I said yesterday

Check the ordering of your libraries if a function in library A calls a function in library B, then the ordering must be A B.

All you need to do, is re-order the libraries in the linker settings. I moved the 'tx' library to the bottom, so the order is now fx, ux, tx:

pastedImage_3.png

To change the order, just select the library and click the down arrow in the top right. As you can see, the ordering of the libraries is critical.

4,269 Views
chadgraham
Contributor V

converse

Thank you!  I did play around with the library order, but misunderstood the implied sequence.  I was thinking that the library B would need to be loaded first so that it was available for A.

0 Kudos
4,265 Views
converse
Senior Contributor V

A library which calls an external function defined in another library should appear before the library containing the function.

0 Kudos
4,269 Views
bobpaddock
Senior Contributor III

Pay particular attention to any leading underscores (_) in the maps that Erich points to.
Do they exist  or not and are of the same number (_) vs (__).

The compiler and linker can have different ideas about them at times,

across C and C++ code.

This has often been a source of trouble for me when using various libraries with C++.

0 Kudos
4,269 Views
chadgraham
Contributor V

Hello and thank you for the replies.

ErichS

I am following up on your suggestions, but I'm unable to find the .elf/.axf file; probable because I'm not able to complete the compile/link process.  Is there another way to access the symbol tool and see the links?

bpaddock

All of the files are listed as .c and .cpp and the compile log lists the .c files using gcc tools.  The actual listings in the library headers are:

   #ifdef __cplusplus // Two Underscores

      /* Yes, C++ compiler is present. Use standard C. */
      extern "C" {

   #endif

   /* Library Header File */

   /* Determine if a C++ compiler is being used. If so, complete the standard
   C conditional started above. */
   #ifdef __cplusplus
      }
   #endif

and the C++ file includes are:

   extern "C"
   {
      #include "library1.h" // Primary Library
      #include "library2.h" // Non-Primary Library
   }

0 Kudos
4,269 Views
bobpaddock
Senior Contributor III

I was referring to the underscores that are part of the function names that that compiler inserts,

not the ones in __cplusplus.

0 Kudos
4,269 Views
ErichStyger
Senior Contributor V

Hi Chad,

you can run the nm (arm-none-eabi-nm -S --size-sort -s lib.a)

on the library (archive) too.

Erich

0 Kudos
4,269 Views
chadgraham
Contributor V

Hello,

ErichS

I was able to track down the references in the library , but I must admit that I'm even more confused.  According to the -nm tool, the primary library contains the proper function call and file name and the non-primary library is calling the same function name.  (Verified with with the image view tool.)

So... it looks like it is not a name mangle issue?

From image view tool of the Non-Primary Library:

  _non-primary_library_called_function -> _non-primary_library_error_function -> _primary_library_function

From the NM tool on Non-Primary Library:

  _non-primary_library_error_function in non-primary_library_error_function.o

  // Lines later

  non-primary_library_error_function.o: 00000000 00000032 T _non-primary_library_error_function

From the NM tool on Primary Library:

  _primary_library_function in primary_library_function.o

  // Lines later

  primary_library_function.o: 00000000 00000130 T _primary_library_function

0 Kudos
4,269 Views
ErichStyger
Senior Contributor V

ok, so it really looks like it is using the C name mangling in the library.

So what is the linker error about it? What function does it expect?

(and I assume you have marked your functions as 'extern' anyway).

Erich

0 Kudos
4,274 Views
ErichStyger
Senior Contributor V

Just for the common understanding:

The "__cplusplus" is for recognizing if the source file is compiled in C or in C++ mode (the file extension actually does not main anything, have a read at Compiling C Files with GNU ARM G++ | MCU on Eclipse .

The 'extern "C"' is for something different: is for the name mangling: either C or C++ (which includes the encoding of the parameters. So using 'extern "C"' is not something redundant, it is mandatory for having the name mangling set for calling C code from C++. Could it be that you have this wrong, or understood it a wrong way? Or that some of your library still uses C++ name mangling but you expect it to have it C name mangling?

You could check with the linker map file or use the GNU nm utility (Listing Code and Data Size with GNU nm in Eclipse | MCU on Eclipse ) to check the name encoding.

I hope this helps,

Erich

0 Kudos
4,274 Views
bobpaddock
Senior Contributor III

"the file extension actually does not [mean] anything"

Some versions of GCC will compile a file that ends with a capital 'C' (.C)

with G++ and a small 'c' (.c) with GCC. 

On Windows where the OS doesn't care and GCC/G++ does,

it can lead to some head scratching quandaries as to what is going on.


Just to make the rest of the thread clear to others, this is what is being discussed:

#if defined(__cplusplus) && __cplusplus
extern "C" {
#endif

/* C stuff goes here*/

#if defined(__cplusplus) && __cplusplus
}
#endif

0 Kudos