WINAVR change in placement of string literals

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Somewhere between WinAVR versions 4.9.2 and 5.4.0 there apparently was a compiler change that effected the placement of string literals.

 

I use a bunch of defines to generate configuration info in EEPROM.  This is loaded into an ATMega EEPROM by the bootloader.

 

Example:


//    Switch    ---------------------------------------------------------------------
typedef struct Config_Item_Switch_
    {
    Config_Item_t        Header;
    NM_ObjectID         ID;
    NM_ObjectID            PointPositionID;
    NM_ObjectID            RelayID;
    char                *SuppressMotorExpr;
    } Config_Item_Switch_t;


#define Config_Switch( ID_, PointPositionID_, RelayID_, SuppressMotorExpr_ ) \
    extern char MACROJOIN( SuppressMotorExpr, __LINE__)[] EEMEM; \
    Config_Item_Switch_t ConfiguredObject_ ## ID_ EEMEM = \
        { \
        .Header.Type = CT_Switch, \
        .Header.Size = sizeof(Config_Item_Switch_t), \
        .ID = (ID_), \
        .PointPositionID = (PointPositionID_), \
        .RelayID = (RelayID_), \
        .SuppressMotorExpr = MACROJOIN( SuppressMotorExpr, __LINE__) \
        }; \
    char MACROJOIN( SuppressMotorExpr, __LINE__)[] EEMEM = (SuppressMotorExpr_) \


Note the line " char                *SuppressMotorExpr;"

 

I realize that I may have been counting on an undocumented aspect of the compiler ... but ...

 

In the past the string literal was placed immediately after the struct, allowing the config loader to know how to scan accross items.  Now it is being placed immediately before the struct.  This is causing the code that loads the config info to fail.

 

Is there a compiler setting that will either:

- Place all EEPROM string literals after the struct (as the compiler did before), or

- Place all EEPROM string literals at the end of EEPROM after all other items are placed.

 

Or, a different declaration that would recreate the old behavior.  ... I suppose I could name a string item, reference the name in the " char                *SuppressMotorExpr;" such as "char *Expression_1234" and then declare Expression_1234 immediatly after the struct.

 

Regards,

 

Chuck Hackett

 

 

Last Edited: Fri. Oct 20, 2017 - 01:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wait a minute, I already DID allocate a second variable ... "MACROJOIN( SuppressMotorExpr, __LINE__)" ... how come the compiler is now putting it "SuppressMotorExpr1234" BEFORE the allocation of the "ConfiguredObject_ ## ID_" item?

 

Hmmm, is it due to the fact that I have the "extern char MACROJOIN( SuppressMotorExpr, __LINE__)[] EEMEM;" before the "ConfiguredObject_ ## ID_" item?

 

Maybe that treatment is what changed in the compiler/linker?

 

To be clear: The way I ran into this is that I got a new laptop and installed the new Atmel Studio (newer than the version on my desktop).  Both machines are using the EXACT same solution file, project files, etc. (shared via dropbox).

 

I suppose it could be a setting in Atmel Studio that is not saved in the solution or project files (thus could be different between machines) but, at the moment at least, I don't know what it would be ...

 

Regards,

 

Chuck Hackett

 

(PS: apologies to the moderator for initially posting this in the wrong forum)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

(sorry to rattle on)

Just realized that I have to have the "extern char MACROJOIN( SuppressMotorExpr, __LINE__)[] EEMEM;" before the "ConfiguredObject_ ## ID_" item because of the reference within the struct ...

 

Regards,

 

Chuck Hackett

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You seem to be getting some very good advice.....cheeky

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Somewhere between WinAVR versions 4.9.2 and 5.4.0

You mean "avr-gcc"?  WinAVR has date-based version numbers like "20100120"...

 

Your sample of code isn't complete enough to evaluate.  Can you provide a complete example of the macro invocation, along with what it generates (from -E) with each compiler version?

 

I know that the last time the gcc folk "gratuitously" changed pre-processor behavior (between 2.9 and 3.4?), we never did find any workaround, nor any traction for adding a knob to allow the old behavior.  :-(

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'll try to work up a minimal sample tomorrow (oops, today here on the east coast) that shows the problem and eliminate all the macro stuff.

 

Is there a compiler option that causes all strings to be allocated elsewhere (in EEPROM in this case) like maybe at the end? 

 

I don't need them after the config item, i just can't have them before it because the code that digests the config info steps through the config area looking at item types and lengths (in the item header) and whether it expects the item (by type) to be followed by one or more strings.  If all strings were collected elsewhere it's no problem as the struct contains the (EEPROM) pointer to the string.

 

Chuck

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah; I think I get it now.  You're not asking about pre-processor/macro stuff at all!

You have something like:

typdef struct foo {
    int a;
    int b;
    char *s;
} foo_t;

foo_t x = { 0, 1, "test" };
foo_t y = { 3, 4, "zap" };

And it used to generate:

00 00  01 00  06 00  't' 'e' 's' t' 00
03 00  04 00  11 00  'z' 'a' 'p' 00

And now it does:

 't' 'e' 's' t' 00  00 00  01 00  00 00 
 'z' 'a' 'p' 00     03 00  04 00  0B 00

 

You could probably put the structs into an array, if you're accessing them as binary data rather than symbols:

foo_t x[] = {
  { 0, 1, "test" };
  { 3, 4, "zap" };
}

that should keep the structs contiguous with each other, at least; I'm not sure where the strings will end up.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Reading between the lines, your problem seems to be one of Symbol ordering. WinAVR-20100110 ordered symbols in the order they were defined in your source, the later Atmel Toolchain did not.

 

This thread from a while ago discusses that "feature"

https://www.avrfreaks.net/forum/why-does-renaming-global-variable-modifies-hex-file

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

At some stage the linker's allocation strategy in GCC changed and it now places items in the reverse order to previously. That is why C code should NEVER rely on the placement of data in memory. One day:

int a;
int b;
int c;

int main(void) {
}

may place the items in a,b,c order in memory and the next it might be c,b,a (or if the linker authors were really messing about a,c,b or c,a,b or something!)

 

The ONLY way to guarantee the order of a,b,c in the above (apart from assigning them to sections and providing a linker script of -section-start's to set the addresses manually) is:

struct {
  int a;
  int b;
  int c;
} foo;

int main(void) {
}

Within 'foo' there is no question about the ordering of a,b,c now (though you still have to think about aligmnet/packing issue but not for AVR where the alignment is byte-wise). If you now want them in EEPROM add an EEMEM to that. And if the layout is more complex, build it all up in one big struct:

typedef struct {
  int a;
  int b;
  int c;
} foo;

typedef struct {
  char d;
  long e;
  float f;
} bar;

struct {
  foo some;
  bar more;
} eedata EEMEM;

int main(void) {
}

In the EEPROM the order will be foo.a, foo.b, foo.c, bar.d, bar.e, bar.f

 

PS as I noted in https://www.avrfreaks.net/comment... even with:

struct {
  foo some;
  bar more;
} eedata EEMEM;

there is still one "unknown" and that is the base address for this. As long as this is the ONLY item in the .eeprom section (EEMEM) in the entire program it's a fair bet it will be at EEPROM location 0 but if you wanted to be totally sure you might want to use __attribute__((section(".in_my_control"))) rather than EEMEM on it then arrange for the objcopy that creates the .eep to do the same that is usually done for EEMEM (.eeprom). But, I guess as long as you are sure there is only one object in .eeprom then the stuff that already allocates this to 0x810000 (virtual) and then 0x0000 (actual) will guarantee it is at 0

Last Edited: Fri. Oct 20, 2017 - 08:24 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
C code should NEVER rely on the placement of data in memory.

Absolutely!

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

> I realize that I may have been counting on an undocumented aspect of the compiler ...

 

 

This is the answer to your question.  The order of location is not specified.

 

> but ...

 

No "but" :-)

 

The order is not specified.  If it happens to please you just by accident, then it works just by accident.

 

If you need specific order, not just by accident, you can user SORT in your linker description file and sort symbols according to name or size or alignment or...

 

For EEPROM it might be a good idea to have ONE structure to model its layout.

avrfreaks does not support Opera. Profile inactive.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

SprinterSB wrote:
 If it happens to please you just by accident, then it works just by accident.

+9999 !!

 

#WorksByAccident #WorksByLuck

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My bad ... At the outset I figured it must have been an unwitting reliance on a tool-chain characteristic which happened to change.

 

This whole process was used to allow my customer to 'build' a configuration file by just using a (large) series of macros that I provided them and does rely on things being in a certain order in EEMEM.

 

In the short term I'll try surrounding each of my 'config items' within a struct (and possibly the entire set of config items as well) but, in the long term (already in the works) all configuration activity is being moved to the PC-based program used to control the system (7.5" gauge ride-on railroad signals) and this whole issue will go away.  The PC program will configure the controllers across the same network used for operation (CAN Bus).  This will remove the current requirement for a user to use Atmel Studio to compile the EEMEM image and load it into EEMEM via the boot loader (never did like that process ... not very user-friendly ... but it was the fastest way for me to get up and running).

 

Thanks to all for the assist (... and not rapping my knuckles too hard :-) )!

 

Regards,

 

Chuck Hackett