Bug 3052

Summary: ld lma assignment change breaks x86-64 Linux 2.6 kernel
Product: binutils Reporter: H.J. Lu <hjl.tools>
Component: ldAssignee: Alan Modra <amodra>
Status: RESOLVED FIXED    
Severity: normal CC: amodra, bug-binutils
Priority: P2    
Version: 2.18   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed: 2006-08-16 07:58:44
Attachments: A testcase

Description H.J. Lu 2006-08-14 22:49:36 UTC
This change

http://sourceware.org/ml/binutils/2006-07/msg00314.html

beaks x86-64 2.6 kernel. Before the change, we have

There are 48 section headers, starting at offset 0x2075af8:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg
Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00     
0   0  0
  [ 1] .text             PROGBITS        ffffffff80100000 100000 20ea98 00  AX 
0   0 64
  [ 2] __ex_table        PROGBITS        ffffffff8030eaa0 30eaa0 0051e0 00   A 
0   0  8

After the change, we got

There are 48 section headers, starting at offset 0x215a658:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg
Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00     
0   0  0
  [ 1] .text             PROGBITS        ffffffff80100000 300000 20ea98 00  AX 
0   0 64
  [ 2] __ex_table        PROGBITS        ffffffff8030eaa0 50eaa0 0051e0 00   A 
0   0  8
Comment 1 H.J. Lu 2006-08-15 21:35:46 UTC
The linker script in question:

  .vsyscall_0 -10*1024*1024: AT ((LOADADDR(.data.cacheline_aligned) +
SIZEOF(.data.cacheline_aligned) + 4095) & ~(4095)) { *(.vsyscall_0) }
  __vsyscall_0 = LOADADDR(.vsyscall_0);
  . = ALIGN(64);
  .xtime_lock : AT ((LOADADDR(.vsyscall_0) + SIZEOF(.vsyscall_0) + 63) & ~(63))
{ *(.xtime_lock) }
  xtime_lock = LOADADDR(.xtime_lock);
  . = ALIGN(16);
  .vxtime : AT ((LOADADDR(.xtime_lock) + SIZEOF(.xtime_lock) + 15) & ~(15)) {
*(.vxtime) }
  vxtime = LOADADDR(.vxtime);
  . = ALIGN(16);
  .wall_jiffies : AT ((LOADADDR(.vxtime) + SIZEOF(.vxtime) + 15) & ~(15)) {
*(.wall_jiffies) }
  wall_jiffies = LOADADDR(.wall_jiffies);
  . = ALIGN(16);
  .sys_tz : AT ((LOADADDR(.wall_jiffies) + SIZEOF(.wall_jiffies) + 15) & ~(15))
{ *(.sys_tz) }
  sys_tz = LOADADDR(.sys_tz);
  . = ALIGN(16);
  .sysctl_vsyscall : AT ((LOADADDR(.sys_tz) + SIZEOF(.sys_tz) + 15) & ~(15)) {
*(.sysctl_vsyscall) }
  sysctl_vsyscall = LOADADDR(.sysctl_vsyscall);
  . = ALIGN(16);
  .jiffies : AT ((LOADADDR(.sysctl_vsyscall) + SIZEOF(.sysctl_vsyscall) + 15) &
~(15)) { *(.jiffies) }
  jiffies = LOADADDR(.jiffies);
  . = ALIGN(16);
  .xtime : AT ((LOADADDR(.jiffies) + SIZEOF(.jiffies) + 15) & ~(15)) { *(.xtime) }
  xtime = LOADADDR(.xtime);
  .vsyscall_1 ADDR(.vsyscall_0) + 1024: AT (LOADADDR(.vsyscall_0) + 1024) {
*(.vsyscall_1) }
  . = LOADADDR(.vsyscall_0) + 4096;

  . = ALIGN(8192); /* init_task */
  .data.init_task : { *(.data.init_task) }

The resulting sections should be

  [14] .data.cacheline_a PROGBITS        ffffffff804dd500 4dd500 009600 00  WA 
0   0 128
  [15] .vsyscall_0       PROGBITS        ffffffffff600000 600000 000103 00  AX 
0   0  1
  [16] .xtime_lock       PROGBITS        ffffffffff600140 600140 00000c 00  WA 
0   0 64
  [17] .vxtime           PROGBITS        ffffffffff600150 600150 000038 00  WA 
0   0 16
  [18] .wall_jiffies     PROGBITS        ffffffffff600190 600190 000008 00  WA 
0   0 16
  [19] .sys_tz           PROGBITS        ffffffffff6001a0 6001a0 000008 00  WA 
0   0 16
  [20] .sysctl_vsyscall  PROGBITS        ffffffffff6001b0 6001b0 000004 00  WA 
0   0 16
  [21] .jiffies          PROGBITS        ffffffffff6001c0 6001c0 000008 00  WA 
0   0 16
  [22] .xtime            PROGBITS        ffffffffff6001d0 6001d0 000010 00  WA 
0   0 16
  [23] .vsyscall_1       PROGBITS        ffffffffff600400 600400 000028 00  AX 
0   0  1
  [24] .data.init_task   PROGBITS        ffffffff804e8000 6e8000 003000 00  WA 
0   0 32

This change:

                    last = r->last_os->output_section_statement.bfd_section; 
                    /* If the current vma overlaps the previous section,
                       then set the current lma to that at the end of
                       the previous section.  The previous section was
                       probably an overlay.  */
                    if ((dot >= last->vma
                         && dot < last->vma + last->size)
                        || (last->vma >= dot
                            && last->vma < dot + os->bfd_section->size))
                      lma = last->lma + last->size;

                    /* Otherwise, keep the same lma to vma relationship
                       as the previous section.  */
                    else
                      lma = dot + last->lma - last->vma;

                    if (os->section_alignment != -1)
                      lma = align_power (lma, os->section_alignment);
                    os->bfd_section->lma = lma;

mishandles it.
Comment 2 H.J. Lu 2006-08-15 21:53:57 UTC
This change

--- ldlang.c.foo        2006-08-15 14:53:05.000000000 -0700
+++ ldlang.c    2006-08-15 14:44:19.000000000 -0700
@@ -4742,11 +4742,14 @@ lang_do_assignments_1 (lang_statement_un
                        || (last->vma >= dot
                            && last->vma < dot + os->bfd_section->size))
                      lma = last->lma + last->size;
-
+                   else
+                     lma = dot;
+#if 0
                    /* Otherwise, keep the same lma to vma relationship
                       as the previous section.  */
                    else
                      lma = dot + last->lma - last->vma;
+#endif

                    if (os->section_alignment != -1)
                      lma = align_power (lma, os->section_alignment);

seems to generate a working kernel.
Comment 3 H.J. Lu 2006-08-16 00:39:17 UTC
The lma values set by lang_do_assignments_1 are used for
_bfd_elf_map_sections_to_segments and those lma values later
are overridden by lang_size_sections_1 with a different logic.
The values set up by lang_do_assignments_1 may incorrect.
Comment 4 H.J. Lu 2006-08-16 01:07:59 UTC
Created attachment 1226 [details]
A testcase

Here is a testcase. old.sec has the dump from the correct output:

[hjl@gnu-16 lma]$ make LD=ld
as   -o foo.o foo.s
ld -o foo -T foo.t foo.o
readelf -S -l --wide foo > foo.sec
diff -up foo.sec old.sec
--- foo.sec	2006-08-15 18:07:37.000000000 -0700
+++ old.sec	2006-08-15 18:04:38.000000000 -0700
@@ -1,15 +1,15 @@
-There are 8 section headers, starting at offset 0x400058:
+There are 8 section headers, starting at offset 0x302058:

 Section Headers:
   [Nr] Name		  Type		  Address	   Off	  Size	 ES Flg
Lk Inf Al
   [ 0] 		  NULL		  0000000000000000 000000 000000 00    
 0   0	0
-  [ 1] .text		  PROGBITS	  ffffffff80100000 300000 000001 00  AX
 0   0	4
-  [ 2] .data.cacheline_a PROGBITS	  ffffffff80100040 300040 000001 00  WA
 0   0	1
-  [ 3] .vsyscall_0	  PROGBITS	  ffffffffff600000 400000 000001 00  AX
 0   0	1
-  [ 4] .data.init_task   PROGBITS	  ffffffff80102000 102000 000001 00  WA
 0   0	1
-  [ 5] .shstrtab	  STRTAB	  0000000000000000 400001 000055 00    
 0   0	1
-  [ 6] .symtab 	  SYMTAB	  0000000000000000 400258 000078 18    
 7   5	8
-  [ 7] .strtab 	  STRTAB	  0000000000000000 4002d0 000001 00    
 0   0	1
+  [ 1] .text		  PROGBITS	  ffffffff80100000 100000 000001 00  AX
 0   0	4
+  [ 2] .data.cacheline_a PROGBITS	  ffffffff80100040 100040 000001 00  WA
 0   0	1
+  [ 3] .vsyscall_0	  PROGBITS	  ffffffffff600000 200000 000001 00  AX
 0   0	1
+  [ 4] .data.init_task   PROGBITS	  ffffffff80102000 302000 000001 00  WA
 0   0	1
+  [ 5] .shstrtab	  STRTAB	  0000000000000000 302001 000055 00    
 0   0	1
+  [ 6] .symtab 	  SYMTAB	  0000000000000000 302258 000078 18    
 7   5	8
+  [ 7] .strtab 	  STRTAB	  0000000000000000 3022d0 000001 00    
 0   0	1
 Key to Flags:
   W (write), A (alloc), X (execute), M (merge), S (strings)
   I (info), L (link order), G (group), x (unknown)
@@ -21,12 +21,12 @@ There are 3 program headers, starting at

 Program Headers:
   Type 	  Offset   VirtAddr	      PhysAddr		 FileSiz 
MemSiz	 Flg Align
-  LOAD 	  0x000000 0xffffffff80000000 0xffffffff00b01000 0x102001
0x102001 RW  0x200000
-  LOAD 	  0x300000 0xffffffff80100000 0xffffffff80100000 0x000041
0x000041 RWE 0x200000
-  LOAD 	  0x400000 0xffffffffff600000 0xffffffff80101000 0x000001
0x000001 R E 0x200000
+  LOAD 	  0x000000 0xffffffff80000000 0xffffffff80000000 0x100041
0x100041 RWE 0x200000
+  LOAD 	  0x200000 0xffffffffff600000 0xffffffff80101000 0x000001
0x000001 R E 0x200000
+  LOAD 	  0x302000 0xffffffff80102000 0xffffffff80102000 0x000001
0x000001 RW  0x200000

  Section to Segment mapping:
   Segment Sections...
-   00	   .text .data.cacheline_aligned .data.init_task
-   01	   .text .data.cacheline_aligned
-   02	   .vsyscall_0
+   00	   .text .data.cacheline_aligned
+   01	   .vsyscall_0
+   02	   .data.init_task
make: *** [all] Error 1
[hjl@gnu-16 lma]$
Comment 5 Alan Modra 2006-08-16 07:58:26 UTC
According to the ld docs, this testcase script (and the x86_64 kernel script) is
invalid because dot is moved backwards.  Nevertheless, I'll see if I can make ld
do something reasonable.
Comment 6 Alan Modra 2006-11-14 03:13:22 UTC
Fixed