Commit c9aeb3c6 authored by wolfgang's avatar wolfgang
Browse files

[project @ 2004-09-27 15:55:25 by wolfgang]

GHCi support for PowerPC Linux, courtesy of Ryan Lortie <desrt@desrt.ca>.

MERGE TO STABLE
parent 305c762e
...@@ -96,6 +96,9 @@ ObjectCode *objects = NULL; /* initially empty */ ...@@ -96,6 +96,9 @@ ObjectCode *objects = NULL; /* initially empty */
static int ocVerifyImage_ELF ( ObjectCode* oc ); static int ocVerifyImage_ELF ( ObjectCode* oc );
static int ocGetNames_ELF ( ObjectCode* oc ); static int ocGetNames_ELF ( ObjectCode* oc );
static int ocResolve_ELF ( ObjectCode* oc ); static int ocResolve_ELF ( ObjectCode* oc );
#if defined(powerpc_TARGET_ARCH)
static int ocAllocateJumpIslands_ELF ( ObjectCode* oc );
#endif
#elif defined(OBJFORMAT_PEi386) #elif defined(OBJFORMAT_PEi386)
static int ocVerifyImage_PEi386 ( ObjectCode* oc ); static int ocVerifyImage_PEi386 ( ObjectCode* oc );
static int ocGetNames_PEi386 ( ObjectCode* oc ); static int ocGetNames_PEi386 ( ObjectCode* oc );
...@@ -1097,6 +1100,9 @@ loadObj( char *path ) ...@@ -1097,6 +1100,9 @@ loadObj( char *path )
# if defined(OBJFORMAT_MACHO) # if defined(OBJFORMAT_MACHO)
r = ocAllocateJumpIslands_MachO ( oc ); r = ocAllocateJumpIslands_MachO ( oc );
if (!r) { return r; } if (!r) { return r; }
# elif defined(OBJFORMAT_ELF) && defined(powerpc_TARGET_ARCH)
r = ocAllocateJumpIslands_ELF ( oc );
if (!r) { return r; }
#endif #endif
/* verify the in-memory image */ /* verify the in-memory image */
...@@ -1264,6 +1270,112 @@ static void addSection ( ObjectCode* oc, SectionKind kind, ...@@ -1264,6 +1270,112 @@ static void addSection ( ObjectCode* oc, SectionKind kind,
} }
/* --------------------------------------------------------------------------
* PowerPC specifics (jump islands)
* ------------------------------------------------------------------------*/
#if defined(powerpc_TARGET_ARCH)
/*
ocAllocateJumpIslands
Allocate additional space at the end of the object file image to make room
for jump islands.
PowerPC relative branch instructions have a 24 bit displacement field.
As PPC code is always 4-byte-aligned, this yields a +-32MB range.
If a particular imported symbol is outside this range, we have to redirect
the jump to a short piece of new code that just loads the 32bit absolute
address and jumps there.
This function just allocates space for one 16 byte ppcJumpIsland for every
undefined symbol in the object file. The code for the islands is filled in by
makeJumpIsland below.
*/
static int ocAllocateJumpIslands( ObjectCode* oc, int count, int first )
{
int aligned;
if( count > 0 )
{
#ifdef USE_MMAP
#error ocAllocateJumpIslands doesnt want USE_MMAP to be defined
#endif
// round up to the nearest 4
aligned = (oc->fileSize + 3) & ~3;
oc->image = stgReallocBytes( oc->image,
aligned + sizeof( ppcJumpIsland ) * count,
"ocAllocateJumpIslands" );
oc->jump_islands = (ppcJumpIsland *) (((char *) oc->image) + aligned);
memset( oc->jump_islands, 0, sizeof( ppcJumpIsland ) * count );
}
else
oc->jump_islands = NULL;
oc->island_start_symbol = first;
oc->n_islands = count;
return 1;
}
static unsigned long makeJumpIsland( ObjectCode* oc,
unsigned long symbolNumber,
unsigned long target )
{
ppcJumpIsland *island;
if( symbolNumber < oc->island_start_symbol ||
symbolNumber - oc->island_start_symbol > oc->n_islands)
return 0;
island = &oc->jump_islands[symbolNumber - oc->island_start_symbol];
// lis r12, hi16(target)
island->lis_r12 = 0x3d80;
island->hi_addr = target >> 16;
// ori r12, r12, lo16(target)
island->ori_r12_r12 = 0x618c;
island->lo_addr = target & 0xffff;
// mtctr r12
island->mtctr_r12 = 0x7d8903a6;
// bctr
island->bctr = 0x4e800420;
return (unsigned long) island;
}
/*
ocFlushInstructionCache
Flush the data & instruction caches.
Because the PPC has split data/instruction caches, we have to
do that whenever we modify code at runtime.
*/
static void ocFlushInstructionCache( ObjectCode *oc )
{
int n = (oc->fileSize + sizeof( ppcJumpIsland ) * oc->n_islands + 3) / 4;
unsigned long *p = (unsigned long *) oc->image;
while( n-- )
{
__asm__ volatile ( "dcbf 0,%0\n\t"
"sync\n\t"
"icbi 0,%0"
:
: "r" (p)
);
p++;
}
__asm__ volatile ( "sync\n\t"
"isync"
);
}
#endif
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* PEi386 specifics (Win32 targets) * PEi386 specifics (Win32 targets)
...@@ -2351,6 +2463,7 @@ ocVerifyImage_ELF ( ObjectCode* oc ) ...@@ -2351,6 +2463,7 @@ ocVerifyImage_ELF ( ObjectCode* oc )
#ifdef EM_IA_64 #ifdef EM_IA_64
case EM_IA_64: IF_DEBUG(linker,debugBelch( "ia64" )); break; case EM_IA_64: IF_DEBUG(linker,debugBelch( "ia64" )); break;
#endif #endif
case EM_PPC: IF_DEBUG(linker,debugBelch( "powerpc32" )); break;
default: IF_DEBUG(linker,debugBelch( "unknown" )); default: IF_DEBUG(linker,debugBelch( "unknown" ));
errorBelch("%s: unknown architecture", oc->fileName); errorBelch("%s: unknown architecture", oc->fileName);
return 0; return 0;
...@@ -2748,7 +2861,7 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC, ...@@ -2748,7 +2861,7 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
target_shndx, symtab_shndx )); target_shndx, symtab_shndx ));
for (j = 0; j < nent; j++) { for (j = 0; j < nent; j++) {
#if defined(DEBUG) || defined(sparc_TARGET_ARCH) || defined(ia64_TARGET_ARCH) #if defined(DEBUG) || defined(sparc_TARGET_ARCH) || defined(ia64_TARGET_ARCH) || defined(powerpc_TARGET_ARCH)
/* This #ifdef only serves to avoid unused-var warnings. */ /* This #ifdef only serves to avoid unused-var warnings. */
Elf_Addr offset = rtab[j].r_offset; Elf_Addr offset = rtab[j].r_offset;
Elf_Addr P = targ + offset; Elf_Addr P = targ + offset;
...@@ -2764,6 +2877,8 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC, ...@@ -2764,6 +2877,8 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
# elif defined(ia64_TARGET_ARCH) # elif defined(ia64_TARGET_ARCH)
Elf64_Xword *pP = (Elf64_Xword *)P; Elf64_Xword *pP = (Elf64_Xword *)P;
Elf_Addr addr; Elf_Addr addr;
# elif defined(powerpc_TARGET_ARCH)
Elf_Sword delta;
# endif # endif
IF_DEBUG(linker,debugBelch( "Rel entry %3d is raw(%6p %6p %6p) ", IF_DEBUG(linker,debugBelch( "Rel entry %3d is raw(%6p %6p %6p) ",
...@@ -2881,6 +2996,46 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC, ...@@ -2881,6 +2996,46 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC,
/* This goes with R_IA64_LTOFF22X and points to the load to /* This goes with R_IA64_LTOFF22X and points to the load to
* convert into a move. We don't implement relaxation. */ * convert into a move. We don't implement relaxation. */
break; break;
# elif defined(powerpc_TARGET_ARCH)
case R_PPC_ADDR16_LO:
*(Elf32_Half*) P = value;
break;
case R_PPC_ADDR16_HI:
*(Elf32_Half*) P = value >> 16;
break;
case R_PPC_ADDR16_HA:
*(Elf32_Half*) P = (value + 0x8000) >> 16;
break;
case R_PPC_ADDR32:
*(Elf32_Word *) P = value;
break;
case R_PPC_REL32:
*(Elf32_Word *) P = value - P;
break;
case R_PPC_REL24:
delta = value - P;
if( delta << 6 >> 6 != delta )
{
value = makeJumpIsland( oc, ELF_R_SYM(info), value );
delta = value - P;
if( value == 0 || delta << 6 >> 6 != delta )
{
barf( "Unable to make ppcJumpIsland for #%d",
ELF_R_SYM(info) );
return 0;
}
}
*(Elf_Word *) P = (*(Elf_Word *) P & 0xfc000003)
| (delta & 0x3fffffc);
break;
# endif # endif
default: default:
errorBelch("%s: unhandled ELF relocation(RelA) type %d\n", errorBelch("%s: unhandled ELF relocation(RelA) type %d\n",
...@@ -2941,6 +3096,10 @@ ocResolve_ELF ( ObjectCode* oc ) ...@@ -2941,6 +3096,10 @@ ocResolve_ELF ( ObjectCode* oc )
freeHashTable(oc->lochash, NULL); freeHashTable(oc->lochash, NULL);
oc->lochash = NULL; oc->lochash = NULL;
#if defined(powerpc_TARGET_ARCH)
ocFlushInstructionCache( oc );
#endif
return 1; return 1;
} }
...@@ -3036,6 +3195,44 @@ ia64_reloc_pcrel21(Elf_Addr target, Elf_Addr value, ObjectCode *oc) ...@@ -3036,6 +3195,44 @@ ia64_reloc_pcrel21(Elf_Addr target, Elf_Addr value, ObjectCode *oc)
#endif /* ia64 */ #endif /* ia64 */
/*
* PowerPC ELF specifics
*/
#ifdef powerpc_TARGET_ARCH
static int ocAllocateJumpIslands_ELF( ObjectCode *oc )
{
Elf_Ehdr *ehdr;
Elf_Shdr* shdr;
int i;
ehdr = (Elf_Ehdr *) oc->image;
shdr = (Elf_Shdr *) ( ((char *)oc->image) + ehdr->e_shoff );
for( i = 0; i < ehdr->e_shnum; i++ )
if( shdr[i].sh_type == SHT_SYMTAB )
break;
if( i == ehdr->e_shnum )
{
errorBelch( "This ELF file contains no symtab" );
return 0;
}
if( shdr[i].sh_entsize != sizeof( Elf_Sym ) )
{
errorBelch( "The entry size (%d) of the symtab isn't %d\n",
shdr[i].sh_entsize, sizeof( Elf_Sym ) );
return 0;
}
return ocAllocateJumpIslands( oc, shdr[i].sh_size / sizeof( Elf_Sym ), 0 );
}
#endif /* powerpc */
#endif /* ELF */ #endif /* ELF */
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
...@@ -3054,57 +3251,25 @@ ia64_reloc_pcrel21(Elf_Addr target, Elf_Addr value, ObjectCode *oc) ...@@ -3054,57 +3251,25 @@ ia64_reloc_pcrel21(Elf_Addr target, Elf_Addr value, ObjectCode *oc)
*) add still more sanity checks. *) add still more sanity checks.
*/ */
/*
ocAllocateJumpIslands_MachO
Allocate additional space at the end of the object file image to make room
for jump islands.
PowerPC relative branch instructions have a 24 bit displacement field.
As PPC code is always 4-byte-aligned, this yields a +-32MB range.
If a particular imported symbol is outside this range, we have to redirect
the jump to a short piece of new code that just loads the 32bit absolute
address and jumps there.
This function just allocates space for one 16 byte jump island for every
undefined symbol in the object file. The code for the islands is filled in by
makeJumpIsland below.
*/
static const int islandSize = 16;
static int ocAllocateJumpIslands_MachO(ObjectCode* oc) static int ocAllocateJumpIslands_MachO(ObjectCode* oc)
{ {
char *image = (char*) oc->image; struct mach_header *header = (struct mach_header *) oc->image;
struct mach_header *header = (struct mach_header*) image; struct load_command *lc = (struct load_command *) (header + 1);
struct load_command *lc = (struct load_command*) (image + sizeof(struct mach_header));
unsigned i; unsigned i;
for(i=0;i<header->ncmds;i++) for( i = 0; i < header->ncmds; i++ )
{ {
if(lc->cmd == LC_DYSYMTAB) if( lc->cmd == LC_DYSYMTAB )
{ {
struct dysymtab_command *dsymLC = (struct dysymtab_command*) lc; struct dysymtab_command *dsymLC = (struct dysymtab_command *) lc;
unsigned long nundefsym = dsymLC->nundefsym;
oc->island_start_symbol = dsymLC->iundefsym;
oc->n_islands = nundefsym;
if(nundefsym > 0) if( !ocAllocateJumpIslands( oc, dsymLC->nundefsym,
{ dsymLC->iundefsym ) )
#ifdef USE_MMAP return 0;
#error ocAllocateJumpIslands_MachO doesnt want USE_MMAP to be defined
#else
oc->image = stgReallocBytes(
image, oc->fileSize + islandSize * nundefsym,
"ocAllocateJumpIslands_MachO");
#endif
oc->jump_islands = oc->image + oc->fileSize;
memset(oc->jump_islands, 0, islandSize * nundefsym);
}
break; // there can be only one LC_DSYMTAB break; // there can be only one LC_DSYMTAB
} }
lc = (struct load_command *) ( ((char*)lc) + lc->cmdsize ); lc = (struct load_command *) ( ((char *)lc) + lc->cmdsize );
} }
return 1; return 1;
} }
...@@ -3152,31 +3317,6 @@ static int resolveImports( ...@@ -3152,31 +3317,6 @@ static int resolveImports(
return 1; return 1;
} }
static void* makeJumpIsland(
ObjectCode* oc,
unsigned long symbolNumber,
void* target)
{
if(symbolNumber < oc->island_start_symbol ||
symbolNumber - oc->island_start_symbol > oc->n_islands)
return NULL;
symbolNumber -= oc->island_start_symbol;
void *island = (void*) ((char*)oc->jump_islands + islandSize * symbolNumber);
unsigned long *p = (unsigned long*) island;
// lis r12, hi16(target)
*p++ = 0x3d800000 | ( ((unsigned long) target) >> 16 );
// ori r12, r12, lo16(target)
*p++ = 0x618c0000 | ( ((unsigned long) target) & 0xFFFF );
// mtctr r12
*p++ = 0x7d8903a6;
// bctr
*p++ = 0x4e800420;
return (void*) island;
}
static char* relocateAddress( static char* relocateAddress(
ObjectCode* oc, ObjectCode* oc,
int nSections, int nSections,
...@@ -3380,7 +3520,7 @@ static int relocateSection( ...@@ -3380,7 +3520,7 @@ static int relocateSection(
{ {
ASSERT(word == 0); ASSERT(word == 0);
word = symbolAddress; word = symbolAddress;
jumpIsland = (long) makeJumpIsland(oc,reloc->r_symbolnum,(void*)word); jumpIsland = makeJumpIsland(oc,reloc->r_symbolnum,word);
word -= ((long)image) + sect->offset + reloc->r_address; word -= ((long)image) + sect->offset + reloc->r_address;
if(jumpIsland != 0) if(jumpIsland != 0)
{ {
...@@ -3626,22 +3766,10 @@ static int ocResolve_MachO(ObjectCode* oc) ...@@ -3626,22 +3766,10 @@ static int ocResolve_MachO(ObjectCode* oc)
freeHashTable(oc->lochash, NULL); freeHashTable(oc->lochash, NULL);
oc->lochash = NULL; oc->lochash = NULL;
/* #if defined (powerpc_TARGET_ARCH)
Flush the data & instruction caches. ocFlushInstructionCache( oc );
Because the PPC has split data/instruction caches, we have to #endif
do that whenever we modify code at runtime.
*/
{
int n = (oc->fileSize + islandSize * oc->n_islands) / 4;
unsigned long *p = (unsigned long*)oc->image;
while(n--)
{
__asm__ volatile ("dcbf 0,%0\n\tsync\n\ticbi 0,%0"
: : "r" (p));
p++;
}
__asm__ volatile ("sync\n\tisync");
}
return 1; return 1;
} }
......
...@@ -36,6 +36,18 @@ typedef ...@@ -36,6 +36,18 @@ typedef
} }
ProddableBlock; ProddableBlock;
/* Jump Islands are sniplets of machine code required for relative
* address relocations on the PowerPC.
*/
#ifdef powerpc_TARGET_ARCH
typedef struct {
short lis_r12, hi_addr;
short ori_r12_r12, lo_addr;
long mtctr_r12;
long bctr;
} ppcJumpIsland;
#endif
/* Top-level structure for an object module. One of these is allocated /* Top-level structure for an object module. One of these is allocated
* for each object file in use. * for each object file in use.
*/ */
...@@ -76,8 +88,8 @@ typedef struct _ObjectCode { ...@@ -76,8 +88,8 @@ typedef struct _ObjectCode {
unsigned int pltIndex; unsigned int pltIndex;
#endif #endif
#ifdef darwin_TARGET_OS #ifdef powerpc_TARGET_ARCH
char* jump_islands; ppcJumpIsland *jump_islands;
unsigned long island_start_symbol; unsigned long island_start_symbol;
unsigned long n_islands; unsigned long n_islands;
#endif #endif
......
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