Jump to content
  • 0

Issues with inline after switching to XC32 v2.15 - "multiple definition of"


I just downloaded XC32 V2.15, I was using V2.10 before. I find that some of my projects no longer compile. On my first check I noticed that the problems seem to occur when inline functions are used and the same header where the inline implementation is done is included in more than one compilation unit?

Has any of you seen similar issues?

I will investigate further and post here if I arrive at an answer.


UPDATE: Ok, I managed to make a small test project to replicate the problem. I am attaching it here. 



Next I am going to test this on some other compilers to see what the deal is. I have confirmed that with that project when you switch it to V2.10 or older it all compiles just fine, but if you use V2.15 it failes to link with the following error:

"/Applications/microchip/xc32/v2.15/bin/xc32-gcc"   -mprocessor=32MZ2048EFM100  -o dist/default/production/TestInlineXC32_2.15.X.production.elf build/default/production/main.o build/default/production/otherFile.o          -DXPRJ_default=default  -legacy-libc    -Wl,--defsym=__MPLAB_BUILD=1,--no-code-in-dinit,--no-dinit-in-serial-mem,-Map="dist/default/production/TestInlineXC32_2.15.X.production.map",--memorysummary,dist/default/production/memoryfile.xml
nbproject/Makefile-default.mk:151: recipe for target 'dist/default/production/TestInlineXC32_2.15.X.production.hex' failed
make[2]: Leaving directory '/Users/ejacobus/MPLABXProjects/TestInlineXC32_2.15.X'
nbproject/Makefile-default.mk:90: recipe for target '.build-conf' failed
make[1]: Leaving directory '/Users/ejacobus/MPLABXProjects/TestInlineXC32_2.15.X'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
build/default/production/otherFile.o: In function `myInlineFunction':
/Users/ejacobus/MPLABXProjects/TestInlineXC32_2.15.X/inlinedheader.h:6: multiple definition of `myInlineFunction'
build/default/production/main.o:/Users/ejacobus/MPLABXProjects/TestInlineXC32_2.15.X/inlinedheader.h:6: first defined here
/Applications/microchip/xc32/v2.15/bin/bin/gcc/pic32mx/4.8.3/../../../../bin/pic32m-ld: Link terminated due to previous error(s).
collect2: error: ld returned 255 exit status
make[2]: *** [dist/default/production/TestInlineXC32_2.15.X.production.hex] Error 255
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2

BUILD FAILED (exit value 2, total time: 680ms)




Share this post

Link to post
Share on other sites

10 answers to this question

Recommended Posts

  • 0

Ok, got some feedback that the REAL bug is that the code compiled in v2.10, this "bug" was fixed in v2.15.

The way to get the behavior back to matching v2.10 is to pass the linker flag  "--allow-multiple-definition" which will allow inline funcitons declared in header files included in multiple compilation units to link successfully.

Of course if you do it that way (place the implementation in the header) then the code SHOULD only compile when you have used "static inline", if you just placed inline, like I did in my example, it is not supposed to compile, and as I discovered it also does not compile on GCC or LLVM. When you use "static inline" correctly it compiles on all the mentioned versions of all the mentioned compilers, because the code is then correct 🙂

Share this post

Link to post
Share on other sites
  • 1

It seems that in 2.15 they changed how the command line options are interpreted.

in 2.10, there was a -fgnu89-inline option that ensured the inlines operated in a particular way (the GNU89 way).  

In 2.15 it seems this option is being quietly ignored.  You can test by removing the option from the command line when you build in 2.10 and then you should see the same error.

  • Helpful 1

Share this post

Link to post
Share on other sites
  • 0

OMG you are a genius! That was it! 

I carefully read the release notes and this was not mentioned at all. Very disappointing, this cost me a couple of hours of struggling today and someone else here has an open issue with Microchip support to try and get a Harmony 2.05 project to build with the new compiler and have not manageed to resolve their issue in a week now, so this is costing customers as lot of time and we all know time is money!

I will go ahead and change my code to C99 semantics for inline, and I guess I will have to also update the Harmony code myself if I want to use the latest compiler. Sigh ...

I was just busy testing, as promised in my update above, on LLVM when you posted that btw. Of course since I was using G89 semantics for inline the project was not compiling on LLVM either, except there it was complaining that there was no definition for the symbol instead of complaining about a duplicate definition.

When I changed the inline definition to extern inline in my header the error swapped around. Now LLVM was complaining that the symbol was no longer missing, but I now had a duplicate symbol. Strangely this compiled just fine on XC32? Had to brush up on my C99 inline semantics a bit and found that I probably want static inline, and yes that compiles on both LLVM and XC32, so I guess I am going to go with that for now.

BTW. The release notes contain only this for XC32 v2.15:

New Features in MPLAB® XC32 v2.15

  • New Part Support -- This release introduces initial support for the MEC15xx family of Embedded Controllers as well as the ATSAMx7, ATSAME54/D51, and ATSAMC2x/D2x families of 32-bit microcontrollers.

  • Tightly-Coupled Memory (TCM) on SAMx7 MCUs -- The SAM family features a low-latency SRAM interface called Tightly-Coupled Memory (TCM). To support this interface, XC32 provides a new tcm attribute. You can apply this attribute to a function or variable and it will be placed into instruction or data TCM as appropriate. (e.g. uint32_t __attribute__((tcm)) var;)

    To enable TCM, pass the -mitcm=<size_in_bytes> and the -mdtcm=<size_in_bytes> options to xc32-gcc/g++ both when compiling and when linking. (See the device datasheet for the size values supported by your target device.) The device-specific startup code and the device-specific linker script then work together to set up, initialize, and enable TCM at startup, before your main() function is called. With this option enabled, the linker allocates the vector table to ITCM, improving both interrupt latency and latency determinism.

    Also for improved determinism, you may also choose to move your stack to DTCM by passing the -mstack-in-tcm option to xc32-gcc/g++ at compile and link time. The linker will allocate a stack to DTCM and the startup code will transfer the stack from System SRAM to DTCM before calling your main() function.

  • Issues Fixed -- See the Fixed Issues section for information on bug fixes addressed in this release.

  • 64-bit executables coming soon -- A future release of XC32 will be provided only as 64-bit executables for Windows x64, Linux x64, and MacOS x64. We will no longer provide 32-bit executables.

Share this post

Link to post
Share on other sites
  • 0

Ok, now I am sleuthing to figure out what is exactly going on. At first I thought that the problem was just that -fgnu89-inline is being ignored, but the plot thickens! 

When I remove the std as well as the -fgnu89-inline from the project I attached above it still builds fine on v2.10 and fails on v2.15, so they are not only ignoring the setting, they are also defaulting things differently ...

At first I suspected that the default mode was changed from GNU89 to C99 in this compiler version, but when I check for it __STDC_VERSION__ is not defined when trying to compile this with either compiler, which means it is not defaulting suddenly to C99. This leaves me with thinking that it may have changed the default from GNU89 to GNU99?

So my first test was to pass -std=c99 to v2.10, and yes it fails identically to the v2.15 with no standard specification, so I thought this makes sense. I think compiled on v2.10 with -std=gnu89 and as expected it compiles fine, but then I tried to compile on v2.15 with -std=gnu89 and nope, inline does not work with gnu89 semantics either.

So it seems like they are not in fact ignoring the -fgnu89-inline flag, it seems that the implementation for that has somehow been ripped out of the heart of the compiler, because even if you try and force it to use gnu89 as the C dialect it fails on v2.15...

I will wait and see if we can get an answer from Microchip on this, and update here if I do.

Share this post

Link to post
Share on other sites
  • 0

I just had a look at your test project and really wonder why you put the definition of a function in a header file. Since you include that header file with the definition in two C files, you end up with two conflicting definitions, don't you?

  • Confused 1

Share this post

Link to post
Share on other sites
  • 0

Oh, I see now. You have to include the body, otherwise the compiler complains (if -Winline is used):

my_inline_func.h:4:17: warning: inlining failed in call to 'inl_func': function body not available [-Winline]

And it works fine with 2.10, I see nice inlined functions in the disassembly:




Share this post

Link to post
Share on other sites
  • 0
47 minutes ago, holdmybeer said:

And now it works with 2.15.

Try to add "extern" to your inline function, this worked for me:

I mentioned in my first response that this is incorrect, I tried it and it works on XC32 but it should not work according to the standard, so the implemenation seems to be incorrect. Most importantly although this works on XC32, it does NOT work on any other compiler in my project.


"When I changed the inline definition to extern inline in my header the error swapped around. Now LLVM was complaining that the symbol was no longer missing, but I now had a duplicate symbol. "

I need a solution which works on all compilers, just FYI my project runs on 12 different compilers, so making it work on XC32 is less than 10% of my problem here ...

So we originally used GNU89 semantics for inline, which means it works as long as all compilers can handle this. GCC can handle this if you pass in the command line switch -fgnu89-inline  OR if you set the C dialect to be GNU89. Both cases should work, and does in v2.10. 

The problem is that in v2.15 neither of these options work, so it is now impossible to use GNU89 semantics for inline by either method. This means the compiler now does C99 or C89 only and can no longer support GNU89 inlining, which is a little strange as GCC, which it is based on, happily handles this...

Share this post

Link to post
Share on other sites
  • 0
7 hours ago, holdmybeer said:

I just had a look at your test project and really wonder why you put the definition of a function in a header file. Since you include that header file with the definition in two C files, you end up with two conflicting definitions, don't you?

Of course the answer here is that the semantics for inline in C89 do not exist (there is no such thing as inline), in GNU89 you can define a C function in a C file and place inline on it as a hint for the optimizer, BUT in C99 and C11 this is illegal, in C99 and C11 the standard specifies that the only legal way to use inline is to have the function body in the header file ...

Read this for some background:


This is the relevant part "So you’d have to put the implementation of such candidate functions in header files."

EDIT: To be super pedantic about it, the standard does not exactly say you must place the definition in the header file, but if you want to use the function from more than one C file you will have to do exactly that for it to work.

Edited by Orunmila
  • Helpful 1

Share this post

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Popular Contributors

    Nobody has received reputation this week.

  • Similar Content

    • By KM1
      I have run into a strange (for me) issue with the rtcounter module as provided by MCC and shown by the example program in the blog area of this site. I am using MPLabX and XC8, both up-to-date versions and I am trying the example on a pic18F47k40 xpress board. The issue is I set an output (RA4) in main after initializing the pic and then enter the while(1) loop where the rtcount_callNextCallback(); is called. The output now is turning on and off. The off duration is typically ~60u seconds, and on time can be varied with changes to the timer interrupt settings (I use a 1mS timer0 in 16 bit mode) and the number sent to the callback. Approximately every 3mS, the off time increases such that that cycle (only) is approx 50% duty cycle.
      Turning the output on or off in the callback routine has no effect - I initially wanted to toggle on and off at a speed visible to the eye. Commenting out the callback in the main loop stops the output from going low.
      I have checked errata and used the data sheet to confirm register settings, I tried moving the current-limited LED from RA4 to RA2, and I have studied the example program looking for obvious differences that may cause this behaviour.
      I would appreciate thoughts and/or suggestions.
    • By burkart
      Hello All,
      I am working on a project (in MPLAB v5.10) with a PIC18F27K40 (PIC18 library v1.77.0) and I'm using the MCC (v3.85.1) generated I2C drivers, I2CSIMPLE from the Foundation Services. I can read from and write successfully to devices on the I2C bus. The problem comes when I try to communicate with a device that's not on the bus, the micro goes into an endless loop waiting for i2c_status to not be busy. My knowledge of programming in C is about 6 on a scale of 10, and for programming for embedded purposes, about 5 out of 10. I would like to have it so that I can check if a specific device is present on the I2C bus, and also be able to recover from errors on the bus without it going into a loop indefinitely.

      This I2C driver is pretty complex, and I am having difficulties wrapping my head around it. How would I make it so that the driver just returns an error or something I can check for status, rather than loop endlessly until the operation completes, which it never will?

      I have not edited any of the MCC generated code. This includes leaving the IRQ enable line commented in the i2c_master.c file, so instead it polls instead of using an interrupt to check if the i2c operation has completed.
      // uncomment the IRQ enable for an interrupt driven driver. // mssp1_enableIRQ(); Following is an example of how I am calling the i2c driver.
      i2c_write1ByteRegister(address, 0x0D, 0x07); // GPPUB pull-ups on pins 0-2 I am attempting to initialize a port extender, MCP23018, specifically enabling some pull-up resistors. I would like to issue this command, and if the extender is not present, then the micro will perform some tasks differently. With the port extender present the write operation works as expected and everything is fine. Of course the problem is when the extender is NOT on the bus to acknowledge.
      I have another question as well. This driver seems to operate a little slow. When watching the bus with a logic analyzer I noticed a rather long pause between bytes. I went looking through the i2c driver and in i2c1_driver.c I found the following code which I suspect is the cause.
      inline void mssp1_waitForEvent(uint16_t *timeout) { // uint16_t to = (timeout!=NULL)?*timeout:100; // to <<= 8; if(PIR3bits.SSP1IF == 0) { while(1)// to--) { if(PIR3bits.SSP1IF) break; __delay_us(100); } } } What is the purpose of the 100 us delay in the while loop? Reducing or eliminating the delay results in reducing or removing the pause between byte transactions, but I don't know enough to know how else this edit will effect the driver. Also, what is the commented out code at the top of the function used for? Is this part of the infinite loop problem I mentioned above?

      James Burkart
    • By Orunmila
      I am trying to use a linker script with MPLAB-X for my PIC32 project but for some reason the script is not being passed to the linker at all. I expected that all I had to do was add the .ld file to my project, typically by placing it in the "Linker Files" virtual folder in MPLAB-X in my project. I did this and the linker script is being ignored by the linker.
      This is one of those $100 questions (if you know the story of the mechanic asking $100 for knowing where to hit ...).
      So my question is how do I get MPLAB-X to use my linker script which I have added to the PIC32 project?
    • By Orunmila
      error: variable has incomplete type 'void'
      If you are getting this error message trying to compile code that used to work or from some example code you found somewhere it is very likely because of the changes in the XC8 interrupt syntax introduced with V2.0 of XC8.
      Specifically I am getting this today for my interrupt service routine definition. I used to use the age-old way of doing this in XC8 as follows:
      // Old way to do this void interrupt myISR(void) { // Old way Interrupt code here } After the changes to the XC8 compiler which were mostly motivated to get better C standard compliance, particularly with C99, the syntax for that function should now use the commonly adopted concept of function declaration-specifier attributes, which traditionally start with 2 underscores and contains either a list of parameters in brackets or have empty brackets if no parameters are present. 
      // New and improved and C99 compliant way to specify an interrupt service routine in XC8 void __interrupt() myISR(void) { // New and improved interrupt code here } This syntax is now also consistent between XC8, XC16 and XC32
      Please see this post for more information on how to either work around this or change to the new syntax.
    • By Orunmila
      I have a MPLAB-X project which uses a loadable (it is combining my program with a bootloader which is in another project). 
      I need to compile this project from the command line for CI automation. For the entire build process every command executed is nicely printed in the build window, but for the loadable it claims to be using Hexmate, but the command line to execute it is not shown at all.
      Can anyone help me with the syntax using Hexmate to get the same behavior as adding the Loadable from MPLAB-X?
    • By Orunmila
      I am trying to pass an include file name on the command line to XC32 to select the file to include in the code. Don't ask why, this is not my code, I am just trying to compile it with this compiler without modifying the code ...
      I am passing 
      -DCONFIG_FILE=\"demo_config.h\" but the compiler does not seem to like this, error message is 
       error: #include expects "FILENAME" or <FILENAME> Anyone know how to bend XC32 to my will here to do this correctly?
      EDIT: I have confirmed that this works fine on vanilla GCC. On MCHP XC32 I can do this and then it compiles fine
      #undef CONFIG_FILE #define CONFIG_FILE "demo_config.h" #include CONFIG_FILE It seems like when I pass in the filename via the command line something weird and unexpected is happening ...
      I used a trick to print the value and the filename is ending up in the code correctly it seems ...
      #define XSTR(x) STR(x) #define STR(x) #x #pragma message "The include is " XSTR(CONFIG_FILE) #ifdef CONFIG_FILE     #include CONFIG_FILE #endif This will produce:
      #pragma message: The include is "demo_config.h" error: #include expects "FILENAME" or <FILENAME>  
    • By Orunmila
      If you have purchased a "MPLAB(R) Xpress PIC18F47K40 Evaluation Board" from Microchip (part number DM182027) and you are running into difficulty because the board is behaving strangely it is most likely caused by a silicon errata on this device!
      The errata can be downloaded here: http://ww1.microchip.com/downloads/en/DeviceDoc/PIC18F27-47K40-Silicon-Errata-and-Data-Sheet-Clarification-80000713E.pdf
      The relevant section of the Errata is shown at the end.
      What is happening is that the compiler is using a TBLRD instruction somewhere and this instruction is not behaving as expected due to a silicon bug in REV A2 of the PIC18F47K40, causing the read to fail and the program to malfunction. Typically this happens as part of the C initialization code generated by the XC8 compiler, and since the compiler is optimizing, changing the code may cause the problem to temporarily disappear because you have few enough global variables that a table read is no longer the fastest way to initialize the memory segment for variables with static linkage.
      The XC8 compiler can avoid generating the sequence which will cause the failure if you tell it in the linker settings to implement the workaround for this Errata. This is done by adding +NVMREG to the setting as follows. Note that this is under the section "XC8 Linker" and the Option Category "Additional Options".

      This is the relevant section of the Errata.

  • Create New...