Why do crashes in iOS relating to dyld_stub_binder occur?
Asked Answered
M

1

8

It's widely known that dynamic link libraries aren't allowed in iOS apps, they may only link to dynamic system libraries. But I do run into some pretty confusing crashes with the 3rd frame from the top of the stack being dyld_stub_binder.

It's tough to find some solid information, but I'm guessing that dyld_stub_binder actually performs late linking of a dynamic system library.

I tend to run into crashes where the exception is EXC_BREAKPOINT UNKNOWN and the crash always seems to occur in the context of dyld_stub_binder.

The implementation of dyld_stub_binder is on the apple open source website. I don't quite understand the assembly, but perhaps someone who does could interpret why this error happens or whether or not it's something that is out of the application's direct control. The assembly code may not be useful though, as I'm talking about the iOS (arm) implementation and this code is i386 and x86_64.

EDIT: An interesting piece of information is that I think I started seeing this crash during efforts for porting to arm64. Is it possible that a runtime exception like this is due to some kind of misalignment?

Marissamarist answered 23/4, 2015 at 20:17 Comment(0)
C
7

As you've stated, the asm for the ARM case is not available, but it's fairly straightforward to figure out since you can decompile fairly easily. What dyld_stub_binder does (on all architectures) is to handle the lazy symbols in a binary. For example, consider the following:

   $ cat a.c
void main(int argc, char **argv)
{

    printf("%s", argv[1]);

}
$ gcc-iphone a.c -o a 
$ jtool -d a
Disassembling from file offset 0x7f44, Address 0x100007f44 
_main:
   100007f44    STP    X29, X30, [X31,#-16]!    
   100007f48    ADD    x29, x31, #0x0   ; ..R29 = R31 (0x0) + 0x0 = 0x1f 
   100007f4c    SUB    X31, X31, #32    
   100007f50    STUR   X0, X29, #-4     ; *((1) + 0x0) = ???
   100007f54    STR    X1, [ X31, #2]   ; *((2) + 0x0) = ???
   100007f58    LDR    X1, [X31, #0x10] ; R1 = *(10) = 0x100000cfeedfacf
   100007f5c    LDR    X1, [X1, #0x8]   ; R1 = *(100000cfeedfad7) = 0x100000cfeedfacf
   100007f60    ADD    x8, x31, #0x0    ; ..R8 = R31 (0x0) + 0x0 = 0x1f 
   100007f64    STR    X1, [ X8, #0]    ; *(0x0) = 0xfeedfacf
   100007f68    ADRP   x0, 0            ; ->R0 = 0x100007000 
   100007f6c    ADD    x0, x0, #0xfb4   ; ..R0 = R0 (0x100007000) + 0xfb4 = 0x100007fb4 "%s"
   100007f70    BL     _printf  ; 0x100007f84
; _printf("%s",arg..);

   100007f74    STR    X0, [ X31, #3]   ; *((254) + 0x0) = ???
   100007f78    ADD    x31, x29, #0x0   ; ..R31 = R29 (0x1f) + 0x0 = 0x1d 
   100007f7c    LDP    X29, X30, [X31],#16  
   100007f80    RET    

see that printf up there? 0x100007f84? Let's see what that is (The built-in otool can't decompile that part, but jtool can:)

_printf:
   100007f84    NOP                     
   100007f88    LDR    X16, #34         ; R16 = *(100008010) = 0x100007fa8

   100007f8c    BR     X16   

So you just to 0x100007fa8. Once again applying jtool:

$ jtool -d 0x100007fa8 a
Disassembling from file offset 0x7fa8, Address 0x100007fa8 
   100007fa8    LDR    X16, #2          
   100007fac    B      0x100007f90

And now we have 0x100007f90, which is ...

   100007f90    ADR    x17, 120         ; ->R17 = 0x100008008 
   100007f94    NOP                     
   100007f98    STP    X16, X17, [X31,#-16]!    
   100007f9c    NOP                     
   100007fa0    LDR    X16, #24         ; R16 = *(100008000) dyld_stub_binder
   100007fa4    BR     X16              

Now, go back to that 0x...8010 which gets loaded - that will be the address of printf(), but it is only bound after the first "hit" or access. You can verify that with dyldinfo, or jtool -lazy_bind:

$ jtool -lazy_bind a
bind information:
segment section          address        type    addend dylib            symbol
__DATA  __la_symbol_ptr  0x100008010    ...     0 libSystem.B.dylib    _printf

Meaning, on first access, the stub_binder finds the address of printf in lib system, and embeds it there.

If the symbol cannot be bound, you get an exception. Though that can be for oh-so-many-reasons. You might want to add the crash log here. If it's a breakpoint, that's a voluntary crash by dyld which usually occurs when symbol was not found. If a debugger (lldb) is attached, it will break there and then. Else - with no debugger - it crashes.

Christenechristening answered 21/5, 2015 at 19:15 Comment(2)
Yes I know it's a late binder for system libraries, I should have been clearer. But regarding why a crash would typically happen in there is confusing. For instance, we build vorbis as a separate library in our project, and intermittently we see a crash in mdct_init. All it's doing is allocating buffers and using trig functions from libmath (cosine table for idct, provided as an iOS system dylib). The functions are all declared in math.h as external, e.g. bound at runtime without static lib. Perhaps this is a problem if libmath.dylib isn't loaded or can't be loaded by iOS for some reason.Marissamarist
Great answer by the way. I had to cut some the appreciation string due to character limit.Marissamarist

© 2022 - 2024 — McMap. All rights reserved.