Repository: davidlattimore/wild (rust)

Issue:
SIGSEGV (Address boundary error) in openh264 tests  with wild
In openh264 checkout (I'm on a bit old 042473ea commit) run:
```
❯ PATH=~/Projects/wild/:$PATH meson setup builddir

❯ PATH=~/Projects/wild/:$PATH meson test -C builddir
...
1/5 api               FAIL             0.06s   killed by signal 11 SIGSEGV
```
With ld.bfd it works fine:
```
❯ meson test
ninja: Entering directory `/home/mateusz/Projects/openh264/builddir'
[173/173] Linking target test/api/test_api
1/5 processing        OK               0.13s
2/5 decoder           OK               1.75s
3/5 encoder           OK               3.88s
4/5 common            OK               4.14s
5/5 api               OK              51.77s

Ok:                 5
Expected Fail:      0
Fail:               0
Unexpected Pass:    0
Skipped:            0
Timeout:            0
```

Attached the reproducer created with wild:
[crash.tgz](https://github.com/user-attachments/files/19347934/crash.tgz)

Running linker-diff doesn't seem to find the cause:
```
❯ WILD_REFERENCE_LINKER=ld ./run-with ~/Projects/wild/target/release/wild
WARNING: wild: --plugin /usr/lib/gcc/x86_64-pc-linux-gnu/14.2.1/liblto_plugin.so is not yet supported
wild: ./bin
ref: ./bin.ref-linker
.dynamic.DT_FLAGS_1.NOW
  wild 1
  ref

rel.extra-copy-relocation.R_X86_64_REX_GOTPCRELX
  `./libcommon.a.p/src_welsCodecTrace.cpp.o` .text _ZL15welsStderrTracePviPKc
  ORIG 0x000000: [ 48 8b 05 00 00 00 00 ] mov 7,%rax
                            ^^^^^^^^^^^ R_X86_64_REX_GOTPCRELX
       stderr -4
  wild 0x144920: [ 48 8b 05 49 d9 02 00 ] mov 0x172270,%rax
                            ^^^^^^^^^^^ R_X86_64_REX_GOTPCRELX NoOp
       GOT->COPY(stderr)
  wild TRACE: relocation applied value_flags=DYNAMIC,
  wild TRACE: resolution_flags=DIRECT | GOT | COPY_RELOCATION, rel_kind=GotRelative,
  wild TRACE: value=0x2d949, symbol_name=stderr
  ref  0x119a10: [ 48 8b 05 31 33 05 00 ] mov 0x16CD48,%rax
                            ^^^^^^^^^^^ R_X86_64_REX_GOTPCRELX NoOp
       GOT->R_X86_64_GLOB_DAT->stderr@GLIBC_2.2.5


Error: linker-diff reported errors. To rerun, execute:
/home/mateusz/Projects/wild/target/release/linker-diff --wild-defaults --display-names wild,ref --ref ./bin.ref-linker ./bin

Stack backtrace:
   0: anyhow::error::<impl anyhow::Error>::msg
   1: libwild::diff::maybe_diff
   2: libwild::Linker::run
   3: libwild::subprocess::run_in_subprocess
   4: wild::main
   5: std::sys::backtrace::__rust_begin_short_backtrace
   6: std::rt::lang_start::{{closure}}
   7: std::rt::lang_start_internal
   8: main
   9: <unknown>
  10: __libc_start_main
  11: _start
```
Backtrace:
```
Program received signal SIGSEGV, Segmentation fault.
std::basic_ostream<char, std::char_traits<char> >::sentry::sentry (this=0x7fffffffdf20, __os=warning: can't find linker symbol for virtual table for `std::basic_ostream<char, std::char_traits<char> >' value
warning:   found `VTT for std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >' instead
...) at /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ostream.tcc:51
warning: 51	/usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ostream.tcc: Nie ma takiego pliku ani katalogu
(gdb) bt
#0  std::basic_ostream<char, std::char_traits<char> >::sentry::sentry (this=0x7fffffffdf20, __os=warning: can't find linker symbol for virtual table for `std::basic_ostream<char, std::char_traits<char> >' value
warning:   found `VTT for std::__cxx11::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >' instead
...) at /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ostream.tcc:51
#1  0x00007ffff7d72df2 in std::basic_ostream<char, std::char_traits<char> >::_M_insert<unsigned long> (this=0x7fffffffe1e0, __v=32) at /usr/src/debug/gcc/gcc-build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/ostream.tcc:66
#2  0x00007ffff7f371f0 in testing::internal::PrintBytesInObjectTo(unsigned char const*, unsigned long, std::basic_ostream<char, std::char_traits<char> >*) () from /usr/lib/libgtest.so.1.16.0
#3  0x000055555564d3ae in testing::internal::RawBytesPrinter::PrintValue<EncodeDecodeFileParamBase, 32ul> (value=..., os=0x7fffffffe1e0) at /usr/include/gtest/gtest-printers.h:315
#4  testing::internal::PrintWithFallback<EncodeDecodeFileParamBase> (value=..., os=0x7fffffffe1e0) at /usr/include/gtest/gtest-printers.h:362
#5  testing::internal::PrintTo<EncodeDecodeFileParamBase> (value=..., os=0x7fffffffe1e0) at /usr/include/gtest/gtest-printers.h:491
#6  testing::internal::UniversalPrinter<EncodeDecodeFileParamBase>::Print (value=..., os=0x7fffffffe1e0) at /usr/include/gtest/gtest-printers.h:883
#7  testing::internal::UniversalPrint<EncodeDecodeFileParamBase> (value=..., os=0x7fffffffe1e0) at /usr/include/gtest/gtest-printers.h:1187
#8  testing::internal::UniversalTersePrinter<EncodeDecodeFileParamBase>::Print (value=..., os=0x7fffffffe1e0) at /usr/include/gtest/gtest-printers.h:1065
#9  testing::PrintToString<EncodeDecodeFileParamBase> (value=...) at /usr/include/gtest/gtest-printers.h:1225
#10 testing::internal::ParameterizedTestSuiteInfo<EncodeDecodeTestAPI>::RegisterTests (this=<optimized out>) at /usr/include/gtest/internal/gtest-param-util.h:603
#11 0x00007ffff7f54d2d in testing::internal::UnitTestImpl::RegisterParameterizedTests() () from /usr/lib/libgtest.so.1.16.0
#12 0x00007ffff7f656be in testing::internal::UnitTestImpl::PostFlagParsingInit() () from /usr/lib/libgtest.so.1.16.0
#13 0x0000555555697c47 in main (argc=<optimized out>, argv=0x7fffffffe518) at ../test/api/simple_test.cpp:19
```

Pull request:
Only use GLOB_DAT for GOT entries
Fixes #576

Patch:
diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs
index 1f0c8c7..9da33c0 100644
--- a/libwild/src/elf_writer.rs
+++ b/libwild/src/elf_writer.rs
@@ -911,7 +911,12 @@ impl<'data, 'layout, 'out> TableWriter<'data, 'layout, 'out> {
                 "Tried to write glob-dat with no allocation. {}",
                 ResFlagsDisplay(res)
             );
-            self.write_dynamic_symbol_relocation::<A>(got_address, 0, res.dynamic_symbol_index()?)?;
+            self.write_dynamic_symbol_relocation::<A>(
+                got_address,
+                0,
+                res.dynamic_symbol_index()?,
+                DynamicRelocationKind::GotEntry,
+            )?;
         } else if res.value_flags.contains(ValueFlags::IFUNC) {
             self.write_ifunc_relocation::<A>(res)?;
         } else {
@@ -1174,6 +1179,7 @@ impl<'data, 'layout, 'out> TableWriter<'data, 'layout, 'out> {
         place: u64,
         addend: i64,
         symbol_index: u32,
+        kind: DynamicRelocationKind,
     ) -> Result {
         let _span = tracing::trace_span!("write_dynamic_symbol_relocation").entered();
         debug_assert_bail!(
@@ -1188,7 +1194,7 @@ impl<'data, 'layout, 'out> TableWriter<'data, 'layout, 'out> {
             LittleEndian,
             false,
             symbol_index,
-            A::get_dynamic_relocation_type(DynamicRelocationKind::DynamicSymbol),
+            A::get_dynamic_relocation_type(kind),
         );
         Ok(())
     }
@@ -2239,6 +2245,7 @@ fn write_absolute_relocation<A: Arch>(
             place,
             addend,
             resolution.dynamic_symbol_index()?,
+            DynamicRelocationKind::Absolute,
         )?;
         Ok(0)
     } else if table_writer.output_kind.is_relocatable() && !resolution.is_absolute() {
diff --git a/linker-diff/src/asm_diff.rs b/linker-diff/src/asm_diff.rs
index 373626a..34b8903 100644
--- a/linker-diff/src/asm_diff.rs
+++ b/linker-diff/src/asm_diff.rs
@@ -1525,7 +1525,7 @@ impl<R: RType> DynamicRelocation<'_, R> {
         // we emit GLOB_DAT relocations. So we normalise to GLOB_DAT so as to treat them as
         // equivalent.
         if self.r_type.dynamic_relocation_kind() == Some(DynamicRelocationKind::JumpSlot) {
-            out.r_type = R::from_dynamic_relocation_kind(DynamicRelocationKind::DynamicSymbol);
+            out.r_type = R::from_dynamic_relocation_kind(DynamicRelocationKind::GotEntry);
         }
 
         out
diff --git a/linker-utils/src/elf.rs b/linker-utils/src/elf.rs
index 276bfa6..49d0314 100644
--- a/linker-utils/src/elf.rs
+++ b/linker-utils/src/elf.rs
@@ -582,7 +582,8 @@ pub enum DynamicRelocationKind {
     TlsDesc,
     TpOff,
     Relative,
-    DynamicSymbol,
+    Absolute,
+    GotEntry,
     JumpSlot,
 }
 
@@ -596,7 +597,8 @@ impl DynamicRelocationKind {
             object::elf::R_X86_64_DTPOFF64 => DynamicRelocationKind::DtpOff,
             object::elf::R_X86_64_TPOFF64 => DynamicRelocationKind::TpOff,
             object::elf::R_X86_64_RELATIVE => DynamicRelocationKind::Relative,
-            object::elf::R_X86_64_GLOB_DAT => DynamicRelocationKind::DynamicSymbol,
+            object::elf::R_X86_64_GLOB_DAT => DynamicRelocationKind::GotEntry,
+            object::elf::R_X86_64_64 => DynamicRelocationKind::Absolute,
             object::elf::R_X86_64_TLSDESC => DynamicRelocationKind::TlsDesc,
             object::elf::R_X86_64_JUMP_SLOT => DynamicRelocationKind::JumpSlot,
             _ => return None,
@@ -614,7 +616,8 @@ impl DynamicRelocationKind {
             DynamicRelocationKind::DtpOff => object::elf::R_X86_64_DTPOFF64,
             DynamicRelocationKind::TpOff => object::elf::R_X86_64_TPOFF64,
             DynamicRelocationKind::Relative => object::elf::R_X86_64_RELATIVE,
-            DynamicRelocationKind::DynamicSymbol => object::elf::R_X86_64_GLOB_DAT,
+            DynamicRelocationKind::Absolute => object::elf::R_X86_64_64,
+            DynamicRelocationKind::GotEntry => object::elf::R_X86_64_GLOB_DAT,
             DynamicRelocationKind::TlsDesc => object::elf::R_X86_64_TLSDESC,
             DynamicRelocationKind::JumpSlot => object::elf::R_X86_64_JUMP_SLOT,
         }
@@ -629,7 +632,8 @@ impl DynamicRelocationKind {
             object::elf::R_AARCH64_TLS_DTPREL => DynamicRelocationKind::DtpOff,
             object::elf::R_AARCH64_TLS_TPREL => DynamicRelocationKind::TpOff,
             object::elf::R_AARCH64_RELATIVE => DynamicRelocationKind::Relative,
-            object::elf::R_AARCH64_GLOB_DAT => DynamicRelocationKind::DynamicSymbol,
+            object::elf::R_AARCH64_ABS64 => DynamicRelocationKind::Absolute,
+            object::elf::R_AARCH64_GLOB_DAT => DynamicRelocationKind::GotEntry,
             object::elf::R_AARCH64_TLSDESC => DynamicRelocationKind::TlsDesc,
             object::elf::R_AARCH64_JUMP_SLOT => DynamicRelocationKind::JumpSlot,
             _ => return None,
@@ -647,7 +651,8 @@ impl DynamicRelocationKind {
             DynamicRelocationKind::DtpOff => object::elf::R_AARCH64_TLS_DTPREL,
             DynamicRelocationKind::TpOff => object::elf::R_AARCH64_TLS_TPREL,
             DynamicRelocationKind::Relative => object::elf::R_AARCH64_RELATIVE,
-            DynamicRelocationKind::DynamicSymbol => object::elf::R_AARCH64_GLOB_DAT,
+            DynamicRelocationKind::Absolute => object::elf::R_AARCH64_ABS64,
+            DynamicRelocationKind::GotEntry => object::elf::R_AARCH64_GLOB_DAT,
             DynamicRelocationKind::TlsDesc => object::elf::R_AARCH64_TLSDESC,
             DynamicRelocationKind::JumpSlot => object::elf::R_AARCH64_JUMP_SLOT,
         }


Test patch:
diff --git a/wild/tests/integration_tests.rs b/wild/tests/integration_tests.rs
index 3490aa9..70ea016 100644
--- a/wild/tests/integration_tests.rs
+++ b/wild/tests/integration_tests.rs
@@ -2123,6 +2123,7 @@ fn integration_test(
         "link_args.c",
         "global_definitions.c",
         "data.c",
+        "data-pointers.c",
         "weak-vars.c",
         "weak-vars-archive.c",
         "weak-fns.c",
diff --git a/wild/tests/sources/data-pointers-2.c b/wild/tests/sources/data-pointers-2.c
new file mode 100644
index 0000000..8b8a2d7
--- /dev/null
+++ b/wild/tests/sources/data-pointers-2.c
@@ -0,0 +1,14 @@
+int foo[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+int bar[8] = {0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70};
+
+int check_pointers(int **p) {
+    if (*p[0] != 2) {
+        return *p[0];
+    }
+
+    if (*p[1] != 0x60) {
+        return *p[1];
+    }
+    
+    return 42;
+}
diff --git a/wild/tests/sources/data-pointers.c b/wild/tests/sources/data-pointers.c
new file mode 100644
index 0000000..518e61b
--- /dev/null
+++ b/wild/tests/sources/data-pointers.c
@@ -0,0 +1,26 @@
+//#Shared:exit.c
+//#EnableLinker:lld
+//#Static:false
+//#LinkArgs:-z now
+//#Shared:data-pointers-2.c
+//#EnableLinker:lld
+// We're linking different .so files, so this is expected.
+//#DiffIgnore:.dynamic.DT_NEEDED
+// GNU ld emits a .got section for the shared object, despite it not being necessary.
+//#DiffIgnore:section.got
+
+#include "exit.h"
+
+extern int foo[8];
+extern int bar[8];
+
+// Since `foo` and `bar` come from a shared object, this should result in a couple of runtime
+// relocations in our data section. We have non-zero offsets relative to these symbols in order to
+// make sure addends work as expected.
+int *pointers[2] = {&foo[2], &bar[6]};
+
+int check_pointers(int **p);
+
+void _start(void) {
+    exit_syscall(check_pointers(pointers));
+}