doc: Add serial to list of ci file reserved words
[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 0
61
62 #define UNPROTECT_GOT     1
63
64 #if UNPROTECT_GOT && CMK_HAS_MPROTECT
65 #include <sys/mman.h>
66 #if CMK_HAS_GETPAGESIZE
67 #include <unistd.h>
68 #endif
69 #endif
70
71 #ifdef __INTEL_COMPILER
72 #define ALIGN_GOT(x)       (long)((~15)&((x)+15))
73 #else
74 #define ALIGN_GOT(x)       ALIGN8(x)
75 #endif
76
77 #if !CMK_SHARED_VARS_UNAVAILABLE
78 #  error "Global-elfgot won't work properly under smp version: -swapglobals disabled"
79 #endif
80
81 CpvDeclare(int, CmiPICMethod);
82
83 #if CMK_AMD64
84 typedef Elf64_Addr    ELFXX_TYPE_Addr;
85 typedef Elf64_Dyn     ELFXX_TYPE_Dyn;
86 typedef Elf64_Rela    ELFXX_TYPE_Rel;
87 typedef Elf64_Sym     ELFXX_TYPE_Sym;
88 #define ELFXX_R_TYPE   ELF64_R_TYPE
89 #define ELFXX_R_SYM    ELF64_R_SYM
90 #define ELFXX_ST_TYPE  ELF64_ST_TYPE
91 #define CMK_DT_REL     DT_RELA
92 #define CMK_DT_RELSZ   DT_RELASZ
93 #define is_elf_global(x)  ((x) == R_X86_64_GLOB_DAT)
94 #elif CMK_IA64
95 #error "NOT SUPPORTED"
96 #else
97 typedef Elf32_Addr    ELFXX_TYPE_Addr;
98 typedef Elf32_Dyn     ELFXX_TYPE_Dyn;
99 typedef Elf32_Rel     ELFXX_TYPE_Rel;
100 typedef Elf32_Sym     ELFXX_TYPE_Sym;
101 #define ELFXX_R_TYPE   ELF32_R_TYPE
102 #define ELFXX_R_SYM    ELF32_R_SYM
103 #define ELFXX_ST_TYPE  ELF32_ST_TYPE
104 #define CMK_DT_REL     DT_REL
105 #define CMK_DT_RELSZ   DT_RELSZ
106 #define is_elf_global(x)  ((x) == R_386_GLOB_DAT)
107 #endif
108
109 extern ELFXX_TYPE_Dyn _DYNAMIC[];      //The Dynamic section table pointer
110
111
112 /**
113         Method to read blacklist of variables that should not be 
114         identified as global variables
115         **/
116
117 CkVec<char *>  _blacklist;
118 static int loaded = 0;
119
120 static void readBlacklist()
121 {
122   if (loaded) return;
123   const char *fname = "blacklist";
124   FILE *bl = fopen(fname, "r");
125   if (bl == NULL){
126                 if (CmiMyPe() == 0) printf("WARNING: Running swapglobals without blacklist, globals from libraries might be getting un-necessarily swapped\n");
127                 loaded = 1;
128                 return;
129   }
130   printf("Loading blacklist from file \"%s\" ... \n", fname);
131   while (!feof(bl)){
132     char name[512];
133     fscanf(bl, "%s\n", name);
134      _blacklist.push_back(strdup(name));
135   }
136   fclose(bl);
137   loaded = 1;
138 }
139
140
141
142
143 /****************** Global Variable Understanding *********************/
144 /**
145  Keeps a list of global variables.
146 */
147 class CtgGlobalList
148 {
149   int datalen; ///< Number of bytes in the table of global data.
150   struct CtgRec {
151     ELFXX_TYPE_Addr *got; ///< Points to our entry in the GOT.
152     int off; ///< Our byte offset into the table of global data.
153     CtgRec() {got=NULL;}
154     CtgRec(ELFXX_TYPE_Addr *got_,int off_) :got(got_), off(off_) {}
155   };
156   CkVec<CtgRec> rec;
157   int nRec;
158 public:
159   /**
160    Analyze the current set of global variables, determine 
161    which are user globals and which are system globals, 
162    and store the list of user globals. 
163   */
164   CtgGlobalList();
165   
166   /// Return the number of bytes needed to store our global data.
167   inline int getSize(void) const {return datalen;}
168   
169   /// Copy the current set of global data into this set,
170   ///   which must be getSize() bytes.
171   void read(void *datav) const;
172   
173   /// Point at this set of global data (must be getSize() bytes).
174   inline void install(void *datav) const {
175     char *data=(char *)datav;
176     for (int i=0;i<nRec;i++)
177       *(rec[i].got)=(ELFXX_TYPE_Addr)(data+rec[i].off);
178   }
179   
180 private:
181   /* Return 1 if this is the name of a user global variable;
182      0 if this is the name of some other (Charm or system)
183      global variable. */
184   int isUserSymbol(const char *name);
185 };
186
187 int match(const char *string, const char *pattern) {
188 #if CMK_HAS_REGEX_H
189   int status;
190
191   regex_t re;
192   if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
193     CmiAbort("error compiling regex");
194   }
195   status = regexec(&re, string, (size_t) 0, NULL, 0);
196   regfree(&re);
197   if (status == 0) {
198     return 1;
199   } else if (status == REG_NOMATCH) {
200     return 0;
201   }
202   perror("error in match\n");
203   return 0;
204 #else
205   CmiPrinf("Warning: elfgot.C::match() is not implemented!\n");
206   return 0;
207 #endif
208 }
209
210 int CtgGlobalList::isUserSymbol(const char *name) {
211     // return 1;
212     if((strncmp("_", name, 1) == 0) || (strncmp("Cpv_", name, 4) == 0)
213        || (strncmp("Csv_", name, 4) == 0) || (strncmp("Ctv_", name, 4) == 0)
214        || (strncmp("Bnv_", name, 4) == 0) || (strncmp("Bpv_", name, 4) == 0)
215        || (strncmp("ckout", name, 5) == 0) || (strncmp("stdout", name, 6) == 0)
216        || (strncmp("ckerr", name, 5) == 0)
217        || (strncmp("environ", name, 7) == 0)
218        || (strncmp("stderr", name, 6) == 0) || (strncmp("stdin", name, 5) == 0)) {
219 #ifdef CMK_GFORTRAN
220         if (match(name, "__.*_MOD_.*")) return 1;
221 #endif
222         return 0;
223     }
224     
225         /**
226                 if the name is on the blacklist, it is not a user symbol
227         */
228         for(int i=0;i<_blacklist.size();i++){
229                 if(strlen(name) == strlen(_blacklist[i]) && strncmp(name,_blacklist[i],strlen(name)) == 0){
230                         return 0;
231                 }
232         }
233                 
234     return 1;
235 }
236
237 void CtgGlobalList::read(void *datav) const {
238     char *data=(char *)datav;
239     for (int i=0;i<nRec;i++) {
240       int size;
241       if (i<nRec-1) 
242         size=rec[i+1].off-rec[i].off;
243       else /* i==nRec-1, last one: */ 
244         size=datalen-rec[i].off;
245       memcpy(data+rec[i].off, (void *)*rec[i].got, size);
246     }
247 }
248
249 /**
250    Analyze the current set of global variables, determine 
251    which are user globals and which are system globals, 
252    and store the list of user globals.
253  */
254 CtgGlobalList::CtgGlobalList() {
255     datalen=0;
256     nRec=0;
257     
258     int count;
259     int relt_size = 0;
260     int type, symindx;
261     char *sym_name;
262     ELFXX_TYPE_Rel *relt=NULL;       //Relocation table
263     ELFXX_TYPE_Sym *symt=NULL;       //symbol table
264     char *str_tab=NULL;         //String table
265
266     // Find tables and sizes of tables from the dynamic segment table
267     for(count = 0; _DYNAMIC[count].d_tag != 0; ++count) {
268         switch(_DYNAMIC[count].d_tag) {
269             case CMK_DT_REL:
270                 relt = (ELFXX_TYPE_Rel *) _DYNAMIC[count].d_un.d_ptr;
271                 break;
272             case CMK_DT_RELSZ:
273                 relt_size = _DYNAMIC[count].d_un.d_val/ sizeof(ELFXX_TYPE_Rel);
274                 break;
275             case DT_SYMTAB:
276                 symt = (ELFXX_TYPE_Sym *) _DYNAMIC[count].d_un.d_ptr;
277                 break;
278             case DT_STRTAB:
279                 str_tab = (char *)_DYNAMIC[count].d_un.d_ptr;
280                 break;
281         }
282     }
283
284     int padding = 0;
285
286 #if UNPROTECT_GOT && CMK_HAS_MPROTECT
287     size_t pagesize = CMK_MEMORY_PAGESIZE;
288 #if CMK_HAS_GETPAGESIZE
289     pagesize = getpagesize();
290 #endif
291 #endif
292
293     // Figure out which relocation data entries refer to global data:
294     for(count = 0; count < relt_size; count ++) {
295         type = ELFXX_R_TYPE(relt[count].r_info);
296         symindx = ELFXX_R_SYM(relt[count].r_info);
297  
298         if(!is_elf_global(type))
299             continue; /* It's not global data */
300
301         sym_name = str_tab + symt[symindx].st_name;
302
303 #if DEBUG_GOT_MANAGER
304         printf("relt[%d]= %s: %d bytes, %p sym, R_==%d\n", count, sym_name, 
305                symt[symindx].st_size, (void *)symt[symindx].st_value, type);
306 #endif
307
308         if(ELFXX_ST_TYPE(symt[symindx].st_info) != STT_OBJECT &&
309            ELFXX_ST_TYPE(symt[symindx].st_info) != STT_NOTYPE
310 #if 0
311 #ifdef __INTEL_COMPILER
312           && ELFXX_ST_TYPE(symt[symindx].st_info) != STT_FUNC
313 #endif
314 #endif
315                  ) /* ? */
316             continue;
317
318         if(strcmp(sym_name, "_DYNAMIC") == 0 ||
319            strcmp(sym_name, "__gmon_start__") == 0 ||
320            strcmp(sym_name, "_GLOBAL_OFFSET_TABLE_") == 0)
321             continue; /* It's system data */
322
323         if (!isUserSymbol(sym_name))
324             continue;
325
326         // It's got the right name-- it's a user global
327         int size = symt[symindx].st_size;
328         int gSize = ALIGN_GOT(size);
329         padding += gSize - size;
330         ELFXX_TYPE_Addr *gGot=(ELFXX_TYPE_Addr *)relt[count].r_offset;
331
332 #if DEBUG_GOT_MANAGER
333         printf("   -> %s is a user global, of size %d, at %p\n",
334                sym_name, size, (void *)*gGot);
335 #endif
336         if ((void *)*gGot != (void *)symt[symindx].st_value)
337             CmiAbort("CtgGlobalList: symbol table and GOT address mismatch!\n");
338
339 #if UNPROTECT_GOT && CMK_HAS_MPROTECT
340         static void *last = NULL;
341         void *pg = (void*)(((size_t)gGot) & ~(pagesize-1));
342         if (pg != last) {
343             mprotect(pg, pagesize, PROT_READ | PROT_WRITE);
344             last = pg;
345         }
346 #endif
347
348         rec.push_back(CtgRec(gGot,datalen));
349         datalen+=gSize;
350     }
351
352     nRec=rec.size();
353
354 #if DEBUG_GOT_MANAGER   
355     printf("relt has %d entries, %d of which are user globals\n\n", 
356            relt_size, nRec);
357     printf("Globals take %d bytes (padding bytes: %d)\n", datalen, padding);
358 #endif
359 }
360
361 /****************** Global Variable Storage and Swapping *********************/
362 CpvStaticDeclare(CtgGlobals,_curCtg);
363
364 struct CtgGlobalStruct {
365 public:
366     /* This is set when our data is pointed to by the current GOT */
367     int installed;
368
369     /* Pointer to our global data segment. */
370     void *data_seg;  
371     int seg_size; /* size in bytes of data segment */
372     
373     void allocate(int size, CthThread tid) {
374       seg_size=size;
375         /* global data segment need to be isomalloc */
376       if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
377         data_seg=CmiIsomalloc(seg_size,tid);
378       else
379         data_seg=malloc(seg_size);
380     }
381     
382     CtgGlobalStruct(void) {
383       installed=0;
384       data_seg=0;
385     }
386     ~CtgGlobalStruct() {
387       if (data_seg) {
388         if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
389         {
390 #if !CMK_USE_MEMPOOL_ISOMALLOC
391           CmiIsomallocFree(data_seg);
392 #endif
393         }
394         else
395           free(data_seg);
396         data_seg = NULL;
397       }
398     }
399     
400     void pup(PUP::er &p);
401 };
402
403 void CtgGlobalStruct::pup(PUP::er &p) {
404     p | seg_size;
405         /* global data segment need to be isomalloc pupped */
406     if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
407 #if CMK_USE_MEMPOOL_ISOMALLOC
408       pup_bytes(&p, &data_seg, sizeof(void*));
409 #else
410       CmiIsomallocPup(&p, &data_seg);
411 #endif
412       else {
413       if (p.isUnpacking()) allocate(seg_size, NULL);
414       p((char *)data_seg, seg_size);
415     }
416 }
417
418 /// Singleton object describing our global variables:
419 static CtgGlobalList *_ctgList=NULL;
420 /// Singleton object describing the original values for the globals.
421 static CtgGlobalStruct *_ctgListGlobals=NULL;
422
423 /** Initialize the globals support (called on each processor). */
424 void CtgInit(void) {
425         CpvInitialize(int, CmiPICMethod);
426         CpvAccess(CmiPICMethod) = 1;
427         CpvInitialize(CtgGlobal,_curCtg);
428         
429         if (!_ctgList) 
430         {
431         /*
432           First call on this node: parse out our globals:
433         */
434                 readBlacklist();
435                 CtgGlobalList *l=new CtgGlobalList;
436                 CtgGlobalStruct *g=new CtgGlobalStruct;
437                 if (CmiMyNode()==0) {
438                         CmiPrintf("Charm++> -swapglobals enabled\n");
439                 }
440                 
441                 g->allocate(l->getSize(), NULL);
442                 l->read(g->data_seg);
443                 l->install(g->data_seg);
444                 _ctgList=l;
445                 _ctgListGlobals=g;
446         }
447         
448         CpvAccess(_curCtg)=_ctgListGlobals;
449 }
450
451 /** Copy the current globals into this new set */
452 CtgGlobals CtgCreate(CthThread tid) {
453         CtgGlobalStruct *g=new CtgGlobalStruct;
454         g->allocate(_ctgList->getSize(), tid);
455         _ctgList->read(g->data_seg);
456         return g;
457 }
458 /** PUP this (not currently installed) globals set */
459 CtgGlobals CtgPup(pup_er pv, CtgGlobals g) {
460         PUP::er *p=(PUP::er *)pv;
461         if (p->isUnpacking()) g=new CtgGlobalStruct;
462         if (g->installed) 
463                 CmiAbort("CtgPup called on currently installed globals!\n");
464         g->pup(*p);
465         if (g->seg_size!=_ctgList->getSize())
466                 CmiAbort("CtgPup: global variable size changed during migration!\n");
467         return g;
468 }
469
470 /** Install this set of globals. If g==NULL, returns to original globals. */
471 void CtgInstall(CtgGlobals g) {
472         CtgGlobals *cur=&CpvAccess(_curCtg);
473         CtgGlobals oldG=*cur;
474         if (g==NULL) g=_ctgListGlobals;
475         if (g == oldG) return;
476         *cur=g;
477         oldG->installed=0;
478         _ctgList->install(g->data_seg);
479         g->installed=1;
480 }
481
482 /** Delete this (not currently installed) set of globals. */
483 void CtgFree(CtgGlobals g) {
484         if (g->installed) CmiAbort("CtgFree called on currently installed globals!\n");
485         delete g;
486 }
487
488 CtgGlobals CtgCurrentGlobals(void){
489         return CpvAccess(_curCtg);
490 }
491
492 void CtgInstall_var(CtgGlobals g, void *ptr) {}
493 void CtgUninstall_var(CtgGlobals g, void *ptr) {}
494
495 #else
496
497 #include "global-nop.c"
498
499 #endif