Commit cb97b80d authored by sewardj's avatar sewardj

[project @ 2001-08-29 15:12:21 by sewardj]

Make the PEi386 linker able to handle symbols in .bss sections
(almost) correctly:

* "Anonymous" bss sections (where something like
     static int a[10]
  lives) are given a calloc'd block of memory to live in.

* Publically visible bss-resident symbols, such as
    int a[10]
  are each given their own calloc'd block.  The normal
  way of things is for identically-named symbols in different
  modules to be commoned up and allocated the largest stated
  size of any of the symbols.  We don't do that -- if two different
  .o files each declared  int a[10],  you'll get two independent
  lumps of memory.  Hence "almost" above.

These calloc'd lumps (pseudo-bss spaces) are not added to the list
of sections extracted for the benefit of the closure-vs-itbl pointer
checks done by the garbage collector.  I don't think that matters.

These fixes need to be propagated to other formats too (viz, ELF).

This fixes the problem reported by ??? in which, on Windows,
   writeFile "foo" "bar" in GHCi gives a segfault.

This bug was so horrible to track down and fix that I have instituted
range checking in the relocation processing machinery.  The runtime
linker checks all locations it is patching to ensure they are within
a known segment before writing to them.  Hopefully this will pick up
any future problems.  The performance impact of this, assessed by
the time to start up GHCi, is unmeasureably small.
parent fd7763c5
/* -----------------------------------------------------------------------------
* $Id: Linker.c,v 1.59 2001/08/21 15:22:09 sewardj Exp $
* $Id: Linker.c,v 1.60 2001/08/29 15:12:21 sewardj Exp $
*
* (c) The GHC Team, 2000
*
......@@ -424,7 +424,7 @@ static OpenedDLL* opened_dlls = NULL;
char*
addDLL ( char* path, char* dll_name )
addDLL ( __attribute((unused)) char* path, char* dll_name )
{
# if defined(OBJFORMAT_ELF)
void *hdl;
......@@ -589,6 +589,7 @@ loadObj( char *path )
oc->symbols = NULL;
oc->sections = NULL;
oc->lochash = allocStrHashTable();
oc->proddables = NULL;
/* chain it onto the list of objects */
oc->next = objects;
......@@ -709,6 +710,39 @@ unloadObj( char *path )
return 0;
}
/* -----------------------------------------------------------------------------
* Sanity checking. For each ObjectCode, maintain a list of address ranges
* which may be prodded during relocation, and abort if we try and write
* outside any of these.
*/
static void addProddableBlock ( ObjectCode* oc, void* start, int size )
{
ProddableBlock* pb
= stgMallocBytes(sizeof(ProddableBlock), "addProddableBlock");
/* fprintf(stderr, "aPB %p %p %d\n", oc, start, size); */
ASSERT(size > 0);
pb->start = start;
pb->size = size;
pb->next = oc->proddables;
oc->proddables = pb;
}
static void checkProddableBlock ( ObjectCode* oc, void* addr )
{
ProddableBlock* pb;
for (pb = oc->proddables; pb != NULL; pb = pb->next) {
char* s = (char*)(pb->start);
char* e = s + pb->size - 1;
char* a = (char*)addr;
/* Assumes that the biggest fixup involves a 4-byte write. This
probably needs to be changed to 8 (ie, +7) on 64-bit
plats. */
if (a >= s && (a+3) <= e) return;
}
barf("checkProddableBlock: invalid fixup in runtime linker");
}
/* --------------------------------------------------------------------------
* PEi386 specifics (Win32 targets)
* ------------------------------------------------------------------------*/
......@@ -1036,13 +1070,15 @@ ocVerifyImage_PEi386 ( ObjectCode* oc )
" data sz %d\n"
" data off %d\n"
" num rel %d\n"
" off rel %d\n",
" off rel %d\n"
" ptr raw 0x%x\n",
sectab_i->VirtualSize,
sectab_i->VirtualAddress,
sectab_i->SizeOfRawData,
sectab_i->PointerToRawData,
sectab_i->NumberOfRelocations,
sectab_i->PointerToRelocations
sectab_i->PointerToRelocations,
sectab_i->PointerToRawData
);
reltab = (COFF_reloc*) (
((UChar*)(oc->image)) + sectab_i->PointerToRelocations
......@@ -1061,9 +1097,9 @@ ocVerifyImage_PEi386 ( ObjectCode* oc )
printName ( sym->Name, strtab -10 );
fprintf ( stderr, "'\n" );
}
fprintf ( stderr, "\n" );
}
fprintf ( stderr, "\n" );
fprintf ( stderr, "string table has size 0x%x\n", * (UInt32*)strtab );
fprintf ( stderr, "---START of string table---\n");
......@@ -1090,12 +1126,12 @@ ocVerifyImage_PEi386 ( ObjectCode* oc )
fprintf ( stderr,
"'\n"
" value 0x%x\n"
" sec# %d\n"
" 1+sec# %d\n"
" type 0x%x\n"
" sclass 0x%x\n"
" nAux %d\n",
symtab_i->Value,
(Int32)(symtab_i->SectionNumber) - 1,
(Int32)(symtab_i->SectionNumber),
(UInt32)symtab_i->Type,
(UInt32)symtab_i->StorageClass,
(UInt32)symtab_i->NumberOfAuxSymbols
......@@ -1134,6 +1170,25 @@ ocGetNames_PEi386 ( ObjectCode* oc )
+ hdr->PointerToSymbolTable
+ hdr->NumberOfSymbols * sizeof_COFF_symbol;
/* Allocate space for any (local, anonymous) .bss sections. */
for (i = 0; i < hdr->NumberOfSections; i++) {
UChar* zspace;
COFF_section* sectab_i
= (COFF_section*)
myindex ( sizeof_COFF_section, sectab, i );
if (0 != strcmp(sectab_i->Name, ".bss")) continue;
if (sectab_i->VirtualSize == 0) continue;
/* This is a non-empty .bss section. Allocate zeroed space for
it, and set its PointerToRawData field such that oc->image +
PointerToRawData == addr_of_zeroed_space. */
zspace = stgCallocBytes(1, sectab_i->VirtualSize,
"ocGetNames_PEi386(anonymous bss)");
sectab_i->PointerToRawData = ((UChar*)zspace) - ((UChar*)(oc->image));
addProddableBlock(oc, zspace, sectab_i->VirtualSize);
/* fprintf(stderr, "BSS section at 0x%x\n", zspace); */
}
/* Copy exported symbols into the ObjectCode. */
oc->n_symbols = hdr->NumberOfSymbols;
......@@ -1150,33 +1205,66 @@ ocGetNames_PEi386 ( ObjectCode* oc )
symtab_i = (COFF_symbol*)
myindex ( sizeof_COFF_symbol, symtab, i );
if (symtab_i->StorageClass == MYIMAGE_SYM_CLASS_EXTERNAL &&
symtab_i->SectionNumber != MYIMAGE_SYM_UNDEFINED) {
addr = NULL;
if (symtab_i->StorageClass == MYIMAGE_SYM_CLASS_EXTERNAL
&& symtab_i->SectionNumber != MYIMAGE_SYM_UNDEFINED) {
/* This symbol is global and defined, viz, exported */
COFF_section* sectabent;
/* cstring_from_COFF_symbol_name always succeeds. */
sname = cstring_from_COFF_symbol_name ( symtab_i->Name, strtab );
/* for MYIMAGE_SYMCLASS_EXTERNAL
&& !MYIMAGE_SYM_UNDEFINED,
the address of the symbol is:
address of relevant section + offset in section
*/
sectabent = (COFF_section*)
myindex ( sizeof_COFF_section,
sectab,
symtab_i->SectionNumber-1 );
COFF_section* sectabent
= (COFF_section*) myindex ( sizeof_COFF_section,
sectab,
symtab_i->SectionNumber-1 );
addr = ((UChar*)(oc->image))
+ (sectabent->PointerToRawData
+ symtab_i->Value);
}
else
if (symtab_i->SectionNumber == MYIMAGE_SYM_UNDEFINED
&& symtab_i->Value > 0) {
/* This symbol isn't in any section at all, ie, global bss.
Allocate zeroed space for it. */
addr = stgCallocBytes(1, symtab_i->Value,
"ocGetNames_PEi386(non-anonymous bss)");
addProddableBlock(oc, addr, symtab_i->Value);
}
if (addr != NULL) {
sname = cstring_from_COFF_symbol_name ( symtab_i->Name, strtab );
/* fprintf(stderr,"addSymbol %p `%s'\n", addr,sname); */
IF_DEBUG(linker, belch("addSymbol %p `%s'\n", addr,sname);)
ASSERT(i >= 0 && i < oc->n_symbols);
/* cstring_from_COFF_symbol_name always succeeds. */
oc->symbols[i] = sname;
insertStrHashTable(symhash, sname, addr);
} else {
# if 0
fprintf ( stderr,
"IGNORING symbol %d\n"
" name `",
i
);
printName ( symtab_i->Name, strtab );
fprintf ( stderr,
"'\n"
" value 0x%x\n"
" 1+sec# %d\n"
" type 0x%x\n"
" sclass 0x%x\n"
" nAux %d\n",
symtab_i->Value,
(Int32)(symtab_i->SectionNumber),
(UInt32)symtab_i->Type,
(UInt32)symtab_i->StorageClass,
(UInt32)symtab_i->NumberOfAuxSymbols
);
# endif
}
i += symtab_i->NumberOfAuxSymbols;
i++;
}
......@@ -1198,7 +1286,7 @@ ocGetNames_PEi386 ( ObjectCode* oc )
myindex ( sizeof_COFF_section, sectab, i );
IF_DEBUG(linker, belch("section name = %s\n", sectab_i->Name ));
#if 0
# if 0
/* I'm sure this is the Right Way to do it. However, the
alternative of testing the sectab_i->Name field seems to
work ok with Cygwin.
......@@ -1206,7 +1294,7 @@ ocGetNames_PEi386 ( ObjectCode* oc )
if (sectab_i->Characteristics & MYIMAGE_SCN_CNT_CODE ||
sectab_i->Characteristics & MYIMAGE_SCN_CNT_INITIALIZED_DATA)
kind = SECTIONKIND_CODE_OR_RODATA;
#endif
# endif
if (0==strcmp(".text",sectab_i->Name) ||
0==strcmp(".rodata",sectab_i->Name))
......@@ -1225,9 +1313,12 @@ ocGetNames_PEi386 ( ObjectCode* oc )
return 0;
}
oc->sections[i].start = start;
oc->sections[i].end = end;
oc->sections[i].kind = kind;
if (end >= start) {
oc->sections[i].start = start;
oc->sections[i].end = end;
oc->sections[i].kind = kind;
addProddableBlock(oc, start, end - start + 1);
}
}
return 1;
......@@ -1324,7 +1415,7 @@ ocResolve_PEi386 ( ObjectCode* oc )
return 0;
}
}
checkProddableBlock(oc, pP);
switch (reltab_j->Type) {
case MYIMAGE_REL_I386_DIR32:
*pP = A + S;
......
/* -----------------------------------------------------------------------------
* $Id: LinkerInternals.h,v 1.3 2001/02/28 10:03:42 sewardj Exp $
* $Id: LinkerInternals.h,v 1.4 2001/08/29 15:12:21 sewardj Exp $
*
* (c) The GHC Team, 2000
*
......@@ -22,6 +22,14 @@ typedef enum { SECTIONKIND_CODE_OR_RODATA,
typedef struct { void* start; void* end; SectionKind kind; }
Section;
typedef
struct _ProddableBlock {
void* start;
int size;
struct _ProddableBlock* next;
}
ProddableBlock;
/* Top-level structure for an object module. One of these is allocated
* for each object file in use.
*/
......@@ -50,6 +58,12 @@ typedef struct _ObjectCode {
/* Allow a chain of these things */
struct _ObjectCode * next;
/* SANITY CHECK ONLY: a list of the only memory regions which may
safely be prodded during relocation. Any attempt to prod
outside one of these is an error in the linker. */
ProddableBlock* proddables;
} ObjectCode;
extern ObjectCode *objects;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment