ELF32: don't silently truncate relocation addends
authorJan Beulich <jbeulich@suse.com>
Wed, 23 Mar 2022 07:43:13 +0000 (08:43 +0100)
committerJan Beulich <jbeulich@suse.com>
Wed, 23 Mar 2022 07:43:13 +0000 (08:43 +0100)
At least x86-64's x32 sub-mode and RISC-V's 32-bit mode calculate
addends as 64-bit values, but store them in signed 32-bit fields when
generating the file without encountering any earlier error. When the
relocated field is a 64-bit one, the value resulting after processing
the relocation record when linking (or the latest when loading) may
thus be wrong due to the truncation.

With the code change in place, one x32 testcase actually triggers the
new diagnostic. That one case of too large a (negative) addend is being
adjusted alongside the addition of a new testcase to actually trigger
the new error. (Note that due to internal BFD behavior the relocation in
.data doesn't get processed anymore after the errors in .text.)

Note that in principle it is possible to express 64-bit relocations in
ELF32, but this would require .rel relocations, i.e. with the addend
stored in the 64-bit field being relocated. But I guess it would be a
lot of effort for little gain to actually support this.

bfd/elfcode.h
gas/testsuite/gas/i386/ilp32/ilp32.exp
gas/testsuite/gas/i386/ilp32/reloc-2.l [new file with mode: 0644]
gas/testsuite/gas/i386/ilp32/reloc-2.s [new file with mode: 0644]
gas/testsuite/gas/i386/ilp32/reloc64.d
gas/testsuite/gas/i386/ilp32/reloc64.l
gas/testsuite/gas/i386/ilp32/reloc64.s

index c3ab0536321eb58c9c306fbe73451be10ff3a380..4d4cb68164ab7728e6050ccd060b8cb93ddb576d 100644 (file)
@@ -997,6 +997,19 @@ elf_write_relocs (bfd *abfd, asection *sec, void *data)
          return;
        }
 
+#if defined(BFD64) && ARCH_SIZE == 32
+      if (rela_hdr->sh_type == SHT_RELA
+         && ptr->howto->bitsize > 32
+         && ptr->addend - INT32_MIN > UINT32_MAX)
+       {
+         _bfd_error_handler (_("%pB: %pA+%"BFD_VMA_FMT"x: "
+                               "relocation addend %"BFD_VMA_FMT"x too large"),
+                             abfd, sec, ptr->address, ptr->addend);
+         *failedp = true;
+         bfd_set_error (bfd_error_bad_value);
+       }
+#endif
+
       src_rela.r_offset = ptr->address + addr_offset;
       src_rela.r_info = ELF_R_INFO (n, ptr->howto->type);
       src_rela.r_addend = ptr->addend;
index d1bb96cc1a16f19ec6edcf858d4079aa1d2c13ce..c2ff5e3e919d2ab177c5558497d2abf0814f99eb 100644 (file)
@@ -38,6 +38,7 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_x32_check] &
     }
 
     run_list_test "reloc64" "--defsym _bad_=1"
+    run_list_test "reloc-2"
 
     set ASFLAGS "$old_ASFLAGS"
 }
diff --git a/gas/testsuite/gas/i386/ilp32/reloc-2.l b/gas/testsuite/gas/i386/ilp32/reloc-2.l
new file mode 100644 (file)
index 0000000..9e89145
--- /dev/null
@@ -0,0 +1,4 @@
+.*: \.text\+2:.*addend.*too large.*
+.*: \.text\+b:.*addend.*too large.*
+.*: Assembler messages:
+.*: Fatal error: .*
diff --git a/gas/testsuite/gas/i386/ilp32/reloc-2.s b/gas/testsuite/gas/i386/ilp32/reloc-2.s
new file mode 100644 (file)
index 0000000..f9035a8
--- /dev/null
@@ -0,0 +1,7 @@
+       .text
+_start:
+       movabs  $x+0x123456789, %rax
+       movabs  x+0x123456789, %eax
+
+       .data
+       .quad x+0x123456789
index 78ca3fd9e38037cc0da11c6087db7cd2356f61c8..e2c461f24e8394142217636eca050c7e454047fc 100644 (file)
@@ -61,7 +61,8 @@ Disassembly of section \.text:
 .*[    ]+R_X86_64_TPOFF32[     ]+xtrn
 .*[    ]+R_X86_64_TPOFF32[     ]+xtrn
 .*[    ]+R_X86_64_TPOFF32[     ]+xtrn
-.*[    ]+R_X86_64_64[  ]+xtrn\+0x1
+.*[    ]+R_X86_64_64[  ]+xtrn\+0x7fffffff
+.*[    ]+R_X86_64_64[  ]+xtrn\-0x80000000
 Disassembly of section \.data:
 #...
 .*[    ]+R_X86_64_32[  ]+xtrn
index 9643e677782fd04745009e8dec63c8fba9c85beb..aad4c583c7b9339e14be4e916f9fa14671150c45 100644 (file)
 .*:175: Error: .*
 .*:176: Error: .*
 .*:177: Error: .*
-.*:189: Error: .*
-.*:192: Error: .* too large for field of 4 bytes at .*
+.*:190: Error: .*
 .*:193: Error: .* too large for field of 4 bytes at .*
 .*:194: Error: .* too large for field of 4 bytes at .*
 .*:195: Error: .* too large for field of 4 bytes at .*
-.*:196: Error: .* too large for field of 2 bytes at .*
-.*:196: Error: .* too large for field of 1 byte at .*
+.*:196: Error: .* too large for field of 4 bytes at .*
 .*:197: Error: .* too large for field of 2 bytes at .*
 .*:197: Error: .* too large for field of 1 byte at .*
-.*:200: Error: .* too large for field of 4 bytes at .*
-.*:201: Error: .* too large for field of 2 bytes at .*
+.*:198: Error: .* too large for field of 2 bytes at .*
+.*:198: Error: .* too large for field of 1 byte at .*
+.*:201: Error: .* too large for field of 4 bytes at .*
 .*:202: Error: .* too large for field of 2 bytes at .*
-.*:203: Error: .* too large for field of 1 byte at .*
+.*:203: Error: .* too large for field of 2 bytes at .*
 .*:204: Error: .* too large for field of 1 byte at .*
+.*:205: Error: .* too large for field of 1 byte at .*
index 9f5990a300a57b5d1a65eadc5c3ef34c1336dce9..db2d2acdf5d7defef9720c0615a51b72cb0960dd 100644 (file)
@@ -178,7 +178,8 @@ bad .byte   xtrn@tpoff
 
        .text
        mov     xtrn@tpoff (%rbx), %eax
-       movabsq $xtrn - 4294967295, %rbp
+       movabsq $xtrn + 0x7fffffff, %rbx
+       movabsq $xtrn - 0x80000000, %rbp
 
        .data
        .quad   xtrn