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,inferred_symbol_mips,symbol_minus_symbol,symbolic_expr_from_relocation,symbolic_operand_attribute,symbolic_operand_candidate,tls_desc_call,tls_descriptor,tls_get_addr,tls_local_dynamic,tls_relative_operand,tls_relative_operand_mips
- 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_cmp_limit,value_reg_unsupported,value_reg,candidate_block_is_not_padding,gp_relative_operand,split_load_total_points,stack_def_use.def_used,indexed_pc_relative_load,branch_to_calculated_pc_rel_addr,code_in_block,jump_table_element_access,compare_and_jump_register,jump_table_candidate,invalid,block_candidate_boundaries,data_in_code_propagate,relocation_adjustment_total,arch.simple_data_load,local_dynamic_tls_candidate,block_limit,block_instruction_next,reg_def_use.used,wis_memo,stack_def_use.live_var_used_in_block,wis_has_prior,arm_jump_table_skip_first_entry,wis_schedule,flags_and_jump_pair,litpool_symbolic_operand,segment_target_range,inferred_main_in_reg,code_in_block_candidate_refined,unresolved_interval,code_in_block_candidate,reg_def_use.return_val_used,start_function,correlated_live_reg,indexed_pc_relative_load_relative,arm_jump_table_block_start,arch.reg_relative_load,arm_jump_table_data_block,next_block_in_byte_interval,impossible_block,reg_def_use.ambiguous_last_def_in_block,reg_def_use.live_var_at_prior_used,data_block_candidate,invalid_jump_table_candidate,compare_and_jump_indirect,reg_def_use.flow_def,arm_jump_table_data_block_limit,stack_base_reg_move,__agg_single3,base_relative_jump,arm_jump_table_block_instruction,__agg_subclause2,no_value_reg_limit,overlap_with_litpool,split_load_candidate,__agg_subclause4,padding_block_candidate,inferred_main_dispatch,relocation_adjustment,tls_get_addr,after_end,stack_def_use.defined_in_block,reg_def_use.used_in_block,stack_def_use.last_def_in_block,candidate_block_is_padding,plt_entry,jump_table_max,reg_def_use.live_var_at_block_end,initialized_data_segment,tls_relative_operand_mips,relative_address,litpool_ref,arm_jump_table_candidate_start,must_fallthrough,may_fallthrough,contains_implausible_instr_seq,__agg_subclause6,no_return_call_refined,block_points_proportional,no_return_block,cinf_ldr_add_pc,relative_address_start,adjusts_stack_in_block,resolved_reaches,block_total_points,next_end,possible_target_from,block_candidate_dependency_edge,block,self_contained_segment,block_next,reg_def_use.live_var_def,jump_table_signed,reg_has_got,no_return_call_propagated,wis_prior,split_load_conflict,jump_table_start,wis_schedule_iter,reg_def_use.ambiguous_block_last_def,jump_table_prelude,first_block_in_byte_interval,arch.extend_load,base_relative_operation,indefinite_litpool_ref,compare_and_jump_immediate,unresolved_block_overlap,split_load,stack_def_use.used_in_block,reg_reg_arithmetic_operation_defs,padding_block_limit,reg_def_use.def_used,tls_desc_call,litpool_boundaries,discarded_block,stack_def_use.live_var_def,base_relative_operand,data_in_code,plt_block,reg_used_for,overlapping_instruction,reg_has_base_image,cmp_defines,nop_in_padding_candidate,compare_and_jump_indirect_op_valid,data_block_limit,__agg_subclause7,block_implies_block,const_value_reg_used,reg_def_use.return_block_end,next_type,__agg_single10,adrp_used,symbol_minus_symbol_litpool_access_pattern,contains_plausible_instr_seq,got_relative_operand,jump_table_candidate_refined,stack_def_use.live_var_at_prior_used,possible_target,instruction_memory_access_size,block_points,basic_target,split_load_operand,incomplete_block,transition_block_limit,data_access,litpool_confidence,block_overlap,arm_jump_table_candidate,reg_def_use.block_last_def,reg_def_use.last_def_in_block,is_padding,simple_data_access_pattern,reg_def_use.live_var_used,straight_line_last_def,__agg_single2,inter_procedural_edge,init_symbol_minus_symbol_candidate_arm,function_inference.function_entry_initial,block_heuristic,hi_load_prop,unresolved_interval_order,symbolic_expr_from_relocation,discarded_split_load,likely_fallthrough,block_boundaries,call_tls_get_addr_mips,stack_def_use.block_last_def,__agg_single6,stack_def_use.live_var_at_block_end,split_load_point,cmp_reg_to_reg,unlikely_have_symbolic_immediate,reg_def_use.defined_in_block,jump_table_target,no_return_call,split_load_for_symbolization,call_tls_get_addr,__agg_subclause3,next_start,stack_def_use.ref_in_block,straight_line_def_used,def_used_for_address,init_ldr_add_pc,last_value_reg_limit,value_reg_edge,data_segment,common_tail,reg_def_use.ref_in_block,known_block,negative_block_heuristic,relative_jump_table_entry_candidate,stack_def_use.live_var_used,unresolved_block,block_last_instruction,value_reg_limit,composite_data_access
- 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:
add_reg_gp,binary_format,pc_relative_operand,tls_index,tls_segmentUsed by:
tls_global_dynamic,tls_local_dynamic,tls_relative_operandRecursive:
arm_jump_table_cmp_limit,value_reg_unsupported,value_reg,candidate_block_is_not_padding,gp_relative_operand,split_load_total_points,stack_def_use.def_used,indexed_pc_relative_load,branch_to_calculated_pc_rel_addr,code_in_block,jump_table_element_access,compare_and_jump_register,jump_table_candidate,invalid,block_candidate_boundaries,data_in_code_propagate,relocation_adjustment_total,arch.simple_data_load,local_dynamic_tls_candidate,block_limit,block_instruction_next,reg_def_use.used,wis_memo,stack_def_use.live_var_used_in_block,wis_has_prior,arm_jump_table_skip_first_entry,wis_schedule,flags_and_jump_pair,litpool_symbolic_operand,segment_target_range,inferred_main_in_reg,code_in_block_candidate_refined,unresolved_interval,code_in_block_candidate,reg_def_use.return_val_used,start_function,correlated_live_reg,indexed_pc_relative_load_relative,arm_jump_table_block_start,arch.reg_relative_load,arm_jump_table_data_block,next_block_in_byte_interval,impossible_block,reg_def_use.ambiguous_last_def_in_block,reg_def_use.live_var_at_prior_used,data_block_candidate,invalid_jump_table_candidate,compare_and_jump_indirect,reg_def_use.flow_def,arm_jump_table_data_block_limit,stack_base_reg_move,__agg_single3,base_relative_jump,arm_jump_table_block_instruction,__agg_subclause2,no_value_reg_limit,overlap_with_litpool,split_load_candidate,__agg_subclause4,padding_block_candidate,inferred_main_dispatch,relocation_adjustment,tls_get_addr,after_end,stack_def_use.defined_in_block,reg_def_use.used_in_block,stack_def_use.last_def_in_block,candidate_block_is_padding,plt_entry,jump_table_max,reg_def_use.live_var_at_block_end,initialized_data_segment,tls_relative_operand_mips,relative_address,litpool_ref,arm_jump_table_candidate_start,must_fallthrough,may_fallthrough,contains_implausible_instr_seq,__agg_subclause6,no_return_call_refined,block_points_proportional,no_return_block,cinf_ldr_add_pc,relative_address_start,adjusts_stack_in_block,resolved_reaches,block_total_points,next_end,possible_target_from,block_candidate_dependency_edge,block,self_contained_segment,block_next,reg_def_use.live_var_def,jump_table_signed,reg_has_got,no_return_call_propagated,wis_prior,split_load_conflict,jump_table_start,wis_schedule_iter,reg_def_use.ambiguous_block_last_def,jump_table_prelude,first_block_in_byte_interval,arch.extend_load,base_relative_operation,indefinite_litpool_ref,compare_and_jump_immediate,unresolved_block_overlap,split_load,stack_def_use.used_in_block,reg_reg_arithmetic_operation_defs,padding_block_limit,reg_def_use.def_used,tls_desc_call,litpool_boundaries,discarded_block,stack_def_use.live_var_def,base_relative_operand,data_in_code,plt_block,reg_used_for,overlapping_instruction,reg_has_base_image,cmp_defines,nop_in_padding_candidate,compare_and_jump_indirect_op_valid,data_block_limit,__agg_subclause7,block_implies_block,const_value_reg_used,reg_def_use.return_block_end,next_type,__agg_single10,adrp_used,symbol_minus_symbol_litpool_access_pattern,contains_plausible_instr_seq,got_relative_operand,jump_table_candidate_refined,stack_def_use.live_var_at_prior_used,possible_target,instruction_memory_access_size,block_points,basic_target,split_load_operand,incomplete_block,transition_block_limit,data_access,litpool_confidence,block_overlap,arm_jump_table_candidate,reg_def_use.block_last_def,reg_def_use.last_def_in_block,is_padding,simple_data_access_pattern,reg_def_use.live_var_used,straight_line_last_def,__agg_single2,inter_procedural_edge,init_symbol_minus_symbol_candidate_arm,function_inference.function_entry_initial,block_heuristic,hi_load_prop,unresolved_interval_order,symbolic_expr_from_relocation,discarded_split_load,likely_fallthrough,block_boundaries,call_tls_get_addr_mips,stack_def_use.block_last_def,__agg_single6,stack_def_use.live_var_at_block_end,split_load_point,cmp_reg_to_reg,unlikely_have_symbolic_immediate,reg_def_use.defined_in_block,jump_table_target,no_return_call,split_load_for_symbolization,call_tls_get_addr,__agg_subclause3,next_start,stack_def_use.ref_in_block,straight_line_def_used,def_used_for_address,init_ldr_add_pc,last_value_reg_limit,value_reg_edge,data_segment,common_tail,reg_def_use.ref_in_block,known_block,negative_block_heuristic,relative_jump_table_entry_candidate,stack_def_use.live_var_used,unresolved_block,block_last_instruction,value_reg_limit,composite_data_access
- 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_cmp_limit,value_reg_unsupported,value_reg,candidate_block_is_not_padding,gp_relative_operand,split_load_total_points,stack_def_use.def_used,indexed_pc_relative_load,branch_to_calculated_pc_rel_addr,code_in_block,jump_table_element_access,compare_and_jump_register,jump_table_candidate,invalid,block_candidate_boundaries,data_in_code_propagate,relocation_adjustment_total,arch.simple_data_load,local_dynamic_tls_candidate,block_limit,block_instruction_next,reg_def_use.used,wis_memo,stack_def_use.live_var_used_in_block,wis_has_prior,arm_jump_table_skip_first_entry,wis_schedule,flags_and_jump_pair,litpool_symbolic_operand,segment_target_range,inferred_main_in_reg,code_in_block_candidate_refined,unresolved_interval,code_in_block_candidate,reg_def_use.return_val_used,start_function,correlated_live_reg,indexed_pc_relative_load_relative,arm_jump_table_block_start,arch.reg_relative_load,arm_jump_table_data_block,next_block_in_byte_interval,impossible_block,reg_def_use.ambiguous_last_def_in_block,reg_def_use.live_var_at_prior_used,data_block_candidate,invalid_jump_table_candidate,compare_and_jump_indirect,reg_def_use.flow_def,arm_jump_table_data_block_limit,stack_base_reg_move,__agg_single3,base_relative_jump,arm_jump_table_block_instruction,__agg_subclause2,no_value_reg_limit,overlap_with_litpool,split_load_candidate,__agg_subclause4,padding_block_candidate,inferred_main_dispatch,relocation_adjustment,tls_get_addr,after_end,stack_def_use.defined_in_block,reg_def_use.used_in_block,stack_def_use.last_def_in_block,candidate_block_is_padding,plt_entry,jump_table_max,reg_def_use.live_var_at_block_end,initialized_data_segment,tls_relative_operand_mips,relative_address,litpool_ref,arm_jump_table_candidate_start,must_fallthrough,may_fallthrough,contains_implausible_instr_seq,__agg_subclause6,no_return_call_refined,block_points_proportional,no_return_block,cinf_ldr_add_pc,relative_address_start,adjusts_stack_in_block,resolved_reaches,block_total_points,next_end,possible_target_from,block_candidate_dependency_edge,block,self_contained_segment,block_next,reg_def_use.live_var_def,jump_table_signed,reg_has_got,no_return_call_propagated,wis_prior,split_load_conflict,jump_table_start,wis_schedule_iter,reg_def_use.ambiguous_block_last_def,jump_table_prelude,first_block_in_byte_interval,arch.extend_load,base_relative_operation,indefinite_litpool_ref,compare_and_jump_immediate,unresolved_block_overlap,split_load,stack_def_use.used_in_block,reg_reg_arithmetic_operation_defs,padding_block_limit,reg_def_use.def_used,tls_desc_call,litpool_boundaries,discarded_block,stack_def_use.live_var_def,base_relative_operand,data_in_code,plt_block,reg_used_for,overlapping_instruction,reg_has_base_image,cmp_defines,nop_in_padding_candidate,compare_and_jump_indirect_op_valid,data_block_limit,__agg_subclause7,block_implies_block,const_value_reg_used,reg_def_use.return_block_end,next_type,__agg_single10,adrp_used,symbol_minus_symbol_litpool_access_pattern,contains_plausible_instr_seq,got_relative_operand,jump_table_candidate_refined,stack_def_use.live_var_at_prior_used,possible_target,instruction_memory_access_size,block_points,basic_target,split_load_operand,incomplete_block,transition_block_limit,data_access,litpool_confidence,block_overlap,arm_jump_table_candidate,reg_def_use.block_last_def,reg_def_use.last_def_in_block,is_padding,simple_data_access_pattern,reg_def_use.live_var_used,straight_line_last_def,__agg_single2,inter_procedural_edge,init_symbol_minus_symbol_candidate_arm,function_inference.function_entry_initial,block_heuristic,hi_load_prop,unresolved_interval_order,symbolic_expr_from_relocation,discarded_split_load,likely_fallthrough,block_boundaries,call_tls_get_addr_mips,stack_def_use.block_last_def,__agg_single6,stack_def_use.live_var_at_block_end,split_load_point,cmp_reg_to_reg,unlikely_have_symbolic_immediate,reg_def_use.defined_in_block,jump_table_target,no_return_call,split_load_for_symbolization,call_tls_get_addr,__agg_subclause3,next_start,stack_def_use.ref_in_block,straight_line_def_used,def_used_for_address,init_ldr_add_pc,last_value_reg_limit,value_reg_edge,data_segment,common_tail,reg_def_use.ref_in_block,known_block,negative_block_heuristic,relative_jump_table_entry_candidate,stack_def_use.live_var_used,unresolved_block,block_last_instruction,value_reg_limit,composite_data_access
- 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:
add_reg_gp,arch.move_reg_imm,arch.reg_arithmetic_operation,binary_format,const_value_reg_used,defined_symbol,gp_relative_operand,instruction_get_op,instruction_immediate_offset,local_dynamic_tls_base,local_dynamic_tls_candidate,local_exec_tls_base,local_exec_tls_candidate,op_indirect,op_indirect_mapped,reg_def_use.def_used,reg_map_nullable,relocation,symbol,tls_desc_call,tls_get_addr,tls_index,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.