arch/mips_tls
MIPS architecture-specific rules for TLS symbolization
TLS Models
Global Dynamic (GD) Model
The most general and flexible TLS model, which can access any thread-local variable defined in any shared library, even when the variable’s location is not known until runtime. This model performs a function call to __tls_get_addr through the GOT and TP offsets.
- E.g.,
lw $v0, %got(__tls_get_addr)($gp) addiu $a0,$gp,%tlsgd(tls_var) move $t9,$v0 jalr $t9 nop
=> $v0 gets the address of tls_var.
Local Dynamic (LD) Model
Similar to Global Dynamic, but optimized when multiple TLS variables are known to be defined in the same module. The first access still calls __tls_get_addr, but subsequent accesses can reuse the base address, avoiding repeated calls.
- E.g.:
lw $v0,%got(__tls_get_addr)($gp) addiu $a0, $gp, %tlsldm(TLS_BASE) move $t9,$v0 jalr $t9 nop move reg1,$v0 lui reg2,%dtprel_hi(tls_var) addu reg3,reg2,reg1
lw/sw reg4,%dtprel_lo(tls_var)(reg3)
=> reg4 gets the contents of tls_var. or
addiu reg4,reg3,%dtprel_lo(tls_var)
=> reg4 gets the address of tls_var.
Initial Exec (IE) Model
This model assumes that the TLS variable is part of the main executable or of a shared object that was included in the initial program image. It uses the GOT to access the variable relative to the thread pointer, without calling __tls_get_addr().
- E.g.:
lw $v1,%gottprel(tls_var)($gp) addiu $v0,$v1,$tp
- or
rdhwr $v1,$29 move $v0,$v1 lw $v1,%gottprel(i)($gp) addu $v0,$v1,$v0
Local Exec (LE) Model
The fastest, but least flexible: This model assumes that the TLS variable is defined in the same module (usually the main executable) and that its offset from the thread pointer is known at link time. No GOT access or function call is needed.
- E.g.,
rdhwr reg1,$29 <- rdhwr (read hardware register), reg 29: thread pointer (TP) move regtemp, reg1 lui reg2, %tprel_hi(tls_var) addu reg3,reg2,regtemp
lw reg4, %tprel_lo(tls_var)(reg3)
=> reg4 gets the contents of tls_var. or
addiu reg4, reg3, %tprel_lo(tls_var)
- add_reg_gp(AddEA:address, Reg:register, Dest:address)
A register gets GP-relative address. E.g., 0x04: addiu $v0, $gp, N => add_reg_gp(0x04,V0,GP_VALUE+N)
- reg_flow(SrcEA:address, Reg:register, DestEA:address, RegTemp:register)
Register data flow in limited step
WARNING: Predicate not present in compiled Datalog program (Dead Code)
- call_tls_get_addr_mips(Call:address, Reg:register)
E.g., 0x04: lw $v0, %got(__tls_get_addr)($gp) 0x08: addiu $a0, $gp, N 0x0C: jalr $v0 or 0x04: lw $v0, %got(__tls_get_addr)($gp) 0x08: addiu $a0, $gp, N 0x0C: move $t0,$v0 0x10: jalr $t0 => call_tls_get_addr(0x10,A0)
Uses:
arch.move_reg_reg,binary_format,got_reference_mips_global,reg_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_relative_operand_mips(EA:address, Index:operand_index, Dest:address, Type:symbol, GotEntry:address, Sym:symbol)
A wrapper of tls_relative_operand with additional info including GOT entry and symbol name
Uses:
add_reg_gp,arch.reg_arithmetic_operation,defined_symbol,instruction_immediate_offset,relocation,tls_index,tls_segmentRecursive:
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
- local_dynamic_tls_candidate(EA_lui:address, EA_lo:address, Index_lo:operand_index, Disp:number)
Candidates for Local Dynamic TLS model
Uses:
arch.memory_access,arch.move_reg_reg,arch.reg_arithmetic_operation,arch.reg_reg_arithmetic_operation,hi_load,nextRecursive:
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
- local_dynamic_tls_base(EA:address)
Heuristic to infer the base address for the Local Dynamic TLS model
The base address is used to compute the addresses of TLS variables. There is no direct way of extracting the base address statically.
This heuristic selects the smallest disp (see local_dynamic_tls_candidate) and the smallest TLS symbol address among available symbols, assuming they correspond, and infers the base address from that.
NOTE: This approach may be inaccurate if the TLS variable with the smallest address does not appear in the binary.
Uses:
__agg_single6,__agg_single7,defined_symbolUsed by:
+disconnected2,tls_relative_operand
- tls_pointer_reg_at(EA:address, Reg:register)
At EA, the TLS pointer is stored in Reg.
Used by:
local_exec_tls_candidate
- local_exec_tls_candidate(EA_lui:address, EA_lo:address, Index_lo:operand_index, Disp:number)
Candidates for Local Exec TLS model
Uses:
arch.memory_access,arch.reg_arithmetic_operation,instruction,op_immediate_and_reg,reg_def_use.def_used,tls_pointer_reg_atUsed by:
__agg_single8,symbolic_operand_attribute,tls_relative_operand
- local_exec_tls_base(EA:address)
Heuristic to infer the base address for the Local Exec TLS model
The base address is used to compute the addresses of TLS variables. There is no direct way of extracting the base address statically.
This heuristic selects the smallest disp (see local_exec_tls_candidate) and the smallest TLS symbol address among available symbols, assuming they correspond, and infers the base address from that.
NOTE: This approach may be inaccurate if the TLS variable with the smallest address does not appear in the binary.
Uses:
__agg_single7,__agg_single8,defined_symbolUsed by:
+disconnected4,tls_relative_operand