File: tls.stp

package info (click to toggle)
systemtap 5.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 47,556 kB
  • sloc: cpp: 81,117; ansic: 54,933; xml: 49,795; exp: 43,595; sh: 11,526; python: 5,003; perl: 2,252; tcl: 1,312; makefile: 1,006; javascript: 149; lisp: 105; awk: 101; asm: 91; java: 70; sed: 16
file content (268 lines) | stat: -rw-r--r-- 8,099 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# Implicit Thread Local Storage tapset
# Copyright (C) 2020, 2021 Red Hat, Inc.
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
# Reference: "ELF Handling For Thread-Local Storage" by Ulrich Drepper

/*
 * TCB Pointer points to the thread control block.  dtv points to the DTV
 * array.  DTV[1]-DTV[N]  point to tls entries for module I from the link
 * map.  DTV[1] is the main executable.  The dtv val points to the tls
 * entry where the tls variable value is located.  The tls variable value
 * is also located a fixed offset from the tcbhead so if relocations are
 * simple enough a compiler can generate a direct load.  Complicated
 * relocations may result in a compiler calling a runtime routine
 * __tls_get_addr which uses the DTV. 
 * 
 * 			        TCB Pointer
 * 			        |
 * 			        V
 * TLS				struct tcbhead
 * +---------------------+	+----------------+ 
 * |OFFSET|OFFSET|OFFSET |      | void *tcb	 |
 * |  2   |  1   |  0    |      | dtv_t *dtv	 | --+
 * +---------------------+	+----------------+   |
 * 		   ^-------------------+	     |
 *          v----------------------------------------+
 *      +----------------------------+ | 
 *  DTV |length|gen|dtv[1]|dtv[2]|...| |
 *      +----------------------------+ |
 * 	    dtv[n] |		       |
 *      +---------------------+        |
 * 	|unsigned long counter|	       |
 * 	|struct dtv_pointer { |	       |
 * 	|  void *val	      |->------+
 * 	|  void *to_free}     |
 * 	+---------------------|
 */ 

global tls_debug = 0
private global tls_link_map_name
private global tls_module = 0

%{
# if defined(__DYNINST__)
# if defined(__i386) || defined(__x86_64)
# include <asm/prctl.h>
# include <sys/prctl.h>
# endif
# endif
%}

// Get the thread context pointer that points to the tls thread control block
@__private30 function get_thread_context_pointer (tls_thread_reg_val:long) %{
# ifdef __i386
{
  unsigned long gs;
# if defined(__KERNEL__)
  rdmsrl(MSR_FS_BASE, gs);
# elif defined(__DYNINST__)
  int arch_prctl(int code, unsigned long *addr);
  arch_prctl(ARCH_GET_FS, &gs);
# endif
  STAP_RETVALUE = gs;
}
# elif defined __x86_64
{
  unsigned long fs;
#if defined(__KERNEL__)
  rdmsrl(MSR_FS_BASE, fs);
# elif defined(__DYNINST__)
  int arch_prctl(int code, unsigned long *addr);
  arch_prctl(ARCH_GET_FS, &fs);
# endif
  STAP_RETVALUE = fs;
}
# elif defined __s390x__
{
  unsigned long tlsval;
  asm volatile ("ear %0,%%a0" : "=r" (tlsval));
  asm volatile ("sllg %0,%0,32" : "=r" (tlsval));
  asm volatile ("ear %0,%%a1" : "=r" (tlsval));
  STAP_RETVALUE = tlsval;
}
# elif defined __aarch64__
{
  unsigned long tlsval;
  asm("mrs %0,tpidr_el0" : "=r" (tlsval));
  STAP_RETVALUE = tlsval;
}
# elif defined __powerpc64__
{
  unsigned long tcb;
  asm("subi %0,%1,28688," : "=r" (tcb) : "r" (STAP_ARG_tls_thread_reg_val)); // -0x7010
  STAP_RETVALUE = tcb;
}
# endif
%}


// Get inode of followed link
// TODO Does not follow symbolic links, e.g. to follow /lib64/libc.so.6
//      The idea was to do 'inode = get_inode_from_path (l_name); name = inode_path(inode);'

@__private30 function get_inode_from_path:long (tls_module_name) %{
  struct inode *tls_inode;
  struct path tls_path;
  int code;
  code = kern_path(/* string */ STAP_ARG_tls_module_name, LOOKUP_FOLLOW, &tls_path);
  if (code == 0)
    STAP_RETVALUE = (int64_t)tls_path.dentry->d_inode;
  else
    STAP_RETVALUE = 0;
%}


// Return basename of a path

@__private30 function basename (l_name:string)
{
   for (lni = 0; lni < strlen (l_name); lni++) {
      if (stringat(l_name, lni) == 0x2f)  // '/'
	 slash = lni;
   } 
   if (slash > 0) {
      slash += 1;
      return substr (l_name, slash, strlen(l_name)-slash);
   }
   else
      return l_name;
}


// Setup the link_map module/l_name mapping

@__private30 function setup_link_map ()
{
  delete tls_link_map_name
%( arch == "x86_64" %?
  l_map_count = @var("_rtld_global","/usr/lib64/ld-linux-x86-64.so.2")->_dl_nns
  // TODO Do namespaces need to be supported? assume [0]
  l_map = @var("_rtld_global","/usr/lib64/ld-linux-x86-64.so.2")->_dl_ns[0]->_ns_loaded;
  if (tls_debug > 1)
    println ("l_map=", @var("_rtld_global","/usr/lib64/ld-linux-x86-64.so.2")->_dl_ns[0]->_ns_loaded$);
%)
%( arch == "powerpc" %?
  l_map_count = @var("_rtld_global","/usr/lib64/ld64.so.2")->_dl_nns
  l_map = @var("_rtld_global","/usr/lib64/ld64.so.2")->_dl_ns[0]->_ns_loaded;
  if (tls_debug > 1) 
    println ("l_map=", @var("_rtld_global","/usr/lib64/ld64.so.2")->_dl_ns[0]->_ns_loaded$);
%)
%( arch == "s390" %?
  l_map_count = @var("_rtld_global","/usr/lib/ld64.so.1")->_dl_nns
  l_map = @var("_rtld_global","/usr/lib/ld64.so.1")->_dl_ns[0]->_ns_loaded;
  if (tls_debug > 1) 
    println ("l_map=", @var("_rtld_global","/usr/lib/ld64.so.1")->_dl_ns[0]->_ns_loaded$);
%)
     
  while (l_map)
    {
     l_name = user_string(l_map->l_name);
     if (l_name == "") {
	 l_map = l_map->l_next
	 continue
     }
     l_addr = user_uint64(l_map->l_addr);
     l_tls_modid = l_map->l_tls_modid
     name_base = basename (l_name);
     if (l_tls_modid != 0) {
	tls_link_map_name[l_tls_modid] = name_base;
	if (tls_debug)
	   printf("link_map: %s l_addr=%#lx modid=%d\n", tls_link_map_name[l_tls_modid], l_addr, l_tls_modid);
       }
     l_map = l_map->l_next
    }
}


// Compare module names.  
// sonames, real sonames, and linker sonames should compare equal

@__private30 function regex_module_compare (stap_mod:string, map_mod:string)
{
   // Not an soname
   if (stap_mod !~ "^lib.*$") 
      return stap_mod == map_mod;

   // Most common case libMATCH-1.soMATCH-2
   // MATCH-1 is the soname minus lib prefix and .so* suffix
   if (stap_mod !~ "lib([-\._\+a-zA-Z0-9]+)\.so([\.0-9]*)")
      return 0;
   stap_lib = matched(1);

   if (map_mod !~ "lib([-\._\+a-zA-Z0-9]+)\.so([\.0-9]*)")
      return 0;
   map_lib = matched(1);
   if (stap_lib == map_lib)
     return 1;
   
   // The above will miss e.g. libc.so.6/libc-2.31.so
   // MATCH-1 is the soname minus lib prefix and -version.so suffix
   if (stap_mod !~ "lib([_\+a-zA-Z0-9]+)([-\.0-9]*)\.so([\.0-9]*)")
     return 0;
   stap_lib = matched(1);
   if (map_mod !~ "lib([_\+a-zA-Z0-9]+)([-\.0-9]*)\.so([\.0-9]*)")
     return 0;
   map_lib = matched(1);
   if (stap_lib == map_lib)
     return 1;
   else
     return 0;
}
      

// Find the module by matching the probe name to the link_map name

@__private30 function set_tls_module_by_name (tls_module:string)
{
  matched_proc = basename(tls_module);

  if (tls_debug)
    printf("Looking for %s in link_map\n", matched_proc);
  foreach (lm in tls_link_map_name) {
    if (lm==0) continue;
    if (regex_module_compare (matched_proc, tls_link_map_name[lm])) {
       if (tls_debug)
	 printf("Name Match: %s \n", tls_link_map_name[lm]);
       return lm;
    }
  }
  return 1;
}


/**
 * sfunction __push_tls_address - Handle implicit tls variable relocation
 * 
 * Description: Called for DW_OP_GNU_push_tls_address relocation from loc2stap.css::location_context::translate
 */

function __push_tls_address (tls_var_offset:long, tls_module:string) 
{
%( arch == "powerpc" %?
   // It seems the value of r13 cannot be dependably relied on in get_thread_context_pointer
   r13 = register("r13")
   %:
   r13 = 0
%)
  tcbhead = get_thread_context_pointer (r13);
  if (tls_debug > 1)
    printf ("tls_module=%s tcbhead=%#lx/%d\n", tls_module, tcbhead, tcbhead);
  setup_link_map ();
  module = set_tls_module_by_name (tls_module);
  if (module >= 0) 
    {
      // dtv[-1] is module count
      // dtv[0] is generation count
      // dtv[1] is module[0] info, the main executable
      // dtv[2..n] is module info for dependent shared objects
      dtv$pointer$val = @cast(tcbhead, "tcbhead", "<linux/stp_tls.h>")->dtv[module]->pointer->val;
      if (tls_debug > 1)
	printf ("dtv$pointer$val=%#lx, tls_var_offset=%#lx, module=%d\n", dtv$pointer$val, tls_var_offset, module);
      return dtv$pointer$val + tls_var_offset;
    }
  else
    return 0;
}