HomePhabricator

[ELF] - Do not remove empty output sections that are explicitly assigned to…

Description

[ELF] - Do not remove empty output sections that are explicitly assigned to phdr in script.

This continues direction started in D43069.

We can keep sections that are explicitly assigned to segment in script.
It helps to simplify code.

Differential revision: https://reviews.llvm.org/D43571

Event Timeline

dim added subscribers: emaste, markj, dim.Oct 1 2018, 1:03 PM

This change breaks booting an lld-linked i386-freebsd kernel, apparently because it moves symbols around, while not updating the sections? For instance, with lld r325886, I have the following readelf output:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
[...]
  [43] .data             PROGBITS        01d15040 1515040 0f2698 00  WA  0   0 64
  [44] set_sysinit_set   PROGBITS        01e076d8 16076d8 001a28 00  WA  0   0  4
  [45] set_sysuninit_set PROGBITS        01e09100 1609100 000c38 00  WA  0   0  4
  [46] set_pcpu          PROGBITS        01e09d40 1609d40 000ef4 00  WA  0   0 64
  [47] set_vnet          PROGBITS        01e0ac38 160ac38 024340 00  WA  0   0  8
  [48] .bss              NOBITS          01e2ef80 162ef78 326a5c 00  WA  0   0 128
[...]
Symbol table '.dynsym' contains 20497 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
[...]
  2107: 01e2ef78     0 NOTYPE  GLOBAL DEFAULT   47 __bss_start
[...]
  4342: 01e2ef78     0 NOTYPE  GLOBAL DEFAULT   47 _edata

while with lld r325887, it becomes:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
[...]
  [43] .data             PROGBITS        01d15040 1515040 0f2698 00  WA  0   0 64
  [44] set_sysinit_set   PROGBITS        01e076d8 16076d8 001a28 00  WA  0   0  4
  [45] set_sysuninit_set PROGBITS        01e09100 1609100 000c38 00  WA  0   0  4
  [46] set_pcpu          PROGBITS        01e09d40 1609d40 000ef4 00  WA  0   0 64
  [47] set_vnet          PROGBITS        01e0ac38 160ac38 024340 00  WA  0   0  8
  [48] .bss              NOBITS          01e2ef80 162ef78 326a5c 00  WA  0   0 128
[...]
Symbol table '.dynsym' contains 20497 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
[...]
  2107: 01e076d8     0 NOTYPE  GLOBAL DEFAULT   43 __bss_start
[...]
  4342: 01e076d8     0 NOTYPE  GLOBAL DEFAULT   43 _edata

E.g., the data segment used to end after the last set_vnet symbol, at 0x1e0ac38 + 0x24340 = 0x1e2ef78. But with lld r325887, it apparently ends at 0x1d15040 + 0xf2698 = 0x1e076d8 ?

At first sight, it looks like lld is eliminating or ignoring the set_ sections, but not "completely". They are still listed in the headers, but the symbols aren't adjusted?

FWIW, the relevant part of the kernel linker script (https://github.com/freebsd/freebsd/blob/master/sys/conf/ldscript.i386#L136) is:

  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    KEEP (*(.gnu.linkonce.d.*personality*))
  }
  .data1          : { *(.data1) }
  _edata = .; PROVIDE (edata = .);
  __bss_start = .;
  .bss            :
[...]

Full readelf -aW outputs:

https://www.andric.com/freebsd/readelf-kernel-lld-r325886.txt
https://www.andric.com/freebsd/readelf-kernel-lld-r325887.txt

grimar added a comment.Oct 2 2018, 2:02 AM

At first sight, it looks like lld is eliminating or ignoring the set_ sections, but not "completely". They are still listed in the headers, but the symbols aren't adjusted?

I do not think it is a possible scenario. I see no reason for removing this sections, they are not empty.
And creating broken sections headers sounds unrealistic in current LLD design.

My guess it happens because of set_sysinit_set, set_sysuninit_set, set_pcpu, set_vnet are orphans (not listed in the linker script),
and our algorithm of placing the orphans started to place them in between of
(https://github.com/freebsd/freebsd/blob/master/sys/conf/ldscript.i386#L142)

_edata = .; PROVIDE (edata = .);
__bss_start = .;

for some reason. And honestly, I would not call this incorrect behavior, though I do not understand now how this patch could affect this.

Two questions:

  1. Why don't you just list them in the script at the expected place? This will solve the issue and make the behavior explicit and cleaner I think.

Its generally not good idea to rely on orphans placement algorithm because it is not standardized and may differ from linker to linker.

  1. Is it possible to get the reproduce file? (-reproduce)
dim added a comment.Oct 2 2018, 9:40 AM

Two questions:

  1. Why don't you just list them in the script at the expected place? This will solve the issue and make the behavior explicit and cleaner I think.

I wondered about that too, I think that it was just never thought about by the designers of the linker sets feature (which puts kernel module specific data into these set_foo sections.

Furthermore, we have read-only and read/write set_ sections. BFD ld and older lld seem to figure these out, then place the read-only ones just after .rodata, and the read/write ones after .data. I am not sure if there is a way to distinguish between these in a linker script? The names always match set_*, but there is no different naming scheme for r/o and r/w sets.

Its generally not good idea to rely on orphans placement algorithm because it is not standardized and may differ from linker to linker.

Yes, I agree. There are probably a few other of these orphans in our kernel section list...

  1. Is it possible to get the reproduce file? (-reproduce)

I've uploaded a reproduction tarball to https://www.andric.com/freebsd/repr-symbol-clash.tar.xz (~37 MiB).

grimar added a comment.EditedOct 3 2018, 4:29 AM

Furthermore, we have read-only and read/write set_ sections. BFD ld and older lld seem to figure these out, then place the read-only ones just after .rodata, and the read/write ones after .data. I am not sure if there is a way to distinguish between these in a linker script? The names always match set_*, but there is no different naming scheme for r/o and r/w sets.

What I meant is to specify them explicitly. You do not need to match the 'set_*' for that.
I modified the script from the reproduce file in the following way (added all set_* r/w sections before _edata symbol assignment):

...
  .data1          : { *(.data1) }
  
  set_sysinit_set    : { *(set_sysinit_set) }
  set_sysuninit_set  : { *(set_sysuninit_set ) }
  set_pcpu           : { *(set_pcpu) }
  set_vnet           : { *(set_sysinit_set) }
  
  _edata = .; PROVIDE (edata = .);
...

And that resolves the issue (at least _edata == 01e2ef78 now, I did not check the booting).
I think the same should be done for the rest (r/o) set_* sections for the safety and overall correctness of the script.

Speaking about some kind of auto distinguishing between r/o and r/w, I do not think it is possible.
The script may have ONLY_IF_RO and ONLY_IF_RW commands, but that is
about different thing and will not help here I believe.

Will the suggested solution work for you?

dim added a comment.Oct 6 2018, 11:46 AM

Furthermore, we have read-only and read/write set_ sections. BFD ld and older lld seem to figure these out, then place the read-only ones just after .rodata, and the read/write ones after .data. I am not sure if there is a way to distinguish between these in a linker script? The names always match set_*, but there is no different naming scheme for r/o and r/w sets.

What I meant is to specify them explicitly. You do not need to match the 'set_*' for that.
I modified the script from the reproduce file in the following way (added all set_* r/w sections before _edata symbol assignment):

...
  .data1          : { *(.data1) }
  
  set_sysinit_set    : { *(set_sysinit_set) }
  set_sysuninit_set  : { *(set_sysuninit_set ) }
  set_pcpu           : { *(set_pcpu) }
  set_vnet           : { *(set_sysinit_set) }
  
  _edata = .; PROVIDE (edata = .);
...

And that resolves the issue (at least _edata == 01e2ef78 now, I did not check the booting).
I think the same should be done for the rest (r/o) set_* sections for the safety and overall correctness of the script.

Speaking about some kind of auto distinguishing between r/o and r/w, I do not think it is possible.
The script may have ONLY_IF_RO and ONLY_IF_RW commands, but that is
about different thing and will not help here I believe.

Will the suggested solution work for you?

Theoretically it could, but in practice it will be very cumbersome, and error-prone, as we can have dozens or even hundreds of these set_ sections, and which ones exist or not is dependent on the user's kernel configuration. Listing them all individually is possible, but I guess only if we do some magical preprocessing on our linker script. @emaste @markj any ideas about that?

Obviously we could also attempt to fold all linker sets into one big set, but that will probably require some serious refactoring in the kernel's mechanism for reading these. Note that they're also used for kernel modules, but I think those won't have more than one linker set. They will have their own linker scripts, most likely, so those would also have to be fixed up.

dim added a comment.Oct 6 2018, 11:58 AM

My guess it happens because of set_sysinit_set, set_sysuninit_set, set_pcpu, set_vnet are orphans (not listed in the linker script),
and our algorithm of placing the orphans started to place them in between of
(https://github.com/freebsd/freebsd/blob/master/sys/conf/ldscript.i386#L142)

_edata = .; PROVIDE (edata = .);
__bss_start = .;

for some reason. And honestly, I would not call this incorrect behavior, though I do not understand now how this patch could affect this.

I'll see if I can find out why, because it would be easiest for us if we can go back to the old behavior.

Two questions:

  1. Why don't you just list them in the script at the expected place? This will solve the issue and make the behavior explicit and cleaner I think. Its generally not good idea to rely on orphans placement algorithm because it is not standardized and may differ from linker to linker.

Actually I found this binutils documentation about it: https://sourceware.org/binutils/docs/ld/Orphan-Sections.html. Quoting:

Orphan sections are sections present in the input files which are not explicitly placed into the output file by the linker script. The linker will still copy these sections into the output file by either finding, or creating a suitable output section in which to place the orphaned input section.

If the name of an orphaned input section exactly matches the name of an existing output section, then the orphaned input section will be placed at the end of that output section.

If there is no output section with a matching name then new output sections will be created. Each new output section will have the same name as the orphan section placed within it. If there are multiple orphan sections with the same name, these will all be combined into one new output section.

If new output sections are created to hold orphaned input sections, then the linker must decide where to place these new output sections in relation to existing output sections. On most modern targets, the linker attempts to place orphan sections after sections of the same attribute, such as code vs data, loadable vs non-loadable, etc. If no sections with matching attributes are found, or your target lacks this support, the orphan section is placed at the end of the file.

The command line options ‘--orphan-handling’ and ‘--unique’ (see Command Line Options) can be used to control which output sections an orphan is placed in.

It seems to be that this is fairly well-defined, and also explains why all the read-only set_ sections were placed directly after .rodata, and the read/write ones directly after .data.

grimar added a comment.Oct 8 2018, 2:20 AM

Two questions:

  1. Why don't you just list them in the script at the expected place? This will solve the issue and make the behavior explicit and cleaner I think. Its generally not good idea to rely on orphans placement algorithm because it is not standardized and may differ from linker to linker.

Actually I found this binutils documentation about it: https://sourceware.org/binutils/docs/ld/Orphan-Sections.html. Quoting:

Orphan sections are sections present in the input files which are not explicitly placed into the output file by the linker script. The linker will still copy these sections into the output file by either finding, or creating a suitable output section in which to place the orphaned input section.

If the name of an orphaned input section exactly matches the name of an existing output section, then the orphaned input section will be placed at the end of that output section.

If there is no output section with a matching name then new output sections will be created. Each new output section will have the same name as the orphan section placed within it. If there are multiple orphan sections with the same name, these will all be combined into one new output section.

If new output sections are created to hold orphaned input sections, then the linker must decide where to place these new output sections in relation to existing output sections. On most modern targets, the linker attempts to place orphan sections after sections of the same attribute, such as code vs data, loadable vs non-loadable, etc. If no sections with matching attributes are found, or your target lacks this support, the orphan section is placed at the end of the file.

The command line options ‘--orphan-handling’ and ‘--unique’ (see Command Line Options) can be used to control which output sections an orphan is placed in.

It seems to be that this is fairly well-defined, and

Yes, sure we know about that description, the overall behavior is described. And LLD follows it.

also explains why all the read-only set_ sections were placed directly after .rodata, and the read/write ones directly after .data.

I think we are talking a bit about different things here. There is no problems with the fact that red-only set_* should be placed after '.rodata',
that is indeed the behavior we have (and want) in LLD.

When we initially implemented orphan sections placement functionality, we found that even gold and bfd have differences in how they do place orphan sections and many
software relied on the particular linker script behavior implementations, so we had to tweak LLD to make it compatible with the wild software.
See that comment about our behavior:
https://github.com/llvm-mirror/lld/blob/master/ELF/Writer.cpp#L1264
we had to place tweaks like:
https://github.com/llvm-mirror/lld/blob/master/ELF/Writer.cpp#L1314

And when you write:

and the read/write ones directly after .data.

LLD do the same, isn't? The difference is that we seems place them after the symbol assignment that follows '.data' and not before it.
That is not what described in the specification.
It still follows the specification in part "the linker attempts to place orphan sections after sections of the same attribute", which
does not mention symbol assignments and other commands.

And that is what I meant when said "not standardized and may differ from linker to linker". Perhaps my wording was unfortunate.

I'll try to debug this patch too anyways. Wondering what did it change.

grimar added a subscriber: ruiu.Oct 8 2018, 2:20 AM
grimar added a comment.EditedOct 8 2018, 7:30 AM

I am still debugging it, but one observation to mention:

If you remove the .data1 : { *(.data1) } line from the script,
(https://github.com/freebsd/freebsd/blob/master/sys/conf/ldscript.i386#L141)
you'll get the same result as after this patch

.data1 is the empty section and it is anyways removed. But with this patch, it is done a bit earlier, what does not
allows our orphan placement algorithm to see/use it for finding the insertion point.

So it is just a luck that the script worked before this revision.

dim added a comment.Oct 8 2018, 12:22 PM

I am still debugging it, but one observation to mention:

If you remove the .data1 : { *(.data1) } line from the script,
(https://github.com/freebsd/freebsd/blob/master/sys/conf/ldscript.i386#L141)
you'll get the same result as after this patch

.data1 is the empty section and it is anyways removed. But with this patch, it is done a bit earlier, what does not
allows our orphan placement algorithm to see/use it for finding the insertion point.

So it is just a luck that the script worked before this revision.

Aha, I see what you mean, yikes! With lld 6.0.1, and that .data1 section commented out of the linker script:

  [40] .data             PROGBITS        01d15040 1515040 0f2698 00  WA  0   0 64
  [41] set_sysinit_set   PROGBITS        01e076d8 16076d8 001a28 00  WA  0   0  4
  [42] set_sysuninit_set PROGBITS        01e09100 1609100 000c38 00  WA  0   0  4
  [43] set_pcpu          PROGBITS        01e09d40 1609d40 000ef4 00  WA  0   0 64
  [44] set_vnet          PROGBITS        01e0ac38 160ac38 024340 00  WA  0   0  8
  [45] .bss              NOBITS          01e2ef80 162ef78 326a5c 00  WA  0   0 128
...
  2107: 01e076d8     0 NOTYPE  GLOBAL DEFAULT   40 __bss_start

so there __bss_start is also pointing at the beginning of set_sysinit_set instead. :-/

Anyhow, I hope you agree that __bss_start *should* point to the beginning of .bss, e.g. 01e2ef80, instead. In that case, this particular revision maybe just exposed another latent issue?

dim added a comment.Oct 8 2018, 1:15 PM

Here is the shortest test case I could come up with. (This uses ld.lld-r325886, but it could also use ld.lld 6.0.1.)

$ cat orphan-testcase.s
        .text
        .type           _start,@function
        .globl          _start
_start:
        retl
.start_end:
        .size   _start, .start_end-_start

        .data
        .type           var1,@object
        .globl          var1
var1:
        .long           42
        .size           var1, 4

        .section        orphan,"aw",@progbits
        .type           var2,@object
        .globl          var2
var2:
        .long           43
        .size           var2, 4

        .bss
        .type           var3,@object
        .globl          var3
var3:
        .long           0
        .size           var3, 4

$ cat orphan-testcase.ldscript1
SECTIONS
{
  .data : { *(.data) }
  .data1 : { *(.data1) }
  __bss_start = .;
  .bss : { *(.bss) }
}

$ cat orphan-testcase.ldscript2
SECTIONS
{
  .data : { *(.data) }
  /* .data1 : { *(.data1) } */
  __bss_start = .;
  .bss : { *(.bss) }
}

$ cat orphan-testcase.sh
#!/bin/sh -ex
clang -c orphan-testcase.s -o output.o
for i in 1 2; do
  ld.lld-r325886 -m elf_i386_fbsd -T orphan-testcase.ldscript$i -o output$i output.o
  readelf -aW output$i > output$i.txt
done
diff -u output1.txt output2.txt

$ ./orphan-testcase.sh
+ clang -c orphan-testcase.s -o output.o
+ ld.lld-r325886 -m elf_i386_fbsd -T orphan-testcase.ldscript1 -o output1 output.o
+ readelf -aW output1
+ ld.lld-r325886 -m elf_i386_fbsd -T orphan-testcase.ldscript2 -o output2 output.o
+ readelf -aW output2
+ diff -u output1.txt output2.txt
--- output1.txt 2018-10-08 22:11:43.254097000 +0200
+++ output2.txt 2018-10-08 22:11:43.290002000 +0200
@@ -64,6 +64,6 @@
      3: 00000001     4 OBJECT  GLOBAL DEFAULT    2 var1
      4: 00000005     4 OBJECT  GLOBAL DEFAULT    3 var2
      5: 00000009     4 OBJECT  GLOBAL DEFAULT    4 var3
-     6: 00000009     0 NOTYPE  GLOBAL DEFAULT    3 __bss_start
+     6: 00000005     0 NOTYPE  GLOBAL DEFAULT    2 __bss_start

 No version information found in this file.

E.g. with r325886 or 6.0.1, __bss_start is wrong in case the .data1 segment is commented out. With lld r325887 and higher there is no difference anymore.

grimar added a comment.Oct 9 2018, 3:08 AM

Thank you for providing the reduced test case!
I used it to do few tests about bfd and gold behavior.

  • TEST BFD

ld.bfd -v
GNU ld (GNU Binutils for Ubuntu) 2.28

ld.bfd output.o -T orphan-testcase.ldscript1 -o out.ld.1
readelf -a out.ld.1

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00200000
       0000000000000001  0000000000000000  AX       0     0     4
  [ 2] .data             PROGBITS         0000000000000001  00200001
       0000000000000004  0000000000000000  WA       0     0     1
  [ 3] orphan            PROGBITS         0000000000000005  00200005
       0000000000000004  0000000000000000  WA       0     0     1
  [ 4] .bss              NOBITS           0000000000000009  00200009
       0000000000000004  0000000000000000  WA       0     0     1

Symbol table '.symtab' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
...
     9: 0000000000000009     0 NOTYPE  GLOBAL DEFAULT    3 __bss_start

ld.bfd output.o -T orphan-testcase.ldscript2 -o out.ld.2
readelf -a out.ld.2

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00200000
       0000000000000001  0000000000000000  AX       0     0     4
  [ 2] .data             PROGBITS         0000000000000001  00200001
       0000000000000004  0000000000000000  WA       0     0     1
  [ 3] orphan            PROGBITS         0000000000000005  00200005
       0000000000000004  0000000000000000  WA       0     0     1
  [ 4] .bss              NOBITS           0000000000000009  00200009
       0000000000000004  0000000000000000  WA       0     0     1
	   
Symbol table '.symtab' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
...
     9: 0000000000000005     0 NOTYPE  GLOBAL DEFAULT    2 __bss_start

Comments:

BFD seems uses '.data1 : { *(.data1) }` for calculating the insertion point,
though it is empty and removed from the output. That is the behavior LLD had
before rL325887.
With the use of orphan-testcase.ldscript1, __bss_start points to `.bss`
With the use of orphan-testcase.ldscript2, BFD has the same output as LLD has now,
__bss_start points to `orphan`
  • TEST GOLD

ld.gold -v
GNU gold (GNU Binutils for Ubuntu 2.26.1) 1.11

ld.gold output.o -T orphan-testcase.ldscript1 -o out.gold.1
readelf -a out.gold.1

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .data             PROGBITS         0000000000000000  00001000
       0000000000000004  0000000000000000  WA       0     0     1
  [ 2] orphan            PROGBITS         0000000000000004  00001004
       0000000000000004  0000000000000000  WA       0     0     1
  [ 3] .text             PROGBITS         0000000000000008  00001008
       0000000000000001  0000000000000000  AX       0     0     4
  [ 4] .bss              NOBITS           0000000000000028  00001028
       0000000000000004  0000000000000000  WA       0     0     1
...
Symbol table '.symtab' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
...
     6: 0000000000000008     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start

ld.gold output.o -T orphan-testcase.ldscript2 -o out.gold.2
readelf -a out.gold.2

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .data             PROGBITS         0000000000000000  00001000
       0000000000000004  0000000000000000  WA       0     0     1
  [ 2] orphan            PROGBITS         0000000000000004  00001004
       0000000000000004  0000000000000000  WA       0     0     1
  [ 3] .text             PROGBITS         0000000000000008  00001008
       0000000000000001  0000000000000000  AX       0     0     4
  [ 4] .bss              NOBITS           0000000000000028  00001028
       0000000000000004  0000000000000000  WA       0     0     1

Symbol table '.symtab' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
...
     6: 0000000000000004     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start

Comments:

gold has different behavior than bfd. In case of orphan-testcase.ldscript1,
it inserts `.text` and `orphan` before .bss and makes `__bss_start` point to
`text'
In case of orphan-testcase.ldscript2 it inserts `.text` and `orphan` before
.bss and makes `__bss_start` point to `orphan'

My conclusion:
I think that the inconsistency of linkers behavior shown above shows that LLD behavior is
not incorrect. It is different. But we never tried to be fully bug compatible with bfd/gold.
First of all the problem is caused by a weak assumption in the linker script, which assumes
that orphans are placed at the particular place before the assignment command,
and my feeling it should be fixed.

Also, returning to the original kernel script, my guess is that data1 was just a hack to make
the script work with bfd? Since it is anyways empty and removed from the output, I see no reason to have it.
And without it, bfd behavior starts to be "broken".
I do not think we want to support such undocumented hacks in LLD.

As a workaround I can suggest to use .data1 : { *(.data1) BYTE(0) } construction.
It keeps the .data1 section in the output. Seems work both for LLD and bfd.

I think that the inconsistency of linkers behavior shown above shows that LLD behavior is not incorrect. It is different. But we never tried to be fully bug compatible with bfd/gold. First of all the problem is caused by a weak assumption in the linker script, which assumes that orphans are placed at the particular place before the assignment command, and my feeling it should be fixed.

I'll grant you that FreeBSD's linker script use is relying on unspecified beaviour. But independent of that I think it is an lld bug if __bss_start is not being set to the beginning of .bss. We don't have to aim for bug compatibility to decide that our current handling of __bss_start could be improved.

Also, returning to the original kernel script, my guess is that data1 was just a hack to make the script work with bfd? Since it is anyways empty and removed from the output, I see no reason to have it.

I'm not sure where .data1 originally came from - it seems to be in the default GNU linker script as far back as I can find. Some online references suggest it may be used to hold initialized medium-length data. In any case, it wasn't added as some sort of workaround.

As a workaround I can suggest to use .data1 : { *(.data1) BYTE(0) } construction. It keeps the .data1 section in the output. Seems work both for LLD and bfd.

This is probably a reasonable workaround for FreeBSD in the interim, but I do think we should change lld's __bss_start calculation.

dim added a comment.Oct 11 2018, 1:16 PM

I think that the inconsistency of linkers behavior shown above shows that LLD behavior is
not incorrect. It is different. But we never tried to be fully bug compatible with bfd/gold.
First of all the problem is caused by a weak assumption in the linker script, which assumes
that orphans are placed at the particular place before the assignment command,
and my feeling it should be fixed.

After trying out a few things, I realized that indeed, the linker cannot really know that some symbol "belongs" to a certain section anyway. E.g. with:

SECTIONS
{
  foo : { *(foo) }
  bar = .;
  baz : { *(baz) }
}

Here bar does not particularly mean "the start of baz", or "the end of foo", but it is just in between them, and I guess that is the *only* guarantee. And then of course it follows, that renaming this symbol to e.g. baz_start or whatever, does not magically make it "the start of baz" either. :)

So another solution is to put that symbol inside the section it 'belongs' to, like:

SECTIONS
{
  .data : { *(.data) }
  .data1 : { *(.data1) }
  .bss : {
    __bss_start = .;
    *(.bss)
    __bss_end = .;
   }
}

In this way, the linker will know that these symbols belong to the .bss section, and I don't think there is any way that an orphan section can be inserted between, e.g. __bss_start and the actual beginning of the first *(.bss) sections.

Our initialization code can then use __bss_start and __bss_end to clear out the section without hitting anything else.

In this way, the linker will know that these symbols belong to the .bss section, and I don't think there is any way that an orphan section can be inserted between,

Yeah, no way.

Our initialization code can then use __bss_start and __bss_end to clear out the section without hitting anything else.

Should work!