49dcef36916eefe4c0d5f77721f2c1e8b5c2c4f0
[charm.git] / src / conv-core / global-elfgot.C
1 /*  Library to manage the program's global offset table.
2  *
3  *  The global offset table (GOT) is a static, linker-generated 
4  *  table used to look up the locations of dynamic stuff
5  *  like subroutines or global data.
6  *
7  *  It's only generated and used if you're on an ELF binary
8  *  format machine, and compile with "-fpic", for "position 
9  *  independent code"; otherwise the code includes direct 
10  *  references to subroutines and data.
11  *
12  *  During execution of a single routine, the GOT is pointed to
13  *  by %ebx.  Changing %ebx only affects global access within
14  *  the current subroutine, so we have to change the single, 
15  *  static copy of the GOT to switch between sets of global variables.
16  *  This is slow, but because the GOT just contains pointers,
17  *  it's not as slow as copying the data in and out of the GOT.
18  *
19  
20 The ELF GOT layout is described in excruciating detail
21 by the Sun documentation, at 
22    Solaris 2.5 Software Developer AnswerBook >> 
23       Linker and Libraries Guide >> 
24         6 Object Files >> 
25           File Format
26
27 A more readable summary is at:  
28   http://www.iecc.com/linker/linker08.html
29
30  *
31  *  Developed by Sameer Kumar (sameer@ks.uiuc.edu) 8/25/03
32  *  Made functional by Orion Lawlor (olawlor@acm.org) 2003/9/16
33  *  Made working on AMD64 by Gengbin Zheng 12/20/2005
34  *
35  *  FIXME2: Sanity check. I am assuming that if a symbol is in the
36  *  relocation table it is in the global offset table. A sanity check
37  *  would be to get the address from the symbol table and look for it
38  *  in the GOT. Pointers to remote function calls may be an exception
39  *  to this.
40  */
41
42 #include "converse.h"
43 #include "cklists.h"
44 #include <string.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <errno.h>
48 #include <sys/types.h>
49 #if CMK_HAS_REGEX_H
50 #include <regex.h>
51 #endif
52 #include <vector>
53 #include <algorithm>
54 #include "converse.h"
55 #include "pup.h"
56
57 #if CMK_HAS_ELF_H
58 #include <elf.h>
59
60 #define DEBUG_GOT_MANAGER 1
61
62 #if !CMK_SHARED_VARS_UNAVAILABLE
63 #  error "Global-elfgot won't work properly under smp version: -swapglobals disabled"
64 #endif
65
66 CpvDeclare(int, CmiPICMethod);
67
68 #if CMK_AMD64
69 typedef Elf64_Addr    ELFXX_TYPE_Addr;
70 typedef Elf64_Dyn     ELFXX_TYPE_Dyn;
71 typedef Elf64_Rela    ELFXX_TYPE_Rel;
72 typedef Elf64_Sym     ELFXX_TYPE_Sym;
73 #define ELFXX_R_TYPE   ELF64_R_TYPE
74 #define ELFXX_R_SYM    ELF64_R_SYM
75 #define ELFXX_ST_TYPE  ELF64_ST_TYPE
76 #define CMK_DT_REL     DT_RELA
77 #define CMK_DT_RELSZ   DT_RELASZ
78 #define is_elf_global(x)  ((x) == R_X86_64_GLOB_DAT)
79 #elif CMK_IA64
80 #error "NOT SUPPORTED"
81 #else
82 typedef Elf32_Addr    ELFXX_TYPE_Addr;
83 typedef Elf32_Dyn     ELFXX_TYPE_Dyn;
84 typedef Elf32_Rel     ELFXX_TYPE_Rel;
85 typedef Elf32_Sym     ELFXX_TYPE_Sym;
86 #define ELFXX_R_TYPE   ELF32_R_TYPE
87 #define ELFXX_R_SYM    ELF32_R_SYM
88 #define ELFXX_ST_TYPE  ELF32_ST_TYPE
89 #define CMK_DT_REL     DT_REL
90 #define CMK_DT_RELSZ   DT_RELSZ
91 #define is_elf_global(x)  ((x) == R_386_GLOB_DAT)
92 #endif
93
94 extern ELFXX_TYPE_Dyn _DYNAMIC[];      //The Dynamic section table pointer
95
96
97 /**
98         Method to read blacklist of variables that should not be 
99         identified as global variables
100         **/
101
102 CkVec<char *>  _blacklist;
103 static int loaded = 0;
104
105 static void readBlacklist()
106 {
107   if (loaded) return;
108   const char *fname = "blacklist";
109   FILE *bl = fopen(fname, "r");
110   if (bl == NULL){
111                 if (CmiMyPe() == 0) printf("WARNING: Running swapglobals without blacklist, globals from libraries might be getting un-necessarily swapped\n");
112                 loaded = 1;
113                 return;
114   }
115   printf("Loading blacklist from file \"%s\" ... \n", fname);
116   while (!feof(bl)){
117     char name[512];
118     fscanf(bl, "%s\n", &name);
119      _blacklist.push_back(strdup(name));
120   }
121   fclose(bl);
122   loaded = 1;
123 }
124
125
126
127
128 /****************** Global Variable Understanding *********************/
129 /**
130  Keeps a list of global variables.
131 */
132 class CtgGlobalList
133 {
134   int datalen; ///< Number of bytes in the table of global data.
135   struct CtgRec {
136     ELFXX_TYPE_Addr *got; ///< Points to our entry in the GOT.
137     int off; ///< Our byte offset into the table of global data.
138     CtgRec() {got=NULL;}
139     CtgRec(ELFXX_TYPE_Addr *got_,int off_) :got(got_), off(off_) {}
140   };
141   CkVec<CtgRec> rec;
142   int nRec;
143 public:
144   /**
145    Analyze the current set of global variables, determine 
146    which are user globals and which are system globals, 
147    and store the list of user globals. 
148   */
149   CtgGlobalList();
150   
151   /// Return the number of bytes needed to store our global data.
152   inline int getSize(void) const {return datalen;}
153   
154   /// Copy the current set of global data into this set,
155   ///   which must be getSize() bytes.
156   void read(void *datav) const;
157   
158   /// Point at this set of global data (must be getSize() bytes).
159   inline void install(void *datav) const {
160     char *data=(char *)datav;
161     for (int i=0;i<nRec;i++)
162       *(rec[i].got)=(ELFXX_TYPE_Addr)(data+rec[i].off);
163   }
164   
165 private:
166   /* Return 1 if this is the name of a user global variable;
167      0 if this is the name of some other (Charm or system)
168      global variable. */
169   int isUserSymbol(const char *name);
170 };
171
172 int match(const char *string, const char *pattern) {
173 #if CMK_HAS_REGEX_H
174   int status;
175
176   regex_t re;
177   if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
178     CmiAbort("error compiling regex");
179   }
180   status = regexec(&re, string, (size_t) 0, NULL, 0);
181   regfree(&re);
182   if (status == 0) {
183     return 1;
184   } else if (status == REG_NOMATCH) {
185     return 0;
186   }
187   perror("error in match\n");
188   return 0;
189 #else
190   CmiPrinf("Warning: elfgot.C::match() is not implemented!\n");
191   return 0;
192 #endif
193 }
194
195 int CtgGlobalList::isUserSymbol(const char *name) {
196     // return 1;
197     if((strncmp("_", name, 1) == 0) || (strncmp("Cpv_", name, 4) == 0)
198        || (strncmp("Csv_", name, 4) == 0) || (strncmp("Ctv_", name, 4) == 0)
199        || (strncmp("Bnv_", name, 4) == 0) || (strncmp("Bpv_", name, 4) == 0)
200        || (strncmp("ckout", name, 5) == 0) || (strncmp("stdout", name, 6) == 0)
201        || (strncmp("environ", name, 7) == 0)
202        || (strncmp("stderr", name, 6) == 0) || (strncmp("stdin", name, 5) == 0)) {
203 #ifdef CMK_GFORTRAN
204         if (match(name, "__.*_MOD_.*")) return 1;
205 #endif
206         return 0;
207     }
208     
209         /**
210                 if the name is on the blacklist, it is not a user symbol
211         */
212         for(int i=0;i<_blacklist.size();i++){
213                 if(strlen(name) == strlen(_blacklist[i]) && strncmp(name,_blacklist[i],strlen(name)) == 0){
214                         return 0;
215                 }
216         }
217                 
218     return 1;
219 }
220
221 void CtgGlobalList::read(void *datav) const {
222     char *data=(char *)datav;
223     for (int i=0;i<nRec;i++) {
224       int size;
225       if (i<nRec-1) 
226         size=rec[i+1].off-rec[i].off;
227       else /* i==nRec-1, last one: */ 
228         size=datalen-rec[i].off;
229       memcpy(data+rec[i].off, (void *)*rec[i].got, size);
230     }
231 }
232
233 struct global_rec
234 {
235     ELFXX_TYPE_Addr * index;
236     size_t size;
237     global_rec(ELFXX_TYPE_Addr *index_, size_t size_)
238   : index(index_), size(size_) {}
239 };
240 static bool compare_globals(const global_rec &l, const global_rec &r)
241 {
242     return l.size < r.size;
243 }
244
245 /**
246    Analyze the current set of global variables, determine 
247    which are user globals and which are system globals, 
248    and store the list of user globals. 
249  */
250 CtgGlobalList::CtgGlobalList() {
251     datalen=0;
252     nRec=0;
253     
254     int count;
255     int relt_size = 0;
256     int type, symindx;
257     char *sym_name;
258     ELFXX_TYPE_Rel *relt=NULL;       //Relocation table
259     ELFXX_TYPE_Sym *symt=NULL;       //symbol table
260     char *str_tab=NULL;         //String table
261
262     // Find tables and sizes of tables from the dynamic segment table
263     for(count = 0; _DYNAMIC[count].d_tag != 0; ++count) {
264         switch(_DYNAMIC[count].d_tag) {
265             case CMK_DT_REL:
266                 relt = (ELFXX_TYPE_Rel *) _DYNAMIC[count].d_un.d_ptr;
267                 break;
268             case CMK_DT_RELSZ:
269                 relt_size = _DYNAMIC[count].d_un.d_val/ sizeof(ELFXX_TYPE_Rel);
270                 break;
271             case DT_SYMTAB:
272                 symt = (ELFXX_TYPE_Sym *) _DYNAMIC[count].d_un.d_ptr;
273                 break;
274             case DT_STRTAB:
275                 str_tab = (char *)_DYNAMIC[count].d_un.d_ptr;
276                 break;
277         }
278     }
279
280     std::vector<global_rec> globals;
281     int padding_old = 0, padding_new = 0;
282
283     // Figure out which relocation data entries refer to global data:
284     for(count = 0; count < relt_size; count ++) {
285         type = ELFXX_R_TYPE(relt[count].r_info);
286         symindx = ELFXX_R_SYM(relt[count].r_info);
287  
288         if(!is_elf_global(type))
289             continue; /* It's not global data */
290
291         sym_name = str_tab + symt[symindx].st_name;
292
293 #if DEBUG_GOT_MANAGER
294         printf("relt[%d]= %s: %d bytes, %p sym, R_==%d\n", count, sym_name, 
295                symt[symindx].st_size, (void *)symt[symindx].st_value, type);
296 #endif
297
298         if(ELFXX_ST_TYPE(symt[symindx].st_info) != STT_OBJECT &&
299            ELFXX_ST_TYPE(symt[symindx].st_info) != STT_NOTYPE
300 #if 0
301 #ifdef __INTEL_COMPILER
302           && ELFXX_ST_TYPE(symt[symindx].st_info) != STT_FUNC
303 #endif
304 #endif
305                  ) /* ? */
306             continue;
307
308         if(strcmp(sym_name, "_DYNAMIC") == 0 ||
309            strcmp(sym_name, "__gmon_start__") == 0 ||
310            strcmp(sym_name, "_GLOBAL_OFFSET_TABLE_") == 0)
311             continue; /* It's system data */
312
313         if (!isUserSymbol(sym_name))
314             continue;
315
316         // It's got the right name-- it's a user global
317         int size = symt[symindx].st_size;
318         int gSize = ALIGN8(size);
319         padding_old += gSize - size;
320         ELFXX_TYPE_Addr *gGot=(ELFXX_TYPE_Addr *)relt[count].r_offset;
321         globals.push_back(global_rec(gGot, size));
322
323 #if DEBUG_GOT_MANAGER
324         printf("   -> %s is a user global, of size %d, at %p\n",
325                sym_name, size, (void *)*gGot);
326 #endif
327         if ((void *)*gGot != (void *)symt[symindx].st_value)
328             CmiAbort("CtgGlobalList: symbol table and GOT address mismatch!\n");
329
330         rec.push_back(CtgRec(gGot,datalen));
331         datalen+=gSize;
332     }
333
334     // Lay out swapped globals as a structure in order of descending
335     // member size, to reduce padding.
336     // Potential optimization: pull small elements from the end to
337     // fill in `padding' space where possible.
338     size_t datalen2 = 0;
339     std::sort(globals.begin(), globals.end(), &compare_globals);
340     for (std::vector<global_rec>::iterator i = globals.begin(); i != globals.end(); ++i) {
341         short alignment = std::min(i->size, (size_t)16);
342         size_t padding = alignment>0?(datalen2 + alignment) % alignment:0;
343         size_t offset = datalen2 + padding;
344         padding_new += padding;
345         //rec.push_back(CtgRec(i->index, offset));
346         datalen2 = offset + i->size;
347     }
348
349     nRec=rec.size();
350
351 #if DEBUG_GOT_MANAGER   
352     printf("relt has %d entries, %d of which are user globals\n\n", 
353            relt_size, nRec);
354     printf("Traditional GOT layout takes %d (padding: %d), "
355            "sorted takes %lu (padding: %d).\n", 
356            datalen, padding_old, (unsigned long) datalen2, padding_new);
357 #endif
358 }
359
360 /****************** Global Variable Storage and Swapping *********************/
361 CpvStaticDeclare(CtgGlobals,_curCtg);
362
363 struct CtgGlobalStruct {
364 public:
365     /* This is set when our data is pointed to by the current GOT */
366     int installed;
367
368     /* Pointer to our global data segment. */
369     void *data_seg;  
370     int seg_size; /* size in bytes of data segment */
371     
372     void allocate(int size) {
373       seg_size=size;
374         /* global data segment need to be isomalloc */
375       if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
376         data_seg=CmiIsomalloc(seg_size);
377       else
378         data_seg=malloc(seg_size);
379     }
380     
381     CtgGlobalStruct(void) {
382       installed=0;
383       data_seg=0;
384     }
385     ~CtgGlobalStruct() {
386       if (data_seg) {
387         if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
388           CmiIsomallocFree(data_seg);
389         else
390           free(data_seg);
391         data_seg = NULL;
392       }
393     }
394     
395     void pup(PUP::er &p);
396 };
397
398 void CtgGlobalStruct::pup(PUP::er &p) {
399     p | seg_size;
400         /* global data segment need to be isomalloc pupped */
401     if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
402       CmiIsomallocPup(&p, &data_seg);
403     else {
404       if (p.isUnpacking()) allocate(seg_size);
405       p((char *)data_seg, seg_size);
406     }
407 }
408
409 /// Singleton object describing our global variables:
410 static CtgGlobalList *_ctgList=NULL;
411 /// Singleton object describing the original values for the globals.
412 static CtgGlobalStruct *_ctgListGlobals=NULL;
413
414 /** Initialize the globals support (called on each processor). */
415 void CtgInit(void) {
416         CpvInitialize(int, CmiPICMethod);
417         CpvAccess(CmiPICMethod) = 1;
418         CpvInitialize(CtgGlobal,_curCtg);
419         
420         if (!_ctgList) 
421         {
422         /*
423           First call on this node: parse out our globals:
424         */
425                 readBlacklist();
426                 CtgGlobalList *l=new CtgGlobalList;
427                 CtgGlobalStruct *g=new CtgGlobalStruct;
428                 if (CmiMyNode()==0) {
429                         CmiPrintf("CHARM> -swapglobals enabled\n");
430                 }
431                 
432                 g->allocate(l->getSize());
433                 l->read(g->data_seg);
434                 l->install(g->data_seg);
435                 _ctgList=l;
436                 _ctgListGlobals=g;
437         }
438         
439         CpvAccess(_curCtg)=_ctgListGlobals;
440 }
441
442 /** Copy the current globals into this new set */
443 CtgGlobals CtgCreate(void) {
444         CtgGlobalStruct *g=new CtgGlobalStruct;
445         g->allocate(_ctgList->getSize());
446         _ctgList->read(g->data_seg);
447         return g;
448 }
449 /** PUP this (not currently installed) globals set */
450 CtgGlobals CtgPup(pup_er pv, CtgGlobals g) {
451         PUP::er *p=(PUP::er *)pv;
452         if (p->isUnpacking()) g=new CtgGlobalStruct;
453         if (g->installed) 
454                 CmiAbort("CtgPup called on currently installed globals!\n");
455         g->pup(*p);
456         if (g->seg_size!=_ctgList->getSize())
457                 CmiAbort("CtgPup: global variable size changed during migration!\n");
458         return g;
459 }
460
461 /** Install this set of globals. If g==NULL, returns to original globals. */
462 void CtgInstall(CtgGlobals g) {
463         CtgGlobals *cur=&CpvAccess(_curCtg);
464         CtgGlobals oldG=*cur;
465         if (g==NULL) g=_ctgListGlobals;
466         if (g == oldG) return;
467         *cur=g;
468         oldG->installed=0;
469         _ctgList->install(g->data_seg);
470         g->installed=1;
471 }
472
473 /** Delete this (not currently installed) set of globals. */
474 void CtgFree(CtgGlobals g) {
475         if (g->installed) CmiAbort("CtgFree called on currently installed globals!\n");
476         delete g;
477 }
478
479 CtgGlobals CtgCurrentGlobals(void){
480         return CpvAccess(_curCtg);
481 }
482
483 void CtgInstall_var(CtgGlobals g, void *ptr) {}
484 void CtgUninstall_var(CtgGlobals g, void *ptr) {}
485
486 #else
487
488 #include "global-nop.c"
489
490 #endif