Can't find strings in compiled sketch

Hi all

It's probably something stupid I am doing but I can't find any strings I used in the final compiled sketch.
I have converted the .hex back to a binary file (using Srecord and AVR Studio) and have also looked at the .elf and nothing.
(By looking at the elf file, I mean running it thru avr-objdump).

The strange thing is the compiler does not report an increase in the compiled sketches size even if I increase or decrease the number of chars in the strings.

Herewith an example (using the Uno):

int led = 13;
void setup() {                
  pinMode(led, OUTPUT);   
 Serial.begin(9600);  
}
void loop() {
  digitalWrite(led, HIGH); 
  delay(1000);    
  digitalWrite(led, LOW); 
  delay(1000);  
}

void PrintIt()
{
  Serial.print("this is a short string");
}

The resulting size is:

Binary sketch size: 2,362 bytes (of a 32,256 byte maximum)

If I change the string in Serial.print, the size is reported as exactly the same.
Yes I know that the function PrintIt() is never called, but surely that should make no difference and should still be included in the compiled sketch?

Can anyone please give me some pointers?
Thank you.

If printIt is never called, the linker will remove the code as it is redundant.

OK, any switches we can pass to tell the linker to just leave things like this alone?

Only if you want to end up with an absolutely massive sketch with every function in the core in it, used or not.

Better to actively use the function so it gets included.

Why do you need to? It’s wasted code space which will just lead to unnecessary wear in the flash of your uC.

I’m not sure that you can edit the linker flags for Arduino, they are built into the program.

Arduino links with --gc-sections which along with some compiler flags it uses will strip dead code. This is actually important as the IDE compiles ALL of the core libraries including ones you don’t need, so if you don’t strip dead code you will end up with a sketch which is larger than it needs to be.
An example:
Empty sketch containing just:

void loop{} 
void setup{}

.
With the normal flags it compiles to 674 bytes.
When it doesn’t strip dead code, it compiles to 964 bytes
That is a 43% increase in size.

Another example:
sketch containing just

void loop{} 
void setup{Serial.begin(9600);}

With the normal flags it compiles to 3270 bytes.
When it doesn’t strip dead code, it compiles to 16906 bytes
That is a 417% increase in size.

A larger example:
sketch containing:

#include <SPI.h>
#include <Ethernet.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1, 177);
void setup(){Ethernet.begin(mac, ip);Serial.print("Hi");}
void loop(){}

With the normal flags it compiles to 5732 bytes.
When it doesn’t strip dead code, it compiles to 29498 bytes
That is a 415% increase in size.

Finally, your sketch goes from:
4268 bytes to 17826 bytes

majenko:
Only if you want to end up with an absolutely massive sketch with every function in the core in it, used or not.

What I meant was, are there any switches that one can apply locally (to certain parts of the code) as opposed to globally?

I understand what you say about conserving memory but the project I'm working on, the spec calls for a version number and some other info embedded into the actual compiled code and since there are several issues with PROGMEM, I thought about just putting into a string but I know that the linker will leave out any variables that are declared and populated but not used, hence thought about putting into a pre-defined function like Serial.print, but it strips that too.

For this project, having the embedded info is more important than saving space and each AVR will only be programmed several times so wear on the flash is not an issue.

If you start doing that you will have no idea where in the flash your string is, so how will you be able to use it?

It is possible using a bit of a hack approach to get the function to stay.

int led = 13;
void (*fptr)(void); 
void setup() {  
  fptr = &PrintIt;             
  pinMode(led, OUTPUT);   
  Serial.begin(9600);  
}
void loop() {
  digitalWrite(led, HIGH); 
  delay(1000);    
  digitalWrite(led, LOW); 
  delay(1000);  
}

void PrintIt()
{
  Serial.print("this is a short string");
}

But that doesn't solve the problem of knowing where the string is stored in the flash. It also won't reduce RAM consumption as the string will be loaded into the data space of the RAM when the processor starts (even though the function is never called).

Understood, but not a problem in this case as the string will be searched for manually.

Thanks Tom, will give it a go and report back.

If you want to do something like this, you have gone beyond what Arduino IDE was designed to do. You should start using either AVR Studio, or UECIDE.
If you use the latter of the two (which is very similar to the Arduino IDE, but much better written), you can achieve exactly what you want in the way you want by doing something like this:

#pragma parameter ldflags=-Os::-Wl,--gc-sections::-Wl,--relax::-Wl,--section-start=.aStringSpace=3F00

//just make sure that the section start is within your flash space, but beyond what the program uses so they don't overlap.

int led = 13;

const char __attribute__ ((section (".aStringSpace"))) __attribute__ ((used)) aString[64] = {"this is a short string"};
volatile char keeper;
void setup() {       
  keeper = pgm_read_byte((short*)(aString)); //keeps the string, and it stays in the program space. Not needed if you do pgm_read on the string somewhere else in your sketch.
  pinMode(led, OUTPUT);   
  Serial.begin(9600);  
}
void loop() {
  digitalWrite(led, HIGH); 
  delay(1000);    
  digitalWrite(led, LOW); 
  delay(1000);  
}

Thank you very much Tom.

The code snippet you posted previously works perfectly.

As regards using AVR Studio or UECIDE, I assume one can use the existing functions of the Arduino and any other libs that others have written.
I have used AVR Studio before, but only to compile stuff for the AVR's using assembler (hate the asm syntax of the Arduino IDE)
and wouldn't know how to incorporate the existing functions/core and other libs with it.

UECIDE is basically just the Arduino IDE rewritten, vastly improved, and made compatible with additional architectures.
http://uecide.org/
It should work as a drop in replacement but allows you to do more stuff.

Fantastic, thanks.

UnoDueTre:
the spec calls for a version number and some other info embedded into the actual compiled code

I just call Serial.println(FILE " " DATE " " TIME) in setup so the sketch tells me what it is when it starts up. You could include hard-coded version information too if you wanted.

PeterH:
I just call Serial.println(FILE " " DATE " " TIME) in setup so the sketch tells me what it is when it starts up. You could include hard-coded version information too if you wanted.

Hi PeterH and thanks for the suggestion, however Serial.print is never actually run, not that one anyway.
I'm using it simply as a place holder for that info.
The reason I chose Serial.print is that it's used else where (but not to TX the actual information) so there isn't much overhead in adding another Serial.print.

UnoDueTre:
The reason I chose Serial.print is that it's used else where (but not to TX the actual information) so there isn't much overhead in adding another Serial.print.

Trouble is, and I did mention it before, it doesn't save you any RAM. The string will be loaded into the RAM when the program starts, regardless of whether or not you use the function. So doing that doesn't actually save you any memory over using a simple:
const char* string = "Boo";

That is why you need either to use the PROGMEM attribute to force it in the program memory, and then use the strcpy_P function when you want to use it. Or use the example for UECIDE which essentially does the same thing but doesn't require the PROGMEM attribute.

Hi Tom

All very valid points.
I initially thought about using PROGMEM but after reading more about it in the Reference section;

However experiments have indicated that, in various versions of Arduino (having to do with GCC version), PROGMEM may work in one location and not in another.

I didn't feel like wrestling with the linker and opted to use Serial.print instead since it's used elsewhere to send other information.
That is when I hit the problem of the linker discarding it because it was not being called.
The reasoning being that by simply changing the code and calling the PrintIt function, the text in that specific Serial.print could be sent via serial if required at a future date.

All in all, using UECIDE makes the most sense and is the way to go.
I have downloaded it and giving it a good test run to see if there are any bugs then will probably change over to it.
In the mean time, I will go with your suggestion of using void (*fptr)(void); (even though it uses RAM) until I make the change to UECIDE.

UnoDueTre:
I understand what you say about conserving memory but the project I’m working on, the spec calls for a version number and some other info embedded into the actual compiled code …

How about this, using the normal IDE?

volatile char VERSION [] PROGMEM = "Nick Gammon version 1.0";

void setup ()
  {
  VERSION [0];
  }  // end of setup

void loop ()
  {
  }  // end of loop

PROGMEM keeps it in program memory (saving RAM). volatile makes sure it is not optimized away. The line in setup makes sure it is referenced and thus the linker keeps it.

Object file:

00000068 <VERSION>:
  68:	4e 69 63 6b 20 47 61 6d 6d 6f 6e 20 76 65 72 73     Nick Gammon vers
  78:	69 6f 6e 20 31 2e 30 00                             ion 1.0.

Notice the string is there.

Yes it is, thanks for that Nick.
I was just a bit scared of using PROGMEM because of what was mentioned about it in the Reference section.

BTW, is there a version 2.0 of you? :slight_smile:

I'm not sure what the reference section is getting at, but a lot of code wouldn't work if PROGMEM was flaky. It is a very common technique to use it for strings.

BTW, is there a version 2.0 of you?

Well, I have kids, if that answers your question. :wink:

What put me off using PROGMEM initially was not only what is written about it in the reference section, but also severaal posts on this forum, the AVR freaks forum and also stack exchange.

I got the impression that PROGMEM was both gcc version and position (in the code) dependent and thus I decided to just leave it alone.

What I meant was how do you find the time (and patience) to answer so many questions.