binary/elf/tls
- tls_segment_register(Reg:input_reg)
Thread Local Storage (TLS)
ELF binaries use a number of optimization models for dynamically loading and statically linking TLS variables. This module defines predicates for inferring and resolving thread-local variable references in ELF binaries.
’tls_segment’ computes the boundaries and alignment of the TLS data block.
’tls_index’ locates GOT allocated TLS structs used by dynamic TLS models.
’tls_get_addr’ identifies __tls_get_addr calls.
’tls_global_dynamic’ computes “General Dynamic” code sequences.
’tls_local_dynamic’ disambiguates “Local Dynamic” code sequences.
’tls_descriptor’ resolves tlsdesc structs used by TLS descriptor model.
’tls_desc_call’ identifies indirect @TLSDESC calls.
’tls_relative_addr’ computes TLS block-relative offsets in instruction operands.
Below is an overview of the supported ELF TLS models with code detailing the “initial” relocations (object-file) and “outstanding” relocations (linked binary).
”General Dynamic” (GD) - dynamic TLS
Dynamic TLS with offsets resolved dynamically by relocations in GOT.
CODE RELOCATIONS
- ————————————————————————( .o)–
add REG, _GLOBAL_OFFSET_TABLE_ lea EAX, X@TLSGD[REG] TLS_GD call ___tls_get_addr@PLT PLT32 mov EAX, DWORD PTR [EAX]
- ————————————————————————( .so)–
add REG, _GLOBAL_OFFSET_TABLE_ lea EAX, N call ___tls_get_addr@PLT PLT32 mov EAX, DWORD PTR [EAX] …
GOT(N ): .zero 4 DTPMOD32 GOT(N+1): .dword OFFSET DTPOFF32
General Dynamic resolves the address of the TLS variable directly through __tls_get_addr using an initialized OFFSET with relocation in the GOT.
Note that this example is for X86-32. While the implementation of these models is generally consistent between 32 and 64, relocation constants and names vary. In particular, X86-64 uses a fixed code-template for the General Dynamic model, detailed in ‘tls_global_dynamic’.
”Local Dynamic” (LD)
Dynamic TLS with static offsets.
CODE RELOCATIONS
- ————————————————————————( .o)–
lea REG, X@TLSLD[RIP] TLSLD call __tls_get_addr@PLT … mov REG, X@DTPOFF[RAX] DTOFF32
- ————————————————————————( .so)–
lea REG, N call __tls_get_addr@PLT … mov REG, [RAX+OFFSET] …
- GOT(N): .zero 8 DTPMOD64
.zero 8
Local Dynamic resolves the beginning of the TLS block through __tls_get_addr, but the offset field in GOT is uninitialized. Consequently, this optimization level requires one less relocation in the GOT, but we must infer variable references from a static OFFSET in instruction operands - resolved by the linker - for @DTPOFF relocations.
”Initial Executable” (IE)
Static TLS with offset relocations allocated in the GOT.
CODE RELOCATIONS
- ————————————————————————( .o)–
mov RAX, X@GOTTPOFF GOTTPOFF mov FS:[RAX], RAX
- ————————————————————————(.exe)–
mov RAX, N mov FS:[RAX], RAX …
GOT(N): .zero 8 TPOFF64
IE requires a single relocation, resolved at startup (load-time), and stored in the GOT entry for X, where @GOTTPOFF relocations are in position-independent code and @INDNTPOFF relocations are in x86-32 position-dependent code.
”Local Executable” (LE)
Static TLS (link-time) with no relocations.
Resolves all TLS references to block-relative offsets statically, without dynamic relocations or indirect references through the GOT.
CODE RELOCATIONS
- ————————————————————————( .o)–
mov RAX, DWORD PTR FS:X@TPOFF TPOFF32
- ————————————————————————(.exe)–
mov RAX, DWORD PTR FS:[-4] NONE
TLS block address is stored in the FS and GS segment register for X86-64 and x86-32 respectively, and the @TPOFF relocations are resolved by the linker to integral offsets, without relocations.
”TLS Descriptors” (TLSDESC)
Dynamic TLS optimization with indirect call to a lazy relocation function pointer in GOT.
CODE RELOCATIONS
- ————————————————————————( .o)–
lea RAX, X@TLSDESC[RIP] TLSDESC call [QWORD PTR [RAX+X@TLSCALL]] mov RDX, QWORD PTR FS:0 add RAX, RDX mov EAX, DWORD PTR [RAX]
- ————————————————————————( .so)–
lea RAX, [RIP+N] call QWORD PTR [RAX] mov RDX, QWORD PTR FS:0 add RAX, RDX mov EAX, DWORD PTR [RAX] …
GOT(N): .zero 8 TLSDESC
General Dynamic and Local Dynamic access models to thread-local variables are “known to be extremely inefficient because of the need to call a function to obtain the address of a thread-local variable” [3] (register clobbering).
TLS descriptor model is an optimized variant (ca. 2018) that replaces tls_index structs with tlsdesc structs and uses @TLSDESC and @TLSCALL symbol attributes.
Descriptor TLS uses a relocated function pointer - stored in the GOT tlsdesc struct - to lazily preserve call-clobbered registers and call __tls_get_addr().
See ‘tls_descriptor’.
See the following for more detailed documentation:
[1] https://www.uclibc.org/docs/tls.pdf [2] https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/6n33n7feo/index.html [3] https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt
Uses:
binary_formatUsed by:
tls_relative_operand
- tls_segment(Start:address, End:address, Align:unsigned)
A TLS data segment, which may contain contiguous sections (i.e. ‘.tbss’ and ‘.tdata’), begins at address ‘Start’ and ends at address ‘End’ and is aligned to ‘Align’ bytes.
Uses:
__agg_single,__agg_single0,__agg_single1Used by:
+disconnected2,got_reference,inferred_symbol,symbol_minus_symbol,symbolic_expr_from_relocation,symbolic_operand_candidate,tls_desc_call,tls_descriptor,tls_get_addr,tls_local_dynamic,tls_relative_operand
- tls_index(EA:address, Offset:unsigned)
A ‘tls_index’ struct is located in the GOT at address ‘EA’ for a TLS variable at some ‘Offset’ into the TLS block.
’tls_index’ structs are allocated to two contiguous entries in GOT,
typedef struct {
unsigned long int ti_module; unsigned long int ti_offset;
} tls_index;
Note that ‘Offset’ is initialized for @TLSGD relocations and zero for @TLSLD.
- call_tls_get_addr(Call:address, Reg:register)
Identify Call to the builtin function ‘__tls_get_addr’.
Reg: the register loaded with the resolved address (either the beginning of the TLS segment for @TLSLD relocations, or the address of the variable for @TLSGD relocations.)
Uses:
binary_format,direct_callRecursive:
arm_jump_table_block_instruction,stack_def_use.live_var_at_prior_used,possible_target_from,data_access,stack_def_use.ref_in_block,code_in_block_candidate,candidate_block_is_padding,relocation_adjustment,data_in_code,cmp_reg_to_reg,__agg_single3,jump_table_candidate_refined,invalid,inferred_main_in_reg,base_relative_operand,relative_address,wis_has_prior,compare_and_jump_indirect_op_valid,litpool_confidence,block_boundaries,resolved_reaches,indexed_pc_relative_load_relative,unresolved_block,split_load_for_symbolization,base_relative_jump,last_value_reg_limit,instruction_memory_access_size,plt_block,straight_line_last_def,__agg_subclause2,symbol_minus_symbol_litpool_access_pattern,jump_table_element_access,block_points,const_value_reg_used,reg_reg_arithmetic_operation_defs,stack_def_use.live_var_def,stack_def_use.live_var_used_in_block,composite_data_access,jump_table_target,value_reg_limit,block_candidate_dependency_edge,tls_get_addr,indefinite_litpool_ref,reg_def_use.used_in_block,simple_data_access_pattern,arch.simple_data_load,split_load,value_reg,stack_def_use.live_var_at_block_end,block_limit,arm_jump_table_data_block,value_reg_unsupported,stack_def_use.last_def_in_block,overlapping_instruction,next_start,block,wis_memo,next_type,cinf_ldr_add_pc,possible_target,relative_jump_table_entry_candidate,arm_jump_table_data_block_limit,reg_def_use.last_def_in_block,__agg_subclause6,stack_def_use.live_var_used,def_used_for_address,known_block,contains_plausible_instr_seq,reg_def_use.live_var_at_block_end,arch.reg_relative_load,split_load_point,candidate_block_is_not_padding,split_load_candidate,split_load_conflict,incomplete_block,wis_schedule_iter,reg_def_use.defined_in_block,__agg_subclause3,reg_def_use.ref_in_block,contains_implausible_instr_seq,base_relative_operation,negative_block_heuristic,no_return_call,reg_def_use.used,block_total_points,reg_def_use.block_last_def,discarded_block,reg_has_base_image,no_return_call_propagated,block_points_proportional,nop_in_padding_candidate,reg_def_use.live_var_used,block_heuristic,litpool_ref,after_end,reg_def_use.flow_def,transition_block_limit,unresolved_interval_order,cmp_defines,block_last_instruction,block_overlap,jump_table_max,arch.extend_load,reg_def_use.ambiguous_last_def_in_block,arm_jump_table_skip_first_entry,impossible_block,hi_load_prop,block_candidate_boundaries,is_padding,may_fallthrough,gp_relative_operand,segment_target_range,arm_jump_table_candidate_start,discarded_split_load,arm_jump_table_cmp_limit,first_block_in_byte_interval,arm_jump_table_candidate,jump_table_signed,stack_def_use.used_in_block,no_value_reg_limit,tls_desc_call,reg_used_for,branch_to_calculated_pc_rel_addr,__agg_single2,init_symbol_minus_symbol_candidate_arm,data_block_candidate,start_function,basic_target,reg_def_use.return_val_used,padding_block_limit,straight_line_def_used,data_block_limit,reg_def_use.ambiguous_block_last_def,wis_prior,stack_def_use.def_used,split_load_operand,init_ldr_add_pc,got_relative_operand,next_block_in_byte_interval,data_in_code_propagate,no_return_call_refined,call_tls_get_addr,inferred_main_dispatch,reg_def_use.def_used,must_fallthrough,block_next,value_reg_edge,unlikely_have_symbolic_immediate,adjusts_stack_in_block,self_contained_segment,block_implies_block,__agg_subclause7,__agg_single6,stack_def_use.defined_in_block,relocation_adjustment_total,compare_and_jump_indirect,flags_and_jump_pair,jump_table_candidate,function_inference.function_entry_initial,invalid_jump_table_candidate,reg_def_use.return_block_end,plt_entry,jump_table_start,reg_has_got,code_in_block_candidate_refined,code_in_block,no_return_block,unresolved_interval,litpool_symbolic_operand,split_load_total_points,unresolved_block_overlap,symbolic_expr_from_relocation,compare_and_jump_register,indexed_pc_relative_load,reg_def_use.live_var_def,arm_jump_table_block_start,overlap_with_litpool,compare_and_jump_immediate,initialized_data_segment,litpool_boundaries,padding_block_candidate,relative_address_start,stack_def_use.block_last_def,jump_table_prelude,correlated_live_reg,inter_procedural_edge,likely_fallthrough,adrp_used,next_end,common_tail,wis_schedule,stack_base_reg_move,reg_def_use.live_var_at_prior_used,data_segment,block_instruction_next
- tls_get_addr(Load:address, Call:address, Dest:address)
A TLS address is resolved dynamically with a call to the builtin ‘__tls_get_addr(struct *ti_index)’ function at address ‘Call’.
The address of the variable’s tls_index struct is loaded in the instruction at address ‘Load’.
The resolved address ‘Dest’ is either the beginning of the TLS segment for @TLSLD relocations, or the address of the variable for @TLSGD relocations.
Uses:
binary_format,pc_relative_operand,tls_index,tls_segmentUsed by:
tls_global_dynamic,tls_local_dynamic,tls_relative_operandRecursive:
arm_jump_table_block_instruction,stack_def_use.live_var_at_prior_used,possible_target_from,data_access,stack_def_use.ref_in_block,code_in_block_candidate,candidate_block_is_padding,relocation_adjustment,data_in_code,cmp_reg_to_reg,__agg_single3,jump_table_candidate_refined,invalid,inferred_main_in_reg,base_relative_operand,relative_address,wis_has_prior,compare_and_jump_indirect_op_valid,litpool_confidence,block_boundaries,resolved_reaches,indexed_pc_relative_load_relative,unresolved_block,split_load_for_symbolization,base_relative_jump,last_value_reg_limit,instruction_memory_access_size,plt_block,straight_line_last_def,__agg_subclause2,symbol_minus_symbol_litpool_access_pattern,jump_table_element_access,block_points,const_value_reg_used,reg_reg_arithmetic_operation_defs,stack_def_use.live_var_def,stack_def_use.live_var_used_in_block,composite_data_access,jump_table_target,value_reg_limit,block_candidate_dependency_edge,tls_get_addr,indefinite_litpool_ref,reg_def_use.used_in_block,simple_data_access_pattern,arch.simple_data_load,split_load,value_reg,stack_def_use.live_var_at_block_end,block_limit,arm_jump_table_data_block,value_reg_unsupported,stack_def_use.last_def_in_block,overlapping_instruction,next_start,block,wis_memo,next_type,cinf_ldr_add_pc,possible_target,relative_jump_table_entry_candidate,arm_jump_table_data_block_limit,reg_def_use.last_def_in_block,__agg_subclause6,stack_def_use.live_var_used,def_used_for_address,known_block,contains_plausible_instr_seq,reg_def_use.live_var_at_block_end,arch.reg_relative_load,split_load_point,candidate_block_is_not_padding,split_load_candidate,split_load_conflict,incomplete_block,wis_schedule_iter,reg_def_use.defined_in_block,__agg_subclause3,reg_def_use.ref_in_block,contains_implausible_instr_seq,base_relative_operation,negative_block_heuristic,no_return_call,reg_def_use.used,block_total_points,reg_def_use.block_last_def,discarded_block,reg_has_base_image,no_return_call_propagated,block_points_proportional,nop_in_padding_candidate,reg_def_use.live_var_used,block_heuristic,litpool_ref,after_end,reg_def_use.flow_def,transition_block_limit,unresolved_interval_order,cmp_defines,block_last_instruction,block_overlap,jump_table_max,arch.extend_load,reg_def_use.ambiguous_last_def_in_block,arm_jump_table_skip_first_entry,impossible_block,hi_load_prop,block_candidate_boundaries,is_padding,may_fallthrough,gp_relative_operand,segment_target_range,arm_jump_table_candidate_start,discarded_split_load,arm_jump_table_cmp_limit,first_block_in_byte_interval,arm_jump_table_candidate,jump_table_signed,stack_def_use.used_in_block,no_value_reg_limit,tls_desc_call,reg_used_for,branch_to_calculated_pc_rel_addr,__agg_single2,init_symbol_minus_symbol_candidate_arm,data_block_candidate,start_function,basic_target,reg_def_use.return_val_used,padding_block_limit,straight_line_def_used,data_block_limit,reg_def_use.ambiguous_block_last_def,wis_prior,stack_def_use.def_used,split_load_operand,init_ldr_add_pc,got_relative_operand,next_block_in_byte_interval,data_in_code_propagate,no_return_call_refined,call_tls_get_addr,inferred_main_dispatch,reg_def_use.def_used,must_fallthrough,block_next,value_reg_edge,unlikely_have_symbolic_immediate,adjusts_stack_in_block,self_contained_segment,block_implies_block,__agg_subclause7,__agg_single6,stack_def_use.defined_in_block,relocation_adjustment_total,compare_and_jump_indirect,flags_and_jump_pair,jump_table_candidate,function_inference.function_entry_initial,invalid_jump_table_candidate,reg_def_use.return_block_end,plt_entry,jump_table_start,reg_has_got,code_in_block_candidate_refined,code_in_block,no_return_block,unresolved_interval,litpool_symbolic_operand,split_load_total_points,unresolved_block_overlap,symbolic_expr_from_relocation,compare_and_jump_register,indexed_pc_relative_load,reg_def_use.live_var_def,arm_jump_table_block_start,overlap_with_litpool,compare_and_jump_immediate,initialized_data_segment,litpool_boundaries,padding_block_candidate,relative_address_start,stack_def_use.block_last_def,jump_table_prelude,correlated_live_reg,inter_procedural_edge,likely_fallthrough,adrp_used,next_end,common_tail,wis_schedule,stack_base_reg_move,reg_def_use.live_var_at_prior_used,data_segment,block_instruction_next
- tls_desc_call(Load:address, Call:address, Dest:address)
A TLS variable is resolved dynamically by indirect call to a TLSCALL relocated builtin function at address ‘Call’.
The address of the variable’s tls_desc struct is loaded in the instruction at address ‘Load’.
Uses:
arch.call,pc_relative_operand,tls_descriptor,tls_segmentUsed by:
tls_relative_operandRecursive:
arm_jump_table_block_instruction,stack_def_use.live_var_at_prior_used,possible_target_from,data_access,stack_def_use.ref_in_block,code_in_block_candidate,candidate_block_is_padding,relocation_adjustment,data_in_code,cmp_reg_to_reg,__agg_single3,jump_table_candidate_refined,invalid,inferred_main_in_reg,base_relative_operand,relative_address,wis_has_prior,compare_and_jump_indirect_op_valid,litpool_confidence,block_boundaries,resolved_reaches,indexed_pc_relative_load_relative,unresolved_block,split_load_for_symbolization,base_relative_jump,last_value_reg_limit,instruction_memory_access_size,plt_block,straight_line_last_def,__agg_subclause2,symbol_minus_symbol_litpool_access_pattern,jump_table_element_access,block_points,const_value_reg_used,reg_reg_arithmetic_operation_defs,stack_def_use.live_var_def,stack_def_use.live_var_used_in_block,composite_data_access,jump_table_target,value_reg_limit,block_candidate_dependency_edge,tls_get_addr,indefinite_litpool_ref,reg_def_use.used_in_block,simple_data_access_pattern,arch.simple_data_load,split_load,value_reg,stack_def_use.live_var_at_block_end,block_limit,arm_jump_table_data_block,value_reg_unsupported,stack_def_use.last_def_in_block,overlapping_instruction,next_start,block,wis_memo,next_type,cinf_ldr_add_pc,possible_target,relative_jump_table_entry_candidate,arm_jump_table_data_block_limit,reg_def_use.last_def_in_block,__agg_subclause6,stack_def_use.live_var_used,def_used_for_address,known_block,contains_plausible_instr_seq,reg_def_use.live_var_at_block_end,arch.reg_relative_load,split_load_point,candidate_block_is_not_padding,split_load_candidate,split_load_conflict,incomplete_block,wis_schedule_iter,reg_def_use.defined_in_block,__agg_subclause3,reg_def_use.ref_in_block,contains_implausible_instr_seq,base_relative_operation,negative_block_heuristic,no_return_call,reg_def_use.used,block_total_points,reg_def_use.block_last_def,discarded_block,reg_has_base_image,no_return_call_propagated,block_points_proportional,nop_in_padding_candidate,reg_def_use.live_var_used,block_heuristic,litpool_ref,after_end,reg_def_use.flow_def,transition_block_limit,unresolved_interval_order,cmp_defines,block_last_instruction,block_overlap,jump_table_max,arch.extend_load,reg_def_use.ambiguous_last_def_in_block,arm_jump_table_skip_first_entry,impossible_block,hi_load_prop,block_candidate_boundaries,is_padding,may_fallthrough,gp_relative_operand,segment_target_range,arm_jump_table_candidate_start,discarded_split_load,arm_jump_table_cmp_limit,first_block_in_byte_interval,arm_jump_table_candidate,jump_table_signed,stack_def_use.used_in_block,no_value_reg_limit,tls_desc_call,reg_used_for,branch_to_calculated_pc_rel_addr,__agg_single2,init_symbol_minus_symbol_candidate_arm,data_block_candidate,start_function,basic_target,reg_def_use.return_val_used,padding_block_limit,straight_line_def_used,data_block_limit,reg_def_use.ambiguous_block_last_def,wis_prior,stack_def_use.def_used,split_load_operand,init_ldr_add_pc,got_relative_operand,next_block_in_byte_interval,data_in_code_propagate,no_return_call_refined,call_tls_get_addr,inferred_main_dispatch,reg_def_use.def_used,must_fallthrough,block_next,value_reg_edge,unlikely_have_symbolic_immediate,adjusts_stack_in_block,self_contained_segment,block_implies_block,__agg_subclause7,__agg_single6,stack_def_use.defined_in_block,relocation_adjustment_total,compare_and_jump_indirect,flags_and_jump_pair,jump_table_candidate,function_inference.function_entry_initial,invalid_jump_table_candidate,reg_def_use.return_block_end,plt_entry,jump_table_start,reg_has_got,code_in_block_candidate_refined,code_in_block,no_return_block,unresolved_interval,litpool_symbolic_operand,split_load_total_points,unresolved_block_overlap,symbolic_expr_from_relocation,compare_and_jump_register,indexed_pc_relative_load,reg_def_use.live_var_def,arm_jump_table_block_start,overlap_with_litpool,compare_and_jump_immediate,initialized_data_segment,litpool_boundaries,padding_block_candidate,relative_address_start,stack_def_use.block_last_def,jump_table_prelude,correlated_live_reg,inter_procedural_edge,likely_fallthrough,adrp_used,next_end,common_tail,wis_schedule,stack_base_reg_move,reg_def_use.live_var_at_prior_used,data_segment,block_instruction_next
- tls_global_dynamic(EA:address)
Find all “General Dynamic” code sequences.
First we find code sequences referencing tls_index structs in GOT with both DTPMOD and DTPOFF relocations.
However, the GD and LD models cannot be distinguished by the presence of a DTPOFF relocation alone, as static variables may have only a DTPMOD relocation and still use the General Dynamic (@TLDGD) model.
For x86-64, we distinguish @TLSGD from @TLSLD with the code template of the form:
.byte 0x66 lea RDI, X@TLSGD[RIP] .value 0x6666 rex64 call __tls_get_addr@PLT
GCC uses explicit directives to inline 0x66 bytes (which are actually `data16’ instruction prefixes) as padding. Likewise, the accompanying `rex64’ prefix on the call instruction inserts a 0x48 byte to extend the code sequence to the required 16-byte length.
Finally, for x86-32 we identify tls_index structs with non-zero offsets and propagate backward to adjacent structs, as the offset value may be initialized to zero. Note that this approach will replace the @TLSGD relocations with a @TLSLD relocation for binaries with a single static TLS variable, as the two models are ambigous in this case.
https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter8-60/index.html
Uses:
binary_format,got_relative_operand,instruction,pc_relative_operand,relocation,tls_get_addr,tls_indexUsed by:
symbolic_operand_attribute,tls_local_dynamicRecursive:
tls_global_dynamic
- tls_local_dynamic(EA:address)
Identify and disambiguate @TLSLD/@TLSLDM by exclusion of previously computed @TLSGD relocations.
General Dynamic TLS uses two relocations, DTPMOD and DTPOFF, allowing __tls_get_addr to return the address of the variable directly.
Local Dynamic TLS uses a single DTPMOD relocation, and __tls_get_addr returns the start of the TLS block. Variables are addressed with integral offsets in indirect operands.
We identify @TLSLD relocations by a call to __tls_get_address that are not global dynamic. For x86-64, we distinguish @TLSGD from @TLSLD with the code template described in ‘tls_global_dynamic’.
- tls_descriptor(EA:address, Offset:unsigned)
A ‘tlsdesc’ struct is located in the GOT at address ‘EA’ and references a symbol at some ‘Offset’ in the ‘tls_segment’.
’tlsdesc’ structs are allocated to two contiguous entries in GOT, for a struct of the general form:
Descriptor structs have a single outstanding relocation for the first struct member, a pointer referencing one of the following resolution functions:
Note that these functions take a single pointer argument, allowing the code to load the address of the struct and indirectly call the function, chosen by the dynamic loader, at the same location, e.g.:
lea RAX, X@TLSDESC[RIP] call [QWORD PTR [RAX+X@TLSCALL]]
Consequently, variations of this model are best explained in terms of the tlsdesc struct after dynamic loading (runtime).
static _dl_tlsdesc_return Offset
undefined weak _dl_tlsdesc_undefweak Addend
unallocated _dl_tlsdesc_dynamic struct tlsdesc_dynamic_arg*
We can infer the target of tlsdesc relocations using the addend of the TLSDESC relocation. For x86-32, static TLS variables may store the offset directly in the arg_slot field.
Also, static TLS variables may be further optimized by the compiler using a combination of @TLSDESC/@TLSCALL and @DTPOFF relocations, using a code sequence like:
lea RAX, _TLS_MODULE_BASE_@TLSDESC[RIP] call [QWORD PTR [RAX+_TLS_MODULE_BASE_@TLSCALL]] mov RSI, QWORD PTR FS:0 lea R8, X@DTPOFF[RAX+RSI] … add RAX, OFFSET Y@DTPOFF
All @DTPOFF relocations will be resolved by the linker to integral indirect operand offsets, and only a single outstanding TLSDESC relocation will exist for consecutive static variables.
Use TLSDESC with the ‘-mtls-dialect=gnu2’ option for GCC.
- tls_relative_operand(EA:address, Index:operand_index, Dest:address, Type:symbol)
Instruction at address ‘EA’ references a TLS data address ‘Dest’ in the operand at ‘Index’ using a TLS relocation of some ‘Type’.
Relocation ‘Type’ is one of following labels corresponding to those used in the ‘symbolic_operand_attribute’ predicate:
DTPOFF Local Dynamic 32+64 TPOFF Local Executable 64 NTPOFF Local Executable 32
Note that TLSGD and TLSLD attributes are excluded as they reference GOT, not TLS offsets. Likewise for “Initial Executable” relocations (e.g. @GOTTPOFF).
Uses:
arch.move_reg_imm,binary_format,const_value_reg_used,instruction_get_op,op_indirect,op_indirect_mapped,reg_def_use.def_used,reg_map_nullable,tls_desc_call,tls_get_addr,tls_local_dynamic,tls_segment,tls_segment_registerUsed by:
symbolic_operand_attribute,symbolic_operand_candidate
- tls_operand_attribute(Type:symbol, Attribute:symbol)
Map TLS relocation types to one or more symbolic operand types.