What causes __do_copy_data to be linked into an executable?

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

Over in arduinoland, a user was having trouble with an "all assembly" BLINK example.

Apparently, this was because variables defined in the .S file ".data" segment were not getting initialized.  Even though the .S file was assembled and linked with the "avr-gcc" command linked with the Arduino core, apparently the .S file was the only thing that contained initialized data, and so __do_copy_data() was not present in the final .elf file (it did contain vectors, startup code, and __do_clear_bss())

 

I had assumed that the .data segment would get copied from flash if it was of non-zero size and symbols therein were reference, but apparently not.  How is this decided?  Is there a way that I can have my .S file force the linker to include do_copy_data?

 

The Arduino thread is here: https://forum.arduino.cc/index.p...

Here's a sample illustrating the issues:
 

main.c:

int main() {
    setup();
    while (1)
	loop();
}

blink.S:

#include "avr/io.h"
	
.text
	
.global setup
setup:
  lds r16, ledStatus
  ret

.global loop
loop:
  ret

.data

nextSwitchAfterMillis:
.long 0, 1, 2, 3

ledStatus:
.byte 0

Compile with "avr-gcc -Os -mmcu=atmega328 main.c blink.S, and use objdump to confirm that do_copy_data is absent...

This topic has a solution.
Last Edited: Mon. Jun 15, 2020 - 09:25 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When I build:

#include <avr/io.h>

int foo = 12345;

int main(void)
{
    while (1)
    {
    }
}

it generates:

	.text
.Ltext0:
	.cfi_sections	.debug_frame
	.section	.text.startup.main,"ax",@progbits
.global	main
	.type	main, @function
main:
.LFB0:
	.file 1 ".././main.c"
	.loc 1 6 0
	.cfi_startproc
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
.L2:
	rjmp .L2
	.cfi_endproc
.LFE0:
	.size	main, .-main
.global	foo
	.section	.data.foo,"aw",@progbits
	.type	foo, @object
	.size	foo, 2
foo:
	.word	12345
	.text
.Letext0:

   ...

.global __do_copy_data

My understanding is that simply including that last line (also true for clear BSS) is enough to force that stuff to link.

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

Sadly, the only way I found to coax it into initializing the asm variables was by including a global or static dummy non-zero var in the C code. If the variable is zero, then only  __do_clear_bs() is called.

 


#include <avr/io.h>

extern "C" void setup();
extern "C" void loop();

volatile uint16_t dummy = 0xCDAB;

int main() {
	setup();
	while (1)
	loop();
}

 

edit: well, while I was playing around another, probably better, solution emerged above cheeky

Last Edited: Mon. Jun 15, 2020 - 09:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:
Sadly, the only way I found
Did that ".global _do_copy_data" not do it then?

 

I just ran an experiment to build an "empty" main(). I used -save-temps to get a copy of main.s. I then created a separate project where the only file was that main.s. Without change it builds as expected. I then added just

.global __do_copy_data

rebuilt and now in the .lss I have:

00000068 <__ctors_end>:
  68:	11 24       	eor	r1, r1
  6a:	1f be       	out	0x3f, r1	; 63
  6c:	cf ef       	ldi	r28, 0xFF	; 255
  6e:	d8 e0       	ldi	r29, 0x08	; 8
  70:	de bf       	out	0x3e, r29	; 62
  72:	cd bf       	out	0x3d, r28	; 61

00000074 <__do_copy_data>:
  74:	11 e0       	ldi	r17, 0x01	; 1
  76:	a0 e0       	ldi	r26, 0x00	; 0
  78:	b1 e0       	ldi	r27, 0x01	; 1
  7a:	ec e9       	ldi	r30, 0x9C	; 156
  7c:	f0 e0       	ldi	r31, 0x00	; 0
  7e:	02 c0       	rjmp	.+4      	; 0x84 <__do_copy_data+0x10>
  80:	05 90       	lpm	r0, Z+
  82:	0d 92       	st	X+, r0
  84:	a0 30       	cpi	r26, 0x00	; 0
  86:	b1 07       	cpc	r27, r17
  88:	d9 f7       	brne	.-10     	; 0x80 <__do_copy_data+0xc>
  8a:	0e 94 4b 00 	call	0x96	; 0x96 <main>
  8e:	0c 94 4c 00 	jmp	0x98	; 0x98 <_exit>

That will drop out without actually copying anything (not surprisingly!) but I have added __do_copy_data only by adding that ".global __do_copy_data" line. So that is the magic. If I add ".global __do_clear_bss" then I also get:

0000008a <__do_clear_bss>:
  8a:	21 e0       	ldi	r18, 0x01	; 1
  8c:	a0 e0       	ldi	r26, 0x00	; 0
  8e:	b1 e0       	ldi	r27, 0x01	; 1
  90:	01 c0       	rjmp	.+2      	; 0x94 <.do_clear_bss_start>

00000092 <.do_clear_bss_loop>:
  92:	1d 92       	st	X+, r1

00000094 <.do_clear_bss_start>:
  94:	a0 30       	cpi	r26, 0x00	; 0
  96:	b2 07       	cpc	r27, r18
  98:	e1 f7       	brne	.-8      	; 0x92 <.do_clear_bss_loop>

(again this is a "do nothing" loop)

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

clawson wrote:

Did that ".global _do_copy_data" not do it then?

 

Sure, I just missed your post because I was investigating the problem and didn't refresh, actually I was replying to the OP.

Last Edited: Mon. Jun 15, 2020 - 10:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
.global __do_copy_data

 

Adding this to the .S file cause __do_copy_data() to be included, the data properly initialized, and fixed the problem...  Thanks!

 

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

I was gonna say the linker brings it in from crt0.   See here.

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

Matt,

Look closer at the conditional compilation in that file. So that was how it was done up to GCC 4.3 then the mechanism changed.

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

avrfreaks does not support Opera. Profile inactive.