1 /* Library to manage the program's global offset table.
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.
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.
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.
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 >>
27 A more readable summary is at:
28 http://www.iecc.com/linker/linker08.html
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
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
44 /* conv-config.h, included in converse.h, defines CMI_SWAPGLOBALS.
45 * -swapglobals only works on ELF systems and in non-SMP mode. */
53 #include <sys/types.h>
61 #include "memory-isomalloc.h"
65 #define DEBUG_GOT_MANAGER 0
67 #define UNPROTECT_GOT 1
69 #if UNPROTECT_GOT && CMK_HAS_MPROTECT
71 #if CMK_HAS_GETPAGESIZE
76 #ifdef __INTEL_COMPILER
77 #define ALIGN_GOT(x) (long)((~15)&((x)+15))
79 #define ALIGN_GOT(x) ALIGN8(x)
82 CpvDeclare(int, CmiPICMethod);
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)
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)
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
116 std::vector<char *> _blacklist;
117 static bool loaded = false;
118 extern int quietModeRequested;
120 static void readBlacklist()
123 const char *fname = "blacklist";
124 FILE *bl = fopen(fname, "r");
126 if (!quietModeRequested && CmiMyPe() == 0) {
127 CmiPrintf("WARNING: Running swapglobals without blacklist, globals from libraries might be unnecessarily swapped.\n");
132 printf("Loading blacklist from file \"%s\" ... \n", fname);
135 if (fscanf(bl, "%511s\n", name) != 1) {
136 CmiAbort("Swapglobals> reading blacklist file failed!");
138 _blacklist.push_back(strdup(name));
147 /****************** Global Variable Understanding *********************/
149 Keeps a list of global variables.
153 size_t datalen; ///< Number of bytes in the table of global data.
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.
158 CtgRec(ELFXX_TYPE_Addr *got_,int off_) :got(got_), off(off_) {}
160 std::vector<CtgRec> rec;
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.
169 /// Return the number of bytes needed to store our global data.
170 inline size_t getSize(void) const {return datalen;}
172 /// Copy the current set of global data into this set,
173 /// which must be getSize() bytes.
174 void read(void *datav) const;
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);
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)
188 int isUserSymbol(const char *name);
191 int match(const char *string, const char *pattern) {
196 if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
197 CmiAbort("error compiling regex");
199 status = regexec(&re, string, (size_t) 0, NULL, 0);
203 } else if (status == REG_NOMATCH) {
206 perror("error in match\n");
209 CmiPrinf("Warning: elfgot.C::match() is not implemented!\n");
214 int CtgGlobalList::isUserSymbol(const char *name) {
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)) {
225 if (match(name, "__.*_MOD_.*")) return 1;
231 if the name is on the blacklist, it is not a user symbol
233 for(unsigned int i=0;i<_blacklist.size();i++){
234 if(strlen(name) == strlen(_blacklist[i]) && strncmp(name,_blacklist[i],strlen(name)) == 0){
242 void CtgGlobalList::read(void *datav) const {
243 char *data=(char *)datav;
244 int nRec = rec.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);
252 size = datalen-rec[nRec-1].off;
253 memcpy(data+rec[nRec-1].off, (void *)(uintptr_t)*rec[nRec-1].got, size);
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.
262 CtgGlobalList::CtgGlobalList() {
266 size_t relt_size = 0;
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) {
277 relt = (ELFXX_TYPE_Rel *) _DYNAMIC[count].d_un.d_ptr;
280 relt_size = _DYNAMIC[count].d_un.d_val/ sizeof(ELFXX_TYPE_Rel);
283 symt = (ELFXX_TYPE_Sym *) _DYNAMIC[count].d_un.d_ptr;
286 str_tab = (char *)_DYNAMIC[count].d_un.d_ptr;
293 #if UNPROTECT_GOT && CMK_HAS_MPROTECT
294 const size_t pagesize = CmiGetPageSize();
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);
312 if(ELFXX_ST_TYPE(symt[symindx].st_info) != STT_OBJECT &&
313 ELFXX_ST_TYPE(symt[symindx].st_info) != STT_NOTYPE
315 #ifdef __INTEL_COMPILER
316 && ELFXX_ST_TYPE(symt[symindx].st_info) != STT_FUNC
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))
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);
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));
347 mprotect(pg, pagesize, PROT_READ | PROT_WRITE);
352 rec.emplace_back(gGot, datalen);
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);
363 /****************** Global Variable Storage and Swapping *********************/
364 CpvStaticDeclare(CtgGlobals,_curCtg);
366 struct CtgGlobalStruct {
368 /* This is set when our data is pointed to by the current GOT */
371 /* Pointer to our global data segment. */
373 size_t seg_size; /* size in bytes of data segment */
375 void allocate(size_t size, CthThread tid) {
377 /* global data segment need to be isomalloc */
378 if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
379 data_seg=CmiIsomallocMallocForThread(tid, seg_size);
381 data_seg=malloc(seg_size);
384 CtgGlobalStruct(void) {
390 if (CmiMemoryIs(CMI_MEMORY_IS_ISOMALLOC))
392 #if !CMK_USE_MEMPOOL_ISOMALLOC
393 CmiIsomallocBlockListFree(data_seg);
402 void pup(PUP::er &p);
405 void CtgGlobalStruct::pup(PUP::er &p) {
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*));
412 CmiIsomallocPup(&p, &data_seg);
415 if (p.isUnpacking()) allocate(seg_size, NULL);
416 p((char *)data_seg, seg_size);
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). */
427 CpvInitialize(int, CmiPICMethod);
428 CpvAccess(CmiPICMethod) = CMI_PIC_ELFGOT;
429 CpvInitialize(CtgGlobals,_curCtg);
434 First call on this node: parse out our globals:
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");
444 g->allocate(l->getSize(), NULL);
445 l->read(g->data_seg);
446 l->install(g->data_seg);
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);
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;
466 CmiAbort("CtgPup called on currently installed globals!\n");
468 if (g->seg_size!=_ctgList->getSize())
469 CmiAbort("CtgPup: global variable size changed during migration!\n");
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;
480 oldG->installed=false;
481 _ctgList->install(g->data_seg);
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");
491 CtgGlobals CtgCurrentGlobals(void){
492 return CpvAccess(_curCtg);
495 #else // CMI_SWAPGLOBALS
497 #include "global-nop.C"