Ticket #7722 (closed feature request: fixed)

Opened 3 months ago

Last modified 2 months ago

iOS patch no 11: Fix quirk with runtime loader

Reported by: StephenBlackheath Owned by:
Priority: normal Milestone:
Component: Compiler Version: 7.7
Keywords: Cc: dterei
Operating System: Other Architecture: arm
Type of failure: None/Unknown Difficulty: Unknown
Test Case: Blocked By:
Blocking: #7724 Related Tickets:

Description

On iOS, due to a quirk, we must put references to external symbols into the data segment. If, for example, we have a declaration like this:

foreign import ccall unsafe "static stdlib.h &free"
    c_free_finalizer :: FunPtr (Ptr Word8 -> IO ())

...then it tries to resolve the address of 'free' at runtime. The loader then attempts to write that address into the text segment. On iOS, modifying the text segment is not allowed, and so a runtime exception is thrown.

We work around this issue by fetching the reference to the external symbol from the data segment.

Attachments

ios-patch-11a-runtime-loader-quirk.patch Download (10.4 KB) - added by StephenBlackheath 3 months ago.
iOS patch 11 rev a: runtime loader quirk
ios-patch-11b-runtime-loader-quirk.patch Download (12.3 KB) - added by StephenBlackheath 3 months ago.
iOS patch 11 rev b: another two cases of putting external symbols into the DATA segment now fix this problem
ios-patch-11c-simply-turn-on-dynamic-no-pic.patch Download (1.2 KB) - added by StephenBlackheath 2 months ago.
Simply pass -relocation-model=dynamic-no-pic to llvm when compiling for iOS, which fixes it nicely

Change History

Changed 3 months ago by StephenBlackheath

iOS patch 11 rev a: runtime loader quirk

Changed 3 months ago by StephenBlackheath

  • status changed from new to patch

Changed 3 months ago by StephenBlackheath

  • blocking 7724 added

Changed 3 months ago by StephenBlackheath

  • owner set to StephenBlackheath

Sadly this patch does not fix this issue, so more work is needed. The regression test is this:

main = do
    putStrLn $ "ByteString regression test: "++show (B.empty)

The program dies with this appearing in the runtime console:

dyld: vm_protect(0x00001000, 0x0042D000, false, 0x07) failed, result=2 for segment __TEXT in /var/mobile/Applications/38E8C4C6-DA4E-4FBA-8183-6473AAA58A66F/Pear.app/Pear

Changed 3 months ago by StephenBlackheath

iOS patch 11 rev b: another two cases of putting external symbols into the DATA segment now fix this problem

Changed 3 months ago by StephenBlackheath

  • owner StephenBlackheath deleted

Changed 3 months ago by simonmar

  • difficulty set to Unknown

I don't fully understand the problem here. How is the free symbol different from other symbols that we refer to? What happens if you write a C program that refers to free? (that would give us a clue about how the C compiler works around the issue, and hence how we should do it).

Changed 3 months ago by StephenBlackheath

I don't fully understand it either. I don't think there's anything special about free apart from - I assume - getting resolved at runtime. Here's what the darwin -> iOS C compiler produces for printf("%p\n", free);

	movw	r0, :lower16:(L_free$non_lazy_ptr-(LPC0_0+4))
	movt	r0, :upper16:(L_free$non_lazy_ptr-(LPC0_0+4))
LPC0_0:
	add	r0, pc
	ldr	r1, [r0]            <-- r1 being the second argument of printf
...
	.section	__DATA,__nl_symbol_ptr,non_lazy_symbol_pointers
	.align	2
L_free$non_lazy_ptr:
	.indirect_symbol	_free
	.long	0

Here's what GHC produces WITHOUT my patch, and this is the code that dies at runtime if you say 'print Data.ByteString?.empty'. The source file is libraries/bytestring/Data/ByteString/Unsafe.hs

        .text
        .align  2
_s2jH_info:                             @ @s2jH_info
...
        ldr     r9, LCPI29_2
...
        .align  2
LCPI29_2:
        .long   _free      <-- still under the .text above, i.e. in text segment

Here's what GHC produces WITH my patch:

        .text
        .align  2
_s2jH_info:                             @ @s2jH_info
...
        ldr     r8, LCPI29_1
...
        ldr     r9, [r8]
...
        .align  2
LCPI29_1:
        .long   _free_ref_n3dd
...
        .section        __DATA,__data
...
        .align  3                       @ @free_ref_n3dd
_free_ref_n3dd:
        .long   _free

I don't know what .indirect_symbol does (and my assumption would be that llvm can't output it), but it certainly looks like the C compiler has also made sure the reference to _free is in the DATA segment. And my code has (surprisingly) fewer instructions than the C output.

My priority has been to get things working, and not so much on proving the necessity or showing that it's the best way. Do you think this satisfies your questions about whether the changes are justified? If not, I'll investigate further.

Changed 2 months ago by simonmar

  • cc dterei added

So it looks like the C compiler is generating an indirect reference, as you would do when compiling a shared library (e.g. with -fPIC). On ELF platforms it isn't necessary to do this for static object files, because the linker relocates the data (rather than the symbol) at runtime. It looks like the iOS linker is using a different strategy.

I expect you can fix this by telling LLVM to use a dynamic reference for the symbols in question (it would be any symbol which might be dynamically linked). If there's no way to get LLVM to generate the right kind of symbol reference, then at the least you can re-use the code in GHC that does this: nativeGen/PIC.hs.

I'm not familiar with LLVM or with GHC's LLVM backend, so I've CC'd David Terei who might be able to help.

Changed 2 months ago by simonmar

Also see #4210; from a quick look at that it seems that you might be able to just use "dynamic-no-pic" as the relocation mode for LLVM.

Changed 2 months ago by StephenBlackheath

Simply pass -relocation-model=dynamic-no-pic to llvm when compiling for iOS, which fixes it nicely

Changed 2 months ago by StephenBlackheath

Simon, you were quite right and it works beautifully. A new very short patch attached to force that option for iOS targets.

Changed 2 months ago by davidterei@…

commit d6a6974f3b88417fcc0d780d164251331bcf9309

Author: David Terei <davidterei@gmail.com>
Date:   Thu Mar 7 13:29:00 2013 -0800

    Fix #7722: iOS runtime loader quirk.
    
    Patch from Stephen Blackheath!

 compiler/main/DriverPipeline.hs |    9 ++++++---
 1 files changed, 6 insertions(+), 3 deletions(-)

Changed 2 months ago by dterei

  • status changed from patch to closed
  • resolution set to fixed

Thanks Stephen and Simon! I love it when LLVM issues are so nicely solved by others :).

Changed 2 months ago by simonmar

Ok, but you might want to check that it isn't generating dynamic references for everything, which could add a lot of overhead. A quick check of the code size and performance before/after on a benchmark or two should be enough.

Note: See TracTickets for help on using tickets.