This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

undefined symbol when linking over 45+ files

Hi

I ran into a odd problem yesterday when I add a code file to control the GPIO for a Cortex M3. All of the routines that were being called from the main code (4 of them) were called out by the linker to be undefined symbols. ??? The .c file compiled with no errors.

I did some experiments to see what the issue might be. I thought maybe the calling signature was wrong and it could not match it up so I cut and pasted just the subroutine definition of one of the subroutines and put it into the top of the only file that called that particular one and just made it a do nothing loop and the linker did resolve the subroutines name/symbol!!

So it is has nothing to do with how the subroutines are called or defined. I did another experiment to see if it had anything to do with the physical number of files being linked. I combined the new GPIO.c file with a existing file and removed the GPIO.c from the link list. I relinked and all of the subroutines names/symbols were resolved BUT a another assembly language name showed up as unresolved. Gees. Its definitely there in a totally different .s file so I don't know why started it will not resolve all of the sudden.

I tried combining more and more files to reduce the number of physical files but nothing seemed to help make this new unresolved system that did not show up before, go away.

So now I don't know what to do. The only thing that makes sense (cause and effect) is a limit on the number of files that the linker can accept. Maybe beyond that there is a internal limit on the number of routines or something that is causing this .s routine to be declared undefined.

Has anyone ever had a problem like this before?

Thanks

Parents
  • "Too long command line when linking? Depending on OS version and how the linker is started, it's likely that the limit is 2k, 8k or 32k characters."

    I am using the latest version, v5 with legacy support. I am using uVision 5.11a

    "By the way - what changes did you do to this project initially to get it to fail to link? Or have you ported some code from some other environment?"

    All I did was add another file to the source files group in uVision. Then this happened. All of this is new original code, nothing ported.

    I have been playing around with it some more and if I #include "gpio.c" at the bottom of a existing file in the linker list, the undefined symbol error for all of the gpio functions goes away but a new one which I mentioned early appears because gpio.c calls a assembly routine called HW_get_8bit_reg. Grr.

    So obviously the functions in gpio.c are being included now and being resolved which is why this other undefined symbol error appeared. I included the .s file hw_reg_access.s where this routine is defined but I still get the error. The .s is getting assembled without error.

    But again the linker will not resolve the subroutine name. Just like with the gpio.c file.

    I have tried reduce the number of files in the link list (group) with the #include trick but that has not helped in this case.

    Is There anything special I need to do to link a .s with my .c and .cpp files?

Reply
  • "Too long command line when linking? Depending on OS version and how the linker is started, it's likely that the limit is 2k, 8k or 32k characters."

    I am using the latest version, v5 with legacy support. I am using uVision 5.11a

    "By the way - what changes did you do to this project initially to get it to fail to link? Or have you ported some code from some other environment?"

    All I did was add another file to the source files group in uVision. Then this happened. All of this is new original code, nothing ported.

    I have been playing around with it some more and if I #include "gpio.c" at the bottom of a existing file in the linker list, the undefined symbol error for all of the gpio functions goes away but a new one which I mentioned early appears because gpio.c calls a assembly routine called HW_get_8bit_reg. Grr.

    So obviously the functions in gpio.c are being included now and being resolved which is why this other undefined symbol error appeared. I included the .s file hw_reg_access.s where this routine is defined but I still get the error. The .s is getting assembled without error.

    But again the linker will not resolve the subroutine name. Just like with the gpio.c file.

    I have tried reduce the number of files in the link list (group) with the #include trick but that has not helped in this case.

    Is There anything special I need to do to link a .s with my .c and .cpp files?

Children
  • Well if you're mixing in C++ (.cpp) files then you're going to need to be acutely aware of the name mangling done for type checking purposes, and ensure that when you call C routines you make the compiler explicitly aware of this. Otherwise, yes you'll see the linker failing.

    extern "C" void foo(int); // of whatever

    I'm not aware the linker has a file limit issue, although I haven't validated such, most would take a response file where command line constraints exist, it's not like we're using DOS tools in 1984.

  • Since I have to get this solved right away, I've been experiment some more and found that if create a dummy function with the same calling signature in a file that is already in the link list, the undefined symbol error for the .s routines goes away. It gets resolved/found by the linker.

    so I am really back to my original problem I guess where the linker will not include the .s file when it links everything.

    It seems to have to do with the number of files I have in the group because if I include what is being dropped by the linker into other existing files, everything gets resolved. It seems to physically not pick up the file(s) I just added to the existing list of files.

    Does anyone know if I maybe create a new group (say source group 2) and split the list into two parts if that may help?

    I am reaching here.

  • But you haven't responded to Pier's post by indicating that you are properly taking care of the mixing between C and C++ functions, and making sure that the C++ code knows if the functions named in header files are using C or C++ naming conventions.

    You can force a symbol in a C++ file to use the C naming convetion. But a symbol in a C file will not use the C++ naming convention - unless you configure the project settings to force the C file to be compiled as C++.

  • Great thought! I totally forgot about name mangling. And yes when I changed all of the vendor supplied driver .c files to be compiled as c++ the functions in GPIO.c are now resolved.

    But it seems that I am having (probably) this same issue with the assembly routines in the hw_reg_access.s file. The GPIO.c is calling assembly routines (exported) to write to registers. Those are still not being resolved. It has to be the same issue with name mangling.

    Since this is a .s and not .c, what can be done to get the assembly name when called from c++ not to be mangled?

    I obviously cannot compile the assembly routine as a c++ in this case

  • OK it looks like the convention is the same when declaring assembly routines per the AAPCS specification. To use extern "C"

    I did that with one of the offending assembly routines:

    extern "C" uint32_t
    HW_get_32bit_reg
    ( addr_t reg_addr
    );

    and the compiler gave me a error #40 expected an identifier

    The syntax looks correct so I don't quite know what it is expecting

  • I found my mistake this morning. I must have accidentally deleted the definition of that routine when adding the extern "c" . Once I replaced it everything linked just fine.

    Thank you for your help in finding out what was wrong with what I was doing with the linker.

    I really appreciate it and I hope this thread helps someone else in the future. :-)

    Steve

  • Make sure you are exporting the function name

    ...
    Foo   PROC
                    EXPORT  Foo
                    IMPORT  Bar
                    PUSH    {LR}
                    LDR     R0, =Bar
                    BLX     R0
                    POP     {PC}
                    ENDP ; Foo
    ...
    

  • Thanks. Yes I did see that in the code (from microsemi manufacturer).

    Everything links fine now (yea) so I move on to the real work.