Repository: davidlattimore/wild (rust)

Issue:
tlsdesc relaxations applied incorrectly in TLSDESC_CALL isn't immediately after
As reported in #832, the tlsdesc test fails when run on nixos.

The problem seems to be our handling of the following TLSDESC relocation.

```asm
  2e:	48 8d 05 00 00 00 00 	lea    0x0(%rip),%rax        # 35 <get_value+0x35>
			31: R_X86_64_GOTPC32_TLSDESC	g6-0x4
  35:	64 03 11             	add    %fs:(%rcx),%edx
  38:	ff 10                	call   *(%rax)
			38: R_X86_64_TLSDESC_CALL	g6
```

There's an extra instruction before the subsequent TLSDESC_CALL instruction, but we're currently assuming that lea and call instructions are adjacent.

I was able to reproduce the problem outside of nixos with the flags `-mtls-dialect=gnu2 -fPIC -O2`. The `-fPIC` was necessary to get the compiler to use TLSDESC at all, otherwise it decided it was unnecessary. The `-O2` was necessary for instruction reordering to insert the interleaving instruction.


Pull request:
Relax TLSDESC and TLSDESC_CALL separately
These two relocations don't have to be adjacent.

To handle this, we now relax these two relocations separately. We also now assume that the instruction that the TLSDESC relocation is a REX-prefixed LEA instruction.

Fix #842

Patch:
diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs
index 446aece..aafd3c5 100644
--- a/libwild/src/elf_writer.rs
+++ b/libwild/src/elf_writer.rs
@@ -2475,6 +2475,16 @@ fn apply_relocation<A: Arch>(
                 symbol_name = layout.symbol_db.symbol_name_for_display(local_symbol_id),
             )
         });
+        tracing::trace!(
+            %value_flags,
+            %resolution_flags,
+            relaxation_kind = ?relaxation.debug_kind(),
+            ?rel_info.kind,
+            %rel_info.size,
+            value,
+            value_hex = %HexU64::new(value),
+            symbol_name = %layout.symbol_db.symbol_name_for_display(local_symbol_id),
+            "relaxation applied");
     } else {
         trace.emit(original_place, || {
             format!(
@@ -3629,8 +3639,8 @@ struct DynamicEntriesWriter<'out> {
     out: &'out mut [DynamicEntry],
 }
 
-impl DynamicEntriesWriter<'_> {
-    fn new(buffer: &mut [u8]) -> DynamicEntriesWriter {
+impl<'out> DynamicEntriesWriter<'out> {
+    fn new(buffer: &'out mut [u8]) -> DynamicEntriesWriter<'out> {
         DynamicEntriesWriter {
             out: slice_from_all_bytes_mut(buffer),
         }
diff --git a/libwild/src/layout.rs b/libwild/src/layout.rs
index 73e840f..09529a6 100644
--- a/libwild/src/layout.rs
+++ b/libwild/src/layout.rs
@@ -1138,9 +1138,9 @@ impl CommonGroupState<'_> {
     }
 }
 
-fn create_global_address_emitter(
-    symbol_resolution_flags: &[ResolutionFlags],
-) -> GlobalAddressEmitter {
+fn create_global_address_emitter<'state>(
+    symbol_resolution_flags: &'state [ResolutionFlags],
+) -> GlobalAddressEmitter<'state> {
     GlobalAddressEmitter {
         symbol_resolution_flags,
     }
@@ -1502,7 +1502,7 @@ impl WorkItem {
 }
 
 impl<'data> Layout<'data> {
-    pub(crate) fn prelude(&self) -> &PreludeLayout {
+    pub(crate) fn prelude(&self) -> &PreludeLayout<'data> {
         let Some(FileLayout::Prelude(i)) = self.group_layouts.first().and_then(|g| g.files.first())
         else {
             panic!("Prelude layout not found at expected offset");
@@ -1652,7 +1652,7 @@ impl<'data> Layout<'data> {
         self.symbol_resolution_flags[symbol_id.as_usize()]
     }
 
-    pub(crate) fn file_layout(&self, file_id: FileId) -> &FileLayout {
+    pub(crate) fn file_layout(&self, file_id: FileId) -> &FileLayout<'data> {
         let group_layout = &self.group_layouts[file_id.group()];
         &group_layout.files[file_id.file()]
     }
@@ -1672,7 +1672,7 @@ impl<'data> Layout<'data> {
             > 0
     }
 
-    pub(crate) fn info_inputs(&self) -> InfoInputs {
+    pub(crate) fn info_inputs<'layout>(&'layout self) -> InfoInputs<'layout> {
         InfoInputs {
             section_part_layouts: &self.section_part_layouts,
             non_addressable_counts: &self.non_addressable_counts,
diff --git a/libwild/src/linker_script.rs b/libwild/src/linker_script.rs
index 41e3f9f..5bbc329 100644
--- a/libwild/src/linker_script.rs
+++ b/libwild/src/linker_script.rs
@@ -397,7 +397,7 @@ mod tests {
     use crate::args::InputSpec;
     use itertools::assert_equal;
 
-    fn parse_script(text: &str) -> Result<LinkerScript> {
+    fn parse_script<'a>(text: &'a str) -> Result<LinkerScript<'a>> {
         LinkerScript::parse(text.as_bytes(), Path::new("test-linker-script.txt"))
     }
 
diff --git a/libwild/src/output_section_id.rs b/libwild/src/output_section_id.rs
index d8fa4c4..47db780 100644
--- a/libwild/src/output_section_id.rs
+++ b/libwild/src/output_section_id.rs
@@ -277,11 +277,11 @@ struct CustomSectionIds {
     nonalloc: Vec<OutputSectionId>,
 }
 
-impl OutputSections<'_> {
+impl<'data> OutputSections<'data> {
     /// Returns an iterator that emits all section IDs and their info.
     pub(crate) fn ids_with_info(
         &self,
-    ) -> impl Iterator<Item = (OutputSectionId, &SectionOutputInfo)> {
+    ) -> impl Iterator<Item = (OutputSectionId, &SectionOutputInfo<'data>)> {
         self.section_infos.iter()
     }
 
diff --git a/libwild/src/program_segments.rs b/libwild/src/program_segments.rs
index 512ebff..21797ec 100644
--- a/libwild/src/program_segments.rs
+++ b/libwild/src/program_segments.rs
@@ -154,7 +154,10 @@ impl ProgramSegmentId {
         )
     }
 
-    pub(crate) fn display(self, program_segments: &ProgramSegments) -> ProgramSegmentDisplay {
+    pub(crate) fn display<'a>(
+        self,
+        program_segments: &'a ProgramSegments,
+    ) -> ProgramSegmentDisplay<'a> {
         ProgramSegmentDisplay {
             id: self,
             program_segments,
diff --git a/libwild/src/symbol_db.rs b/libwild/src/symbol_db.rs
index 7d4f873..25fc222 100644
--- a/libwild/src/symbol_db.rs
+++ b/libwild/src/symbol_db.rs
@@ -543,7 +543,7 @@ impl<'data> SymbolDb<'data> {
         self.symbol_definitions[symbol_id.as_usize()] = new_definition;
     }
 
-    pub(crate) fn file(&self, file_id: FileId) -> ParsedInput {
+    pub(crate) fn file<'db>(&'db self, file_id: FileId) -> ParsedInput<'db> {
         match &self.groups[file_id.group()] {
             Group::Prelude(prelude) => ParsedInput::Prelude(prelude),
             Group::Objects(parsed_input_objects) => {
@@ -593,7 +593,7 @@ impl<'data> SymbolDb<'data> {
 
     pub(crate) fn all_unversioned_symbols(
         &self,
-    ) -> impl Iterator<Item = (&PreHashed<UnversionedSymbolName>, &SymbolId)> {
+    ) -> impl Iterator<Item = (&PreHashed<UnversionedSymbolName<'data>>, &SymbolId)> {
         self.buckets.iter().flat_map(|b| b.name_to_id.iter())
     }
 
diff --git a/libwild/src/version_script.rs b/libwild/src/version_script.rs
index 5f9c25f..90ebf43 100644
--- a/libwild/src/version_script.rs
+++ b/libwild/src/version_script.rs
@@ -187,7 +187,7 @@ impl<'data> VersionScript<'data> {
             .count() as u16
     }
 
-    pub(crate) fn version_iter(&self) -> impl Iterator<Item = &Version> {
+    pub(crate) fn version_iter(&self) -> impl Iterator<Item = &Version<'data>> {
         self.versions.iter()
     }
 
diff --git a/libwild/src/x86_64.rs b/libwild/src/x86_64.rs
index d4ddbaf..fe817f8 100644
--- a/libwild/src/x86_64.rs
+++ b/libwild/src/x86_64.rs
@@ -308,25 +308,33 @@ impl crate::arch::Relaxation for Relaxation {
                     }
                 }
             }
+            // TODO: It looks to me like we should be able to optimise to local-exec for any
+            // executable, not just static executables. However, currently the test fails if we try
+            // this. Investigate why.
             object::elf::R_X86_64_GOTPC32_TLSDESC
                 if !interposable && output_kind.is_static_executable() =>
             {
-                // lea    0x0(%rip),%rax
-                if section_bytes.get(offset - 3..offset)? == [0x48, 0x8d, 0x05] {
-                    return create(
-                        RelaxationKind::TlsDescToLocalExec,
-                        object::elf::R_X86_64_TPOFF32,
-                    );
-                }
+                // We assume that the instruction that this relocation applies to is a REX-prefixed
+                // LEA instruction.
+                return create(
+                    RelaxationKind::TlsDescToLocalExec,
+                    object::elf::R_X86_64_TPOFF32,
+                );
             }
+            // Note, the conditions on this relaxation (is_executable) must match those on
+            // TLSDESC_CALL below.
             object::elf::R_X86_64_GOTPC32_TLSDESC if output_kind.is_executable() => {
-                // lea    0x0(%rip),%rax
-                if section_bytes.get(offset - 3..offset)? == [0x48, 0x8d, 0x05] {
-                    return create(
-                        RelaxationKind::TlsDescToInitialExec,
-                        object::elf::R_X86_64_GOTTPOFF,
-                    );
-                }
+                // We assume that the instruction that this relocation applies to is a REX-prefixed
+                // LEA instruction.
+                return create(
+                    RelaxationKind::TlsDescToInitialExec,
+                    object::elf::R_X86_64_GOTTPOFF,
+                );
+            }
+            // Note, the conditions on this relaxation (is_executable) must match those on
+            // GOTPC32_TLSDESC above.
+            object::elf::R_X86_64_TLSDESC_CALL if output_kind.is_executable() => {
+                return create(RelaxationKind::SkipTlsDescCall, object::elf::R_X86_64_NONE);
             }
             _ => return None,
         };
diff --git a/linker-diff/src/aarch64.rs b/linker-diff/src/aarch64.rs
index 7c072a2..686cfe9 100644
--- a/linker-diff/src/aarch64.rs
+++ b/linker-diff/src/aarch64.rs
@@ -206,12 +206,12 @@ impl Arch for AArch64 {
         String::new()
     }
 
-    fn decode_instructions_in_range(
-        section_bytes: &[u8],
+    fn decode_instructions_in_range<'data>(
+        section_bytes: &'data [u8],
         section_address: u64,
         _function_offset_in_section: u64,
         range: std::ops::Range<u64>,
-    ) -> Vec<crate::arch::Instruction<Self>> {
+    ) -> Vec<crate::arch::Instruction<'data, Self>> {
         let mut offset = range.start & !3;
 
         let mut instructions = Vec::new();
diff --git a/linker-diff/src/asm_diff.rs b/linker-diff/src/asm_diff.rs
index db871a5..064d697 100644
--- a/linker-diff/src/asm_diff.rs
+++ b/linker-diff/src/asm_diff.rs
@@ -394,7 +394,7 @@ impl<'data, A: Arch> RelaxationGroup<'data, A> {
         }
     }
 
-    fn matches_if_ok(&self) -> Option<Vec<RelaxationMatch<A>>> {
+    fn matches_if_ok(&self) -> Option<Vec<RelaxationMatch<'data, A>>> {
         self.match_results
             .iter()
             .map(|r| match r {
@@ -405,7 +405,7 @@ impl<'data, A: Arch> RelaxationGroup<'data, A> {
             .ok()
     }
 
-    fn matches_skipping_nops(&self) -> Vec<&RelaxationMatchResult<A>> {
+    fn matches_skipping_nops(&self) -> Vec<&RelaxationMatchResult<'data, A>> {
         self.match_results
             .iter()
             .filter(|result| !matches!(result, RelaxationMatchResult::Matched(m) if m.relaxation.relaxation_kind.is_replace_with_no_op()))
diff --git a/linker-diff/src/lib.rs b/linker-diff/src/lib.rs
index 323bb58..365b6ad 100644
--- a/linker-diff/src/lib.rs
+++ b/linker-diff/src/lib.rs
@@ -375,7 +375,11 @@ impl<'data> Binary<'data> {
 
     /// Looks up a symbol, first trying to get a global, or failing that a local. If multiple
     /// symbols have the same name, then `hint_address` is used to select which one to return.
-    pub(crate) fn symbol_by_name(&self, name: &[u8], hint_address: u64) -> NameLookupResult {
+    pub(crate) fn symbol_by_name<'file: 'data>(
+        &'file self,
+        name: &[u8],
+        hint_address: u64,
+    ) -> NameLookupResult<'data, 'file> {
         match self.lookup_symbol(&self.name_index.globals_by_name, name, hint_address) {
             NameLookupResult::Undefined => {
                 self.lookup_symbol(&self.name_index.locals_by_name, name, hint_address)
@@ -384,12 +388,12 @@ impl<'data> Binary<'data> {
         }
     }
 
-    fn lookup_symbol(
-        &self,
+    fn lookup_symbol<'file: 'data>(
+        &'file self,
         symbol_map: &HashMap<&[u8], Vec<object::SymbolIndex>>,
         name: &[u8],
         hint_address: u64,
-    ) -> NameLookupResult {
+    ) -> NameLookupResult<'data, 'file> {
         let indexes = symbol_map.get(name).map(Vec::as_slice).unwrap_or_default();
 
         if indexes.len() >= 2 {
@@ -416,16 +420,25 @@ impl<'data> Binary<'data> {
         }
     }
 
-    fn section_by_name(&self, name: &str) -> Option<ElfSection64<LittleEndian>> {
+    fn section_by_name<'file: 'data>(
+        &'file self,
+        name: &str,
+    ) -> Option<ElfSection64<'data, 'file, LittleEndian>> {
         self.section_by_name_bytes(name.as_bytes())
     }
 
-    fn section_by_name_bytes(&self, name: &[u8]) -> Option<ElfSection64<LittleEndian>> {
+    fn section_by_name_bytes<'file: 'data>(
+        &'file self,
+        name: &[u8],
+    ) -> Option<ElfSection64<'data, 'file, LittleEndian>> {
         let index = self.sections_by_name.get(name)?.index;
         self.elf_file.section_by_index(index).ok()
     }
 
-    fn section_containing_address(&self, address: u64) -> Option<ElfSection64<LittleEndian>> {
+    fn section_containing_address<'file: 'data>(
+        &'file self,
+        address: u64,
+    ) -> Option<ElfSection64<'file, 'data, LittleEndian>> {
         self.elf_file
             .sections()
             .find(|sec| (sec.address()..sec.address() + sec.size()).contains(&address))
diff --git a/linker-diff/src/section_map.rs b/linker-diff/src/section_map.rs
index 16523ab..8800b52 100644
--- a/linker-diff/src/section_map.rs
+++ b/linker-diff/src/section_map.rs
@@ -225,7 +225,10 @@ impl<'data> IndexedLayout<'data> {
         Ok(())
     }
 
-    pub(crate) fn get_section_info(&self, section_id: InputSectionId) -> Option<&SectionInfo> {
+    pub(crate) fn get_section_info(
+        &self,
+        section_id: InputSectionId,
+    ) -> Option<&SectionInfo<'data>> {
         self.files[section_id.file_index].sections[section_id.section_index.0].as_ref()
     }
 
@@ -242,7 +245,10 @@ impl<'data> IndexedLayout<'data> {
         &self.files[section_id.file_index]
     }
 
-    pub(crate) fn input_filename_for_section(&self, section_id: InputSectionId) -> &FileIdentifier {
+    pub(crate) fn input_filename_for_section(
+        &self,
+        section_id: InputSectionId,
+    ) -> &FileIdentifier<'data> {
         &self.input_file_for_section(section_id).identifier
     }
 
@@ -351,7 +357,7 @@ pub(crate) struct InputSectionId {
     pub(crate) section_index: object::SectionIndex,
 }
 
-impl SectionInfo<'_> {
+impl<'data> SectionInfo<'data> {
     pub(crate) fn index(&self) -> object::SectionIndex {
         self.section_id.section_index
     }
@@ -360,7 +366,7 @@ impl SectionInfo<'_> {
         &self,
         offset: u64,
         layout: &IndexedLayout,
-    ) -> Result<FunctionInfo> {
+    ) -> Result<FunctionInfo<'data>> {
         if self.functions.is_empty() {
             bail!(
                 "Cannot diff section with no functions: {}",
diff --git a/linker-diff/src/x86_64.rs b/linker-diff/src/x86_64.rs
index 1c17ffa..7fefd77 100644
--- a/linker-diff/src/x86_64.rs
+++ b/linker-diff/src/x86_64.rs
@@ -193,12 +193,12 @@ impl Arch for X86_64 {
         relaxation_kind.apply(section_bytes, offset_in_section, addend);
     }
 
-    fn decode_instructions_in_range(
-        section_bytes: &[u8],
+    fn decode_instructions_in_range<'data>(
+        section_bytes: &'data [u8],
         section_address: u64,
         function_offset_in_section: u64,
         range: std::ops::Range<u64>,
-    ) -> Vec<Instruction<Self>> {
+    ) -> Vec<Instruction<'data, Self>> {
         let mut instructions = Vec::new();
 
         let mut decoder = AsmDecoder::new(
diff --git a/linker-utils/src/x86_64.rs b/linker-utils/src/x86_64.rs
index 7ac0d68..e12d68f 100644
--- a/linker-utils/src/x86_64.rs
+++ b/linker-utils/src/x86_64.rs
@@ -159,17 +159,15 @@ impl RelaxationKind {
                 *offset_in_section += 15;
             }
             RelaxationKind::TlsDescToLocalExec => {
-                section_bytes[offset - 3..offset + 6].copy_from_slice(&[
+                section_bytes[offset - 3..offset + 4].copy_from_slice(&[
                     // mov {offset},%rax
-                    0x48, 0xc7, 0xc0, 0, 0, 0, 0, // xchg   %ax,%ax
-                    0x66, 0x90,
+                    0x48, 0xc7, 0xc0, 0, 0, 0, 0,
                 ]);
             }
             RelaxationKind::TlsDescToInitialExec => {
-                section_bytes[offset - 3..offset + 6].copy_from_slice(&[
+                section_bytes[offset - 3..offset + 4].copy_from_slice(&[
                     // mov {GOT}(%rip),%rax
-                    0x48, 0x8b, 0x05, 0, 0, 0, 0, // xchg   %ax,%ax
-                    0x66, 0x90,
+                    0x48, 0x8b, 0x05, 0, 0, 0, 0,
                 ]);
             }
             RelaxationKind::SkipTlsDescCall => {
@@ -190,9 +188,7 @@ impl RelaxationKind {
             | RelaxationKind::TlsGdToLocalExecLarge
             | RelaxationKind::TlsLdToLocalExec
             | RelaxationKind::TlsLdToLocalExecNoPlt
-            | RelaxationKind::TlsLdToLocalExec64
-            | RelaxationKind::TlsDescToLocalExec
-            | RelaxationKind::TlsDescToInitialExec => RelocationModifier::SkipNextRelocation,
+            | RelaxationKind::TlsLdToLocalExec64 => RelocationModifier::SkipNextRelocation,
             RelaxationKind::MovIndirectToLea
             | RelaxationKind::MovIndirectToAbsolute
             | RelaxationKind::RexMovIndirectToAbsolute
@@ -200,6 +196,8 @@ impl RelaxationKind {
             | RelaxationKind::RexCmpIndirectToAbsolute
             | RelaxationKind::CallIndirectToRelative
             | RelaxationKind::JmpIndirectToRelative
+            | RelaxationKind::TlsDescToLocalExec
+            | RelaxationKind::TlsDescToInitialExec
             | RelaxationKind::NoOp
             | RelaxationKind::SkipTlsDescCall => RelocationModifier::Normal,
         }


Test patch:
diff --git a/wild/tests/integration_tests.rs b/wild/tests/integration_tests.rs
index 7ac0466..fa09a38 100644
--- a/wild/tests/integration_tests.rs
+++ b/wild/tests/integration_tests.rs
@@ -2360,6 +2360,7 @@ fn integration_test(
         "basic-comdat.s",
         "input_does_not_exist.c",
         "ifunc2.c",
+        "ctors.c",
         "visibility-merging.c",
         "tls-local-exec.c",
         "undefined_symbols.c",
diff --git a/wild/tests/sources/ctors.c b/wild/tests/sources/ctors.c
new file mode 100644
index 0000000..e488a4d
--- /dev/null
+++ b/wild/tests/sources/ctors.c
@@ -0,0 +1,28 @@
+//#LinkerDriver:gcc
+//#DiffIgnore:section.data
+//#DiffIgnore:section.rodata
+
+static int ctors_init_val = 0;
+
+void init1() {
+    ctors_init_val += 10;
+}
+__attribute__ ((section (".ctors"), used)) static void* init1_ptr = init1;
+
+void init2() {
+    ctors_init_val += 30;
+}
+__attribute__ ((section (".ctors"), used)) static void* init2_ptr = init2;
+
+void init3() {
+    ctors_init_val += 2;
+}
+__attribute__ ((section (".ctors"), used)) static void* init3_ptr = init3;
+
+int main() {
+    if (ctors_init_val != 42) {
+        return 123;
+    }
+
+    return 42;
+}
diff --git a/wild/tests/sources/libc-integration.c b/wild/tests/sources/libc-integration.c
index ef10ea1..0eb26a2 100644
--- a/wild/tests/sources/libc-integration.c
+++ b/wild/tests/sources/libc-integration.c
@@ -79,6 +79,10 @@
 //#CompArgs:-g -no-pie -DDYNAMIC_DEP -DVERIFY_CTORS
 //#CompSoArgs:-g -fPIC -ftls-model=global-dynamic
 //#LinkerDriver:gcc
+// On nixos, with this configuration, GNU ld seems to only optimise TLSGD to initial-exec rather
+// than local exec. lld does however still optimise to local exec, so we enable it as an additional
+// reference point.
+//#EnableLinker:lld
 //#LinkArgs:-dynamic -no-pie -Wl,--strip-debug -Wl,--gc-sections -Wl,-z,now -L/does/not/exist
 
 //#Config:gcc-dynamic-pie-large:shared
@@ -115,8 +119,6 @@ extern int value42;
 
 const char* str1 = "This is str1";
 
-static int ctors_init_val = 0;
-
 void set_tvar_local(int v);
 int get_tvar_local(void);
 void set_tvar_local2(int v);
@@ -141,27 +143,6 @@ get_int_fn_t fn_pointers[] = {
 
 get_int_fn_t get_sometimes_weak_fn_ptr(void);
 
-// Initialisation with .ctors currently seems to fail with lld (tested with 18.1.8). For that
-// reason, we only enable this part of the test for selected variants that don't enable lld.
-#ifdef VERIFY_CTORS
-
-void init1() {
-    ctors_init_val += 10;
-}
-__attribute__ ((section (".ctors"), used)) static void* init1_ptr = init1;
-
-void init2() {
-    ctors_init_val += 30;
-}
-__attribute__ ((section (".ctors"), used)) static void* init2_ptr = init2;
-
-void init3() {
-    ctors_init_val += 2;
-}
-__attribute__ ((section (".ctors"), used)) static void* init3_ptr = init3;
-
-#endif
-
 // Make sure that we can refer to a non-default version of a symbol from libc.
 #if defined(__x86_64__)
 __asm__(".symver old_timer_gettime, timer_gettime@GLIBC_2.2.5");
@@ -298,11 +279,6 @@ int main() {
     if (strcmp(str1, "This is str1") != 0) {
         return 122;
     }
-#ifdef VERIFY_CTORS
-    if (ctors_init_val != 42) {
-        return 123;
-    }
-#endif
 
     if (weak_fn3() != 15) {
         return 124;
diff --git a/wild/tests/sources/tlsdesc.c b/wild/tests/sources/tlsdesc.c
index bdf93ed..483a9af 100644
--- a/wild/tests/sources/tlsdesc.c
+++ b/wild/tests/sources/tlsdesc.c
@@ -6,14 +6,14 @@
 //#DiffIgnore:section.rodata
 
 //#Config:gcc-tls-desc:default
-//#CompArgs:-mtls-dialect=gnu2
+//#CompArgs:-mtls-dialect=gnu2 -fPIC -O2
 //#LinkerDriver:gcc
 //#LinkArgs:-Wl,-z,now
 //#Object:tlsdesc-obj.c
 //#Arch: x86_64
 
 //#Config:gcc-tls-desc-aarch64:gcc-tls-desc
-//#CompArgs:-mtls-dialect=desc
+//#CompArgs:-mtls-dialect=desc -fPIC
 //#Arch: aarch64
 
 //#Config:gcc-tls-desc-pie:gcc-tls-desc
@@ -40,13 +40,13 @@
 //#Arch: aarch64
 
 //#Config:clang-tls-desc:gcc-tls-desc
-//#CompArgs:-mtls-dialect=gnu2
+//#CompArgs:-mtls-dialect=gnu2 -fPIC
 //#Compiler:clang
 //#RequiresClangWithTlsDesc:true
 //#Arch: x86_64
 
 //#Config:clang-tls-desc-aarch64:clang-tls-desc
-//#CompArgs:-mtls-dialect=desc
+//#CompArgs:-mtls-dialect=desc -fPIC
 //#Arch: aarch64
 
 //#Config:clang-tls-desc-shared:clang-tls-desc
@@ -55,7 +55,7 @@
 //#Arch: x86_64
 
 //#Config:clang-tls-desc-shared-aarch64:clang-tls-desc-shared
-//#CompArgs:-mtls-dialect=desc
+//#CompArgs:-mtls-dialect=desc -fPIC
 //#Arch: aarch64
 
 int get_value();