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

Unresolved external

I started a new project with an LPC2106 cpu.
There are two files in it: blinky.c and time.c
In Blinky.c I declare:

extern void init_time(void);

And call it in the main function.
Function is written in time.c

When I link I get a undefined symbol report on init_time

If I look at blinky.lst I can see a call to a external symbol called: "Z9init_timev"
And if I look at time.lst I can see a global symbol "init_time"

Anyone know what I am doing wrong???

Parents
  • "Absolutely sure! Both time.c and blinky.c shows up in the project window. Both are compiled."

    That answers Stefan's 1st question, but not his 2nd:

    Is the declaration of init_time in time.c is *exactly* the same, ie void init_time(void)?

    "Still... Why the "Z9" in the beginning of init_timer....?"

    It is quite possible that the compiler could add prefixes and/or suffixes related to the type of the function, its parameters, etc.
    That's why Stefan's 2nd question is so important!

    One way to guarantee that the declaration matches the definition is to put the declaration of init_time() in a header file - say time.h - and #include that it both main.c and time.c. That way, the compiler can give an error/warning if the declaration does not match the definition, since it can see both when it compiles time.c!

Reply
  • "Absolutely sure! Both time.c and blinky.c shows up in the project window. Both are compiled."

    That answers Stefan's 1st question, but not his 2nd:

    Is the declaration of init_time in time.c is *exactly* the same, ie void init_time(void)?

    "Still... Why the "Z9" in the beginning of init_timer....?"

    It is quite possible that the compiler could add prefixes and/or suffixes related to the type of the function, its parameters, etc.
    That's why Stefan's 2nd question is so important!

    One way to guarantee that the declaration matches the definition is to put the declaration of init_time() in a header file - say time.h - and #include that it both main.c and time.c. That way, the compiler can give an error/warning if the declaration does not match the definition, since it can see both when it compiles time.c!

Children
  • The declaration is the same as it is possible to see if the project is downloaded.
    I think I will wait for support to answeer my question. It looks like nobody else have used the ARM...

  • You should really have checked (your declaration is the same as the definition) before posting a message like that. They look obviously different to me.

  • You are correct. I did leave out the void when writing the function. Have tried som many ways....
    But problem is still there, with or without the "void".

  • Your problem is the name of the blinky.c source file. It's actually in all upper-case: BLINKY.C. Now the Keil ARM tools are based on GNU compilers, which are Unix-born, and thus do care about the case of filenames. From GCC's point of view, "BLINKY.C" is a C++ file name, and so it compiled the source as C++. C++ puts a lot more information into a function's linker-visible name than C (google:"name mangling"), which break direct link compatibility with C. Thus your problem.

    Proposed solution: remove BLINKY.C from the project, rename the file to lower-case, and add it back to the project under the new name.

  • It looks like Nohau, Sweden solved the problem....

    Problem can be located to the GCC compiler.

    Filenames in capital letters are considered to be C++ files and function calls are made in that style.

    By renaming BLINKY.C to blinky.c the problem was solved.

    Do I need to add any personal opinion about this????

    //Helge

  • Thanks Hans-Bernhard...

    Your answeer came at the same time as I wrote mine...

    //Helge

  • Is there a command-line option to disable this?
    (eg, like Borland has an option to ignore a .cpp extension, and compile the file as plain 'C')

    Or an option that the functions are to be generated with plain 'C' naming & calling conventions?

    You may need some time to check this out - I remember how long the list of GCC arguments was about 4 years ago...! ;-0

    Of course, being open-source, you could always add one if it doesn't already exist...! ;-)

  • The -x language option for GCC allows you to specify the language of input files, overriding the detection done on the basis of the file extension.

    (And it is just the extension, not the whole file name in uppercase. "blinky.C" would also be treated as a C++ file. .cc, .cp, .cxx, .cpp, .CPP, .c++, and .C are all extensions that indicate a C++ file to GCC. Back in the day, no one was quite sure what the "obvious" extension for C++ ought to be, particularly since "+" is not often allowed in filenames by various OSes. The uppercase C was just asking for trouble, though; I'm not sure where that convention was used.)

    http://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html

  • "I'm not sure where that convention was used"

    Remember that GNU is of UNIX origin; to understand these things, you have to think like a UNIX hacker (in the benign sense of the word).

    To a UNIX hacker it is perfectly fine, reasonable and commonplace to find that a lowercase 'c' has a totally and completely different meaning to an uppercase 'C'.
    UNIX hackers find this perfectly natural; they will find it just as hard to understand how a Windows user could even consider the possibility that "blinky.c" would be in any way equivalent to "blinky.C"

    Since a lowercase ".c" suffix was well established as indicating a 'C' source file, it is not surprising that UNIX hackers should think of using ".C" for C++ source - since C++ is effectively a "bigger" 'C', it would make sense (to them) to to a "big" (uppercase) ".C" to distinguish it!

    It's a cultural thing!

  • Actually we have completed your example. It is available at:
    http://www.keil.com/download/files/lpc2100_blinky.zip

    Detailed information can be found in the Abstract.txt file that comes with the example. It is tested with the simulator (but should also run on real hardware).

    This example shows:
    - how to setup the Vector Interrupt controller.
    - how to setup timer0 for timer interrupts.
    - how to put the device into power-down mode.

  • The uppercase C was just asking for trouble, though; I'm not sure where that convention was used.

    On Unix, of course, where filenames are case-sensitive as god intended ;-) The way the Microsoft platforms treat letter case in filenames ("case-preserving, but not case-respecting") is atrocious, IMHO. It's a hysterical remnant from the original QDOS
    (and thus from CP/M) which has no business still remaining in all current incarnations of Windows today. They ought to have killed it in the first edition of NT, but they didn't.

    The DOS and Windows ports of GNU software
    (DJGPP, MinGW, cygwin) all faced this same problem at some time, and no-one found a better solution yet than the classic "Don't do that, then!" workaround.

    IMHO, Keil should put a catch for this into uVision. At least show a warning dialog if a file *.C is added to a project and offer to add it as *.c instead...

    Note that lower-casing all extensions unambiguously would be a bad plan because of the distinction between *.s and *.S. Keil ARM projects may well want to have *.S files in them!

  • But "Unix" is also the source of most if not all of those other extensions as well. There had to be some sub-culture that thought .C was a great idea. It's even more cute than .cxx. (See, the plus signs in .c++ are rotated 45 degress and become x's...) So I was wondering, which "Unix", or school? Is it AT&T thing, or Berkeley, or MIT, or Sun, or NeXT-ish?

    Relying only upon case to distinguish two identifiers is a poor design choice, IMO. I remember once having to deal with some code where the author liked to declare things "Static", which was of course variably #define'd to mean almost completely not what you thought it did. Or imagine filling up your 8051 code with references to "p0" and "ie" that are just ordinary data. Why doesn't "ResetCPUWatchdog()" do the same thing as "ResetCpuWatchdog()", and are you really going to remember that late at night before the deadline? Should "RM -f" (read memory, fast) really exist in your shell next to "rm -f"?

    There's a reason stricmp() and friends exist.

  • Looks like the example program from Keil support only runs on the simulator, not the real chip.
    There are no interrupts generated in real target.