Robert B
Robert B

Reputation: 3255

avr-gcc not obeying .org directives when linking two .S files

OK, here's one file, tinyrom_2313.S:

.text

.org 0
.global init

init:
    rjmp main

.org 0x0020
.global main

main:
    cli

    // Disable watchdog timer

    wdr
    ldi r16, 0x00
    out _SFR_IO_ADDR(MCUSR), r16
    in r17, _SFR_IO_ADDR(WDTCSR)
    ori r17, _BV(WDCE) | _BV(WDE)
    out _SFR_IO_ADDR(WDTCSR), r17
    out _SFR_IO_ADDR(WDTCSR), r16

    // Disable input pullups

    ldi r16, _BV(PUD)
    out _SFR_IO_ADDR(MCUCR), r16

    // Pin directions are all
    // inputs on reset.

    // Set pin directions:
    //   PA2 - PA0 are inputs
    //   PB7 - PB0 are inputs
    //   PD6 - PD0 are outputs

    ldi r16, 0xFF
    out _SFR_IO_ADDR(DDRD), r16

    // We store the data table at 0x0100.

    ldi ZH, 0x01
loop:
    in ZL, _SFR_IO_ADDR(PINB)
    lpm r0, Z
    out _SFR_IO_ADDR(PORTD), r0
    rjmp loop

And here's the second file, single_step_rom.S:

.text
.org 0x0100
//          / N     H    C
//          H E     O S  U
//          O X     LRT  R
//          L T     DUE  R
//          D___    ANP ___
.byte 0b00000000 // 000 000
.byte 0b00001010 // 000 001
.byte 0b00000011 // 000 010
.byte 0b00000011 // 000 011
.byte 0b00001101 // 000 100
.byte 0b00001110 // 000 101
.byte 0b00001110 // 000 110
.byte 0b00000011 // 000 111
.byte 0b00000001 // 001 000
.byte 0b00000001 // 001 001
.byte 0b00000011 // 001 010
.byte 0b00000011 // 001 011
.byte 0b00001101 // 001 100
.byte 0b00001110 // 001 101
.byte 0b00001111 // 001 110
.byte 0b00001111 // 001 111
.byte 0b00000100 // 010 000
.byte 0b00001010 // 010 001
.byte 0b00000011 // 010 010
.byte 0b00000011 // 010 011
.byte 0b00000100 // 010 100
.byte 0b00001110 // 010 101
.byte 0b00001110 // 010 110
.byte 0b00000011 // 010 111
.byte 0b00000001 // 011 000
.byte 0b00000001 // 011 001
.byte 0b00000011 // 011 010
.byte 0b00000011 // 011 011
.byte 0b00000100 // 011 100
.byte 0b00001110 // 011 101
.byte 0b00001111 // 011 110
.byte 0b00001111 // 011 111
.byte 0b00000000 // 100 000
.byte 0b00001010 // 100 001
.byte 0b00001010 // 100 010
.byte 0b00000000 // 100 011
.byte 0b00001101 // 100 100
.byte 0b00001101 // 100 101
.byte 0b00001110 // 100 110
.byte 0b00000011 // 100 111
.byte 0b00000001 // 101 000
.byte 0b00000001 // 101 001
.byte 0b00001010 // 101 010
.byte 0b00000000 // 101 011
.byte 0b00001101 // 101 100
.byte 0b00001101 // 101 101
.byte 0b00001111 // 101 110
.byte 0b00001111 // 101 111
.byte 0b00000100 // 110 000
.byte 0b00001010 // 110 001
.byte 0b00001010 // 110 010
.byte 0b00000000 // 110 011
.byte 0b00000100 // 110 100
.byte 0b00001101 // 110 101
.byte 0b00001110 // 110 110
.byte 0b00000011 // 110 111
.byte 0b00000001 // 111 000
.byte 0b00000001 // 111 001
.byte 0b00001010 // 111 010
.byte 0b00000000 // 111 011
.byte 0b00000100 // 111 100
.byte 0b00001101 // 111 101
.byte 0b00001111 // 111 110
.byte 0b00001111 // 111 111

Now, I compile:

avr-gcc -nostartfiles -mmcu=attiny2313 -Wl,-Map,map.txt -o single_step_rom.elf single_step_rom.S tinyrom_2313.S

I found that it put single_step_rom properly at 0x0100, but then it stuck tinyrom_2313 at 0x0140. In other words, it looks like avr-gcc completely ignored the .org directive in tinyrom_2313.S.

Swapping the order of the .S files gives the opposite problem: tinyrom_2313 is properly at 0x0000, but then single_step_rom is placed at 0x0142, as if avr-gcc is using .org 0x0100 as additive to where the previous file left off.

The description of .org is:

.org new-lc, fill: Advance the location counter of the current section to new-lc. new-lc is either an absolute expression or an expression with the same section as the current subsection.

Clearly I'm not understanding what is meant by "advance to". Perhaps they meant "advance by". In any case, how do I get avr-gcc to place the code where specified? I'm hoping for a simpler solution than defining a script file and having to pass it via -Wl,-T.

I'll accept a "yeah, you can't do it without a script file" answer as long as someone can explain to me what is meant by "advance to" in the description of the .org directive.

Upvotes: 1

Views: 131

Answers (1)

emacs drives me nuts
emacs drives me nuts

Reputation: 3918

There's an entity which describes the "current location" during locating step when linking. The value of it is the simple dot . in the linker description file. Take for example this part of ./avr/lib/ldscripts/avr5.xn which locates the static constructors and destructors and defines some symbols like __ctors_start along with it:

     __ctors_start = . ;
     *(.ctors)
     __ctors_end = . ;
     __dtors_start = . ;
     *(.dtors)
     __dtors_end = . ;

One feature of . is that it cannot be moved backwards.

Coming to your project, you are linking by means of

> avr-gcc ... single_step_rom.S tinyrom_2313.S

Due to the order of input files, the first thing that the linker/locator sees is

.text
.org 0x100

from single_step_rom.S (or from respective .o) to be precise. Thus .is advanced to 0x100, the data is put there, and then comestinyrom_2313.S`'s

.text
.org 0

As . cannot be moved backwards, the effect of .org 0x0 is void.

How to fix it

Way 1: (Mis) use existing input sections.

The .text output section gathers many input sections, just have a look at the default linker description file for your device. For example, in my verion of binutils there is *(.hightext) after *(.text*). So you can locate code to section .text and table data to .hightext, like in

.section .hightext, "a", @progbits
.balign 0x100
.global my_data
my_data:
   .byte ...

and then load the address like so:

    ldi ZH, hi8(my_data)

Notice that .org will disappear altogether. Moreover, the first jump instruction would technically go to .vectors, because it is the 1st vector (reset), and all other code would go to .text.

Way 2: Use linker script augmentation

Use augmentation like

SECTIONS
{
    .text :
    {
        . = ALIGN(0x100);
        *(.my_data*)
    }
}
INSERT AFTER .text

Then store this few lines as my_data.ld and provide it by means of -T mydata.ld during link. Put your table data into section .my_data. To access it, define an appropriate symbol like in way 1.

Upvotes: 1

Related Questions