On 10/22/11 06:24 PM, Ben Gamari wrote:
On Sat, 22 Oct 2011 16:58:18 +0200, Karel Gardas<[email protected]>
wrote:
Hi Ben,
first of all, thank you very much for your progress. No, I don't see
crash, but in fact infinite loop. As far as I understand the linker
message '..linking...done' is printed when all the relocations are
processed -- so it does not necessarily mean that all the relocations
are processed correctly. My bet is that some of your relocation
processing might not be correct.
Definitely. As I noted, I'm still not very confident that the relocation
code is correct. That being said, just getting to the point where it
doesn't branch to the middle of nowhere is quite a milestone.
It is indeed! Now it would be great if we just make it kind of working
at the end. :-)
If you don't get to this sooner (or any other interested developer),
then in about 2 weeks I'll check your code to see if I see any obvious
problem with your implementation in comparison with what ARM suggest in
their ELF doc...
Thanks! Another set of eyes would definitely be helpful. I'll try taking
a look at it again sometime this weekend. It would be really nice if you
could figure out where it's getting stuck. Breaking in GDB will give you
the address. I have a patch which adds debugging output to all of the
relocation paths, so in principle you can identify what type of
relocation, if any, produced the code in question. Thanks again!
This patch might be useful indeed. While thinking about this on
background over past few days I've got an idea that you probably forgot
to flush caches. Do not forget ARM provides divided data and isns caches
and while you modify data in d-cache it does not necessarily propagate
to the i-cache. Good reading about this is here[1]. Looking into the
code proves this too. I added some code to support cache flushing but
I'm neither sure this is 100% correct yet. Anyway, now I get nice
segfaults instead of infinite loop. I've copied your changes to the
GHC-HEAD so attached patch contains all your changes and mine. Sorry for
that, but by searching for ocFlush you will find my changes quickly.
BTW: Simon Marlow provided nice advice in email thread "Re: Ghci
segfault on powerpc-linux" how to debug GHC linker. Basically its about:
"I don't know anything about PPC, but I might be able to give you
general help with debugging the linker. To start with you want to
compile stage 2 with debugging turned on:
$ cd ghc
$ rm stage2/build/tmp/ghc-stage2
$ make 2 GhcDebugged=YES
then run it with +RTS -Dl to get linker debug output (direct the output
to a file, there will be lots of it)."
I've used this here and got 90M debugging file. IMHO this might be quite
good for debugging on your side to see if you do relocations correctly.
On my side I'm still way behind you and so I'll try to understand ARM
ELF relocations and then review all your code first...
Thanks,
Karel
[1]:
http://blogs.arm.com/software-enablement/141-caches-and-self-modifying-code/
diff --git a/rts/Linker.c b/rts/Linker.c
index f5b90d4..abeeba9 100644
--- a/rts/Linker.c
+++ b/rts/Linker.c
@@ -144,7 +144,7 @@ static ObjectCode* mkOc( char *path, char *image, int
imageSize,
static int ocVerifyImage_ELF ( ObjectCode* oc );
static int ocGetNames_ELF ( ObjectCode* oc );
static int ocResolve_ELF ( ObjectCode* oc );
-#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
+#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) ||
defined(arm_HOST_ARCH)
static int ocAllocateSymbolExtras_ELF ( ObjectCode* oc );
#endif
#elif defined(OBJFORMAT_PEi386)
@@ -2219,7 +2219,7 @@ loadOc( ObjectCode* oc ) {
IF_DEBUG(linker, debugBelch("loadOc: ocAllocateSymbolExtras_MachO
failed\n"));
return r;
}
-# elif defined(OBJFORMAT_ELF) && (defined(powerpc_HOST_ARCH) ||
defined(x86_64_HOST_ARCH))
+# elif defined(OBJFORMAT_ELF) && (defined(powerpc_HOST_ARCH) ||
defined(x86_64_HOST_ARCH) || defined(arm_HOST_ARCH))
r = ocAllocateSymbolExtras_ELF ( oc );
if (!r) {
IF_DEBUG(linker, debugBelch("loadOc: ocAllocateSymbolExtras_ELF
failed\n"));
@@ -2424,13 +2424,13 @@ addSection ( ObjectCode* oc, SectionKind kind,
* them right next to the object code itself.
*/
-#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
+#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) ||
defined(arm_HOST_ARCH)
/*
ocAllocateSymbolExtras
Allocate additional space at the end of the object file image to make room
- for jump islands (powerpc, x86_64) and GOT entries (x86_64).
+ for jump islands (powerpc, x86_64, arm) and GOT entries (x86_64).
PowerPC relative branch instructions have a 24 bit displacement field.
As PPC code is always 4-byte-aligned, this yields a +-32MB range.
@@ -2503,6 +2503,10 @@ static int ocAllocateSymbolExtras( ObjectCode* oc, int
count, int first )
return 1;
}
+#endif
+
+#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
+
static SymbolExtra* makeSymbolExtra( ObjectCode* oc,
unsigned long symbolNumber,
unsigned long target )
@@ -2530,7 +2534,7 @@ static SymbolExtra* makeSymbolExtra( ObjectCode* oc,
extra->jumpIsland.bctr = 0x4e800420;
#endif
#ifdef x86_64_HOST_ARCH
- // jmp *-14(%rip)
+ // jmp *-14(%rip)
static uint8_t jmp[] = { 0xFF, 0x25, 0xF2, 0xFF, 0xFF, 0xFF };
extra->addr = target;
memcpy(extra->jumpIsland, jmp, 6);
@@ -2539,7 +2543,73 @@ static SymbolExtra* makeSymbolExtra( ObjectCode* oc,
return extra;
}
-#endif
+#endif // defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
+
+#ifdef arm_HOST_ARCH
+static SymbolExtra* makeArmSymbolExtra( ObjectCode* oc,
+ unsigned long symbolNumber,
+ unsigned long target,
+ int fromThumb,
+ int toThumb )
+{
+ SymbolExtra *extra;
+
+ ASSERT( symbolNumber >= oc->first_symbol_extra
+ && symbolNumber - oc->first_symbol_extra < oc->n_symbol_extras);
+
+ extra = &oc->symbol_extras[symbolNumber - oc->first_symbol_extra];
+
+ // Make sure instruction mode bit is set properly
+ if (toThumb)
+ target |= 1;
+ else
+ target &= ~1;
+
+ if (!fromThumb) {
+ // In ARM encoding:
+ // movw r12, #0
+ // movt r12, #0
+ // bkpt #2
+ // bx r12
+ uint32_t code[] = { 0xe300c000, 0xe340c000, 0xe12fff1c };
+
+ // Patch lower half-word into movw
+ code[0] |= ((target>>12) & 0xf) << 16;
+ code[0] |= target & 0xfff;
+ // Patch upper half-word into movt
+ target >>= 16;
+ code[1] |= ((target>>12) & 0xf) << 16;
+ code[1] |= target & 0xfff;
+
+ memcpy(extra->jumpIsland, code, 12);
+
+ } else {
+ // In Thumb encoding:
+ // movw r12, #0
+ // movt r12, #0
+ // bx r12
+ static uint16_t code[] = { 0xf240, 0x0c00,
+ 0xf2c0, 0x0c00,
+ 0x4760 };
+
+ // Patch lower half-word into movw
+ code[0] |= ((target>>11) & 0x1) << 10;
+ code[0] |= (target>>12) & 0xf;
+ code[1] |= ((target>>8) & 0x7) << 12;
+ code[1] |= target & 0xff;
+ // Patch upper half-word into movt
+ target >>= 16;
+ code[2] |= ((target>>11) & 0x1) << 10;
+ code[2] |= (target>>12) & 0xf;
+ code[3] |= ((target>>8) & 0x7) << 12;
+ code[3] |= target & 0xff;
+
+ memcpy(extra->jumpIsland, code, 10);
+ }
+
+ return extra;
+}
+#endif // arm_HOST_ARCH
/* --------------------------------------------------------------------------
* PowerPC specifics (instruction cache flushing)
@@ -2590,6 +2660,41 @@ ocFlushInstructionCache( ObjectCode *oc )
}
#endif /* powerpc_HOST_ARCH */
+/* --------------------------------------------------------------------------
+ * ARM specifics (instruction cache flushing)
+ * ------------------------------------------------------------------------*/
+
+#ifdef arm_HOST_ARCH
+/*
+ ocFlushInstructionCache
+
+ Flush the data & instruction caches.
+ Because the ARM has split data/instruction caches, we have to
+ do that whenever we modify code at runtime.
+ */
+
+static void
+ocFlushInstructionCacheFrom(void* begin, void* end)
+{
+#ifdef linux_HOST_OS
+ __clear_cache(begin, end);
+#else
+#error "ARM cache flushing is not implemented for your OS"
+#endif
+}
+
+static void
+ocFlushInstructionCache( ObjectCode *oc )
+{
+ /* The main object code */
+ ocFlushInstructionCacheFrom(oc->image
+ , oc->image + oc->fileSize);
+
+ /* Jump Islands */
+ ocFlushInstructionCacheFrom(oc->symbol_extras, oc->symbol_extras +
(sizeof(SymbolExtra) * oc->n_symbol_extras));
+}
+#endif /* arm_HOST_ARCH */
+
/* --------------------------------------------------------------------------
* PEi386 specifics (Win32 targets)
@@ -3535,6 +3640,44 @@ ocResolve_PEi386 ( ObjectCode* oc )
# define R_X86_64_PC64 24
# endif
+/*
+ * Workaround for glibc implementations (e.g. eglibc) with incomplete
+ * relocation lists
+ */
+#ifndef R_ARM_THM_CALL
+# define R_ARM_THM_CALL 10
+#endif
+#ifndef R_ARM_CALL
+# define R_ARM_CALL 28
+#endif
+#ifndef R_ARM_JUMP24
+# define R_ARM_JUMP24 29
+#endif
+#ifndef R_ARM_THM_JUMP24
+# define R_ARM_THM_JUMP24 30
+#endif
+#ifndef R_ARM_TARGET1
+# define R_ARM_TARGET1 38
+#endif
+#ifndef R_ARM_MOVW_ABS_NC
+# define R_ARM_MOVW_ABS_NC 43
+#endif
+#ifndef R_ARM_MOVT_ABS
+# define R_ARM_MOVT_ABS 44
+#endif
+#ifndef R_ARM_THM_MOVW_ABS_NC
+# define R_ARM_THM_MOVW_ABS_NC 47
+#endif
+#ifndef R_ARM_THM_MOVT_ABS
+# define R_ARM_THM_MOVT_ABS 48
+#endif
+#ifndef R_ARM_THM_JUMP11
+# define R_ARM_THM_JUMP11 102
+#endif
+#ifndef R_ARM_THM_JUMP8
+# define R_ARM_THM_JUMP8 103
+#endif
+
/*
* Define a set of types which can be used for both ELF32 and ELF64
*/
@@ -3729,6 +3872,9 @@ ocVerifyImage_ELF ( ObjectCode* oc )
IF_DEBUG(linker,debugBelch( "Architecture is " ));
switch (ehdr->e_machine) {
+#ifdef EM_ARM
+ case EM_ARM: IF_DEBUG(linker,debugBelch( "arm" )); break;
+#endif
case EM_386: IF_DEBUG(linker,debugBelch( "x86" )); break;
#ifdef EM_SPARC32PLUS
case EM_SPARC32PLUS:
@@ -4090,7 +4236,7 @@ ocGetNames_ELF ( ObjectCode* oc )
}
/* Do ELF relocations which lack an explicit addend. All x86-linux
- relocations appear to be of this form. */
+ and arm-linux relocations appear to be of this form. */
static int
do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC,
Elf_Shdr* shdr, int shnum )
@@ -4138,6 +4284,9 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC,
#endif
StgStablePtr stablePtr;
StgPtr stableVal;
+#ifdef arm_HOST_ARCH
+ int is_target_thm=0, T=0;
+#endif
IF_DEBUG(linker,debugBelch( "Rel entry %3d is raw(%6p %6p)",
j, (void*)offset, (void*)info ));
@@ -4173,6 +4322,16 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC,
return 0;
}
IF_DEBUG(linker,debugBelch( "`%s' resolves to %p\n", symbol, (void*)S
));
+#ifdef arm_HOST_ARCH
+ // Thumb instructions have bit 0 of symbol's st_value set
+ is_target_thm = sym.st_value & 0x1;
+ T = sym.st_info & STT_FUNC && is_target_thm;
+
+ // Make sure we clear bit 0. Strictly speaking we should have done
+ // this to st_value above but I believe alignment requirements should
+ // ensure that no instructions start on an odd address
+ S &= ~1;
+#endif
}
IF_DEBUG(linker,debugBelch( "Reloc: P = %p S = %p A = %p\n",
@@ -4188,6 +4347,191 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC,
case R_386_32: *pP = value; break;
case R_386_PC32: *pP = value - P; break;
# endif
+# ifdef arm_HOST_ARCH
+ case R_ARM_ABS32:
+ case R_ARM_TARGET1: // Specified by Linux ARM ABI
+ *(Elf32_Word *)P += S;
+ *(Elf32_Word *)P |= T;
+ break;
+
+ case R_ARM_REL32:
+ *(Elf32_Word *)P += S;
+ *(Elf32_Word *)P |= T;
+ *(Elf32_Word *)P -= P;
+ break;
+
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ {
+ StgWord32 *word = (StgWord32 *)P;
+ StgInt32 imm = (*word & 0x00ffffff) << 2;
+ StgInt32 offset;
+ int overflow;
+
+ // Sign extend 24 to 32 bits
+ if (imm & 0x02000000)
+ imm -= 0x04000000;
+ offset = ((S + imm) | T) - P;
+
+ overflow = offset <= (StgInt32)0xfe000000 || offset >=
(StgInt32)0x02000000;
+
+ if ((is_target_thm && ELF_R_TYPE(info) == R_ARM_JUMP24) ||
overflow) {
+ // Generate veneer
+ offset = &(makeArmSymbolExtra(oc, ELF_R_SYM(info), S+imm, 0,
is_target_thm)->jumpIsland);
+ offset -= P - 8;
+ offset &= ~1; // Clear instruction mode bit
+ } else if (is_target_thm && ELF_R_TYPE(info) == R_ARM_CALL) {
+ StgWord32 cond = (*word & 0xf0000000) >> 28;
+ if (cond == 0xe) {
+ // Change instruction to BLX
+ *word |= 0xf0000000; // Set first nibble
+ *word = (*word & ~0x01ffffff)
+ | ((offset >> 2) & 0x00ffffff) // imm24
+ | ((offset & 0x2) << 23); // H
+ break;
+ } else {
+ errorBelch("%s: Can't transition from ARM to Thumb when cond
!= 0xe\n",
+ oc->fileName);
+ return 0;
+ }
+ }
+
+ offset >>= 2;
+ *word = (*word & ~0x00ffffff)
+ | (offset & 0x00ffffff);
+ break;
+ }
+
+ case R_ARM_MOVT_ABS:
+ case R_ARM_MOVW_ABS_NC:
+ {
+ StgWord32 *word = (StgWord32 *)P;
+ StgInt16 offset = ((*word & 0xf0000) >> 4)
+ | (*word & 0xfff);
+ // Sign extend from 16 to 32 bits
+ offset = (offset ^ 0x8000) - 0x8000;
+
+ offset += S;
+ if (ELF_R_TYPE(info) == R_ARM_THM_MOVT_ABS)
+ offset >>= 16;
+ else
+ offset |= T;
+
+ *word = (*word & 0xfff0f000)
+ | ((offset & 0xf000) << 4)
+ | (offset & 0x0fff);
+ break;
+ }
+
+ case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
+ {
+ StgWord16 *upper = (StgWord16 *)P;
+ StgWord16 *lower = (StgWord16 *)(P + 2);
+
+ int overflow;
+ int sign = (*upper >> 10) & 1;
+ int j1, j2, i1, i2;
+
+ // Decode immediate value
+ j1 = (*lower >> 13) & 1; i1 = ~(j1 ^ sign) & 1;
+ j2 = (*lower >> 11) & 1; i2 = ~(j2 ^ sign) & 1;
+ StgInt32 imm = (sign << 24)
+ | (i1 << 23)
+ | (i2 << 22)
+ | ((*upper & 0x03ff) << 12)
+ | ((*lower & 0x07ff) << 1);
+
+ // Sign extend 25 to 32 bits
+ if (imm & 0x01000000)
+ imm -= 0x02000000;
+
+ offset = ((imm + S) | T) - P;
+ overflow = offset <= (StgInt32)0xff000000 || offset >=
(StgInt32)0x01000000;
+
+ if ((!is_target_thm && ELF_R_TYPE(info) == R_ARM_THM_JUMP24) ||
overflow) {
+ // Generate veneer
+ offset = &(makeArmSymbolExtra(oc, ELF_R_SYM(info), S+imm, 1,
is_target_thm)->jumpIsland);
+ offset -= P - 4;
+ offset |= 1; // Set thumb indicator bit
+ } else if (!is_target_thm && ELF_R_TYPE(info) == R_ARM_THM_CALL) {
+ *lower &= ~(1<<12); // Change instruction to BLX
+ offset &= ~1; // Make sure offset is aligned properly
+ }
+
+ // Reencode instruction
+ i1 = ~(offset >> 23) & 1; j1 = sign ^ i1;
+ i2 = ~(offset >> 22) & 1; j2 = sign ^ i2;
+ *upper = ( (*upper & 0xf800)
+ | (sign << 10)
+ | ((offset >> 12) & 0x03ff) );
+ *lower = ( (*lower & 0xd000)
+ | (j1 << 13)
+ | (j2 << 11)
+ | ((offset >> 1) & 0x07ff) );
+ break;
+ }
+
+ case R_ARM_THM_MOVT_ABS:
+ case R_ARM_THM_MOVW_ABS_NC:
+ {
+ StgWord16 *upper = (StgWord16 *)P;
+ StgWord16 *lower = (StgWord16 *)(P + 2);
+ StgWord16 offset = (*upper & 0x000f << 12)
+ | ((*upper & 0x0400) << 1)
+ | ((*lower & 0x7000) >> 4)
+ | (*lower & 0x00ff);
+
+ offset = (offset ^ 0x8000) - 0x8000; // Sign extend
+ offset += S;
+ if (ELF_R_TYPE(info) == R_ARM_THM_MOVW_ABS_NC)
+ offset |= T;
+ else
+ offset >>= 16;
+
+ *upper = ( (*upper & 0xfbf0)
+ | ((offset & 0xf000) >> 12)
+ | ((offset & 0x0800) >> 1) );
+ *lower = ( (*lower & 0x8f00)
+ | ((offset & 0x0700) << 4)
+ | (offset & 0x00ff) );
+ break;
+ }
+
+ case R_ARM_THM_JUMP8:
+ {
+ StgWord16 *word = (StgWord16 *)P;
+ StgWord offset = *word & 0x01fe;
+ offset += S - P;
+ if (!is_target_thm) {
+ errorBelch("%s: Thumb to ARM transition with JUMP8 relocation
not supported\n",
+ oc->fileName);
+ return 0;
+ }
+
+ *word = (*word & ~0x01fe)
+ | (offset & 0x01fe);
+ break;
+ }
+
+ case R_ARM_THM_JUMP11:
+ {
+ StgWord16 *word = (StgWord16 *)P;
+ StgWord offset = *word & 0x0ffe;
+ offset += S - P;
+ if (!is_target_thm) {
+ errorBelch("%s: Thumb to ARM transition with JUMP11 relocation
not supported\n",
+ oc->fileName);
+ return 0;
+ }
+
+ *word = (*word & ~0x0ffe)
+ | (offset & 0x0ffe);
+ break;
+ }
+
+# endif
+
default:
errorBelch("%s: unhandled ELF relocation(Rel) type %lu\n",
oc->fileName, (lnat)ELF_R_TYPE(info));
@@ -4503,7 +4847,7 @@ ocResolve_ELF ( ObjectCode* oc )
}
}
-#if defined(powerpc_HOST_ARCH)
+#if defined(powerpc_HOST_ARCH) || defined(arm_HOST_ARCH)
ocFlushInstructionCache( oc );
#endif
@@ -4514,7 +4858,7 @@ ocResolve_ELF ( ObjectCode* oc )
* PowerPC & X86_64 ELF specifics
*/
-#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
+#if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH) ||
defined(arm_HOST_ARCH)
static int ocAllocateSymbolExtras_ELF( ObjectCode *oc )
{
diff --git a/rts/LinkerInternals.h b/rts/LinkerInternals.h
index d545c12..037bc91 100644
--- a/rts/LinkerInternals.h
+++ b/rts/LinkerInternals.h
@@ -40,7 +40,7 @@ typedef
ProddableBlock;
/* Jump Islands are sniplets of machine code required for relative
- * address relocations on the PowerPC.
+ * address relocations on the PowerPC, x86_64 and ARM.
*/
typedef struct {
#ifdef powerpc_HOST_ARCH
@@ -53,6 +53,8 @@ typedef struct {
#elif x86_64_HOST_ARCH
uint64_t addr;
uint8_t jumpIsland[6];
+#elif arm_HOST_ARCH
+ uint8_t jumpIsland[16];
#endif
} SymbolExtra;
@@ -104,7 +106,7 @@ typedef struct _ObjectCode {
unsigned int pltIndex;
#endif
-#if powerpc_HOST_ARCH || x86_64_HOST_ARCH
+#if powerpc_HOST_ARCH || x86_64_HOST_ARCH || arm_HOST_ARCH
SymbolExtra *symbol_extras;
unsigned long first_symbol_extra;
unsigned long n_symbol_extras;
_______________________________________________
Cvs-ghc mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/cvs-ghc