For a while, the C++ shortcomings of the .map file processor meant that I completely ignored those errors. But v11.1.0 has come out (MCUXpresso IDE v11.1.0 [Build 3209] [2019-12-12]), and it has pretty much fixed all of them, so I gave it another try. Unfortunately, there's still a problem, and it has to do with ld's PROVIDE keyword.
First, an explanation: You can tell ld that, if it cannot find one symbol, to PROVIDE a different symbol in its place. If the required symbol is given, then the PROVIDE is ignored.
For example, you could specify a default function, and then a table of function pointers pre-populated with a three different entries:
typedef void Fn(void);
// User defined functions
extern void Fn1(void);
extern void Fn2(void);
extern void Fn3(void);
// Table of callable functions
Fn *table[] = { &Fn1, &Fn2, &Fn3 };
// Default implementation
void DefaultFn(void) {
// Do default stuff here
} // DefaultFn(void)
This would require all three Fn#() functions to be defined somewhere - except that the linker script has the following lines:
PROVIDE ( Fn1 = DefaultFn );
PROVIDE ( Fn2 = DefaultFn );
PROVIDE ( Fn3 = DefaultFn );
Now, unless the programmer provides one of the Fn#() functions, DefaultFn() will do the job. Yes, this is similar to weak functions and weak substitutions, but a different way to achieve the same end - and somewhat more portable.
Your parser, however, doesn't recognise PROVIDE as a keyword, and gets confused by the opening parenthesis:
missing '=' at '('
Again, ld has no problem with this syntax: only your parser does.
Thank you for reporting this. We'll look into it.
Greetings,
MCUXpresso IDE Support
Hello John,
Usually in the linker script the PROVIDE modifier is used to declare symbols and define were will start and end the different sections. For example:
/* Kinetis Flash Configuration data */
. = 0x400 ;
PROVIDE(__FLASH_CONFIG_START__ = .) ;
KEEP(*(.FlashConfig))
PROVIDE(__FLASH_CONFIG_END__ = .) ;
ASSERT(!(__FLASH_CONFIG_START__ == __FLASH_CONFIG_END__), "Linker Flash Config Support Enabled, but no .FlashConfig section provided within application");
This assigns the value to the symbol first define depending of the '.' value. So I think this is due to one of the values in the PROVIDE modifier. It could be that the DefaultFn is not identify correctly in the linker file.
If you want to retain this symbol it would be better to use the __attribute__((used)) modifier to retain the symbol.
extern void Fn1(void) __attribute__((used));
Let me know if this helps you.
Best Regards,
Alexis Andalon
Alexis,
Thanks for your reply. I'm glad you said "usually", because while you can do that, it changes what the linker does with the symbol.
First of all, in your example note that PROVIDE() is unnecessary - you can just give the raw lines:
__FLASH_CONFIG_START__ = . ;
__FLASH_CONFIG_END__ = . ;
What PROVIDE() means to the linker is "If the program references a symbol and it hasn't been defined, PROVIDE this value." In your example, the symbol wouldn't be defined anywhere else, so using PROVIDE() is nugatory.
To demonstrate, I've included a screenshot of an actual .map file of mine, that shows where the map file parser complains - and notably, where it doesn't. Unlike my cut-down example above, however, it uses C++ name mangling, so see below the image for an explanation of what I'm doing, and why.
But please note the actual values assigned by the linker and reported by the map file: I've deliberately altered one of the lines to not use PROVIDE(), and you'll see that the linker does the wrong thing with a 0x00000000 value - but at least the map file parser doesn't complain about that line. Also note that as soon as the linker doesn't need to do a PROVIDE (indicated by "[!provide]"), from then on the map file parser stops complaining about the syntax of lines that it complained about before - but that might also be because for some reason it's opened a group (note the blue circled minus on that line):
For ease of explanation, I've demangled the C++ names:
0x60001738 PROVIDE (ARM::NVICs::IRQ52() = ARM::ISRs::Invalid() )
0x60001738 PROVIDE (ARM::NVICs::IRQ54() = ARM::ISRs::Invalid() )
0x60001738 PROVIDE (ARM::NVICs::IRQ55() = ARM::ISRs::Invalid() )
0x00000000 ARM::NXP::ISRs::eDMA_Ch0() = ARM::NVICs::IRQ0()
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch1() = ARM::NVICs::IRQ1() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch2() = ARM::NVICs::IRQ2() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch3() = ARM::NVICs::IRQ3() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch4() = ARM::NVICs::IRQ4() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch5() = ARM::NVICs::IRQ5() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch6() = ARM::NVICs::IRQ6() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch7() = ARM::NVICs::IRQ7() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch8() = ARM::NVICs::IRQ8() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch9() = ARM::NVICs::IRQ9() )
[!provide] PROVIDE (ARM::NXP::ISRs::eDMA_Ch10() = ARM::NVICs::IRQ10() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch11() = ARM::NVICs::IRQ11() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch12() = ARM::NVICs::IRQ12() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch13() = ARM::NVICs::IRQ13() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch14() = ARM::NVICs::IRQ14() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Ch15() = ARM::NVICs::IRQ15() )
0x60001738 PROVIDE (ARM::NXP::ISRs::eDMA_Error() = ARM::NVICs::IRQ16() )
Hopefully this makes it clearer, but to explain:
In short, I can't use your "__attribute__((used))" solution, because the symbol has deliberately not been given - I am using PROVIDE for the exact opposite purpose (PROVIDEing a symbol in place of the undefined one).
I don't know why I can't edit my reply, so I'm adding this to the above.
I said:
The ARM::NVICs module defines 240 ARM-generic IRQs, numbered IRQ0() to IRQ239(). The Interrupt Vector Table uses these generic definitions rather than the MCU-specific ones. Each simply assert()s, for debugging.
I should have been more explicit. In fact, each has its own PROVIDE line, of the form
PROVIDE ( ARM::NVICs::IRQ#() = ARM::NVICs::IRQ() );
And then ARM::NVICs::IRQ() in turn is PROVIDEd with ARM::ISRs::Invalid() - which is the function that does the assert.
You'll note that ALL of the functions (except the one I defined) have the address 0x60001738, which is the address of ARM::ISRs::Invalid(). I haven't duplicated that code 240 times for the IRQs.