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