how can I get the `__NSAutoreleaseNoPool` address?

后端 未结 1 1501
被撕碎了的回忆
被撕碎了的回忆 2021-01-16 11:25

I tried with

extern void __NSAutoreleaseNoPool(void* obj);

but that results in an unresolved symbol when linking (not sure what Framework i

1条回答
  •  粉色の甜心
    2021-01-16 11:45

    This works:

    #include 
    #include 
    #import 
    #include 
    #include 
    #include 
    #include 
    
    // Adapted from:
    // https://github.com/0xced/iOS-Artwork-Extractor/blob/master/Classes/FindSymbol.c
    // Adapted from MoreAddrToSym / GetFunctionName()
    // http://www.opensource.apple.com/source/openmpi/openmpi-8/openmpi/opal/mca/backtrace/darwin/MoreBacktrace/MoreDebugging/MoreAddrToSym.c
    void *FindSymbol(const struct mach_header *img, const char *symbol)
    {
        if ((img == NULL) || (symbol == NULL))
            return NULL;
    
        // only 64bit supported
    #if defined (__LP64__)
    
        if(img->magic != MH_MAGIC_64)
            // we currently only support Intel 64bit
            return NULL;
    
        struct mach_header_64 *image = (struct mach_header_64*) img;
    
        struct segment_command_64 *seg_linkedit = NULL;
        struct segment_command_64 *seg_text = NULL;
        struct symtab_command *symtab = NULL;
        unsigned int index;
    
        struct load_command *cmd = (struct load_command*)(image + 1);
    
        for (index = 0; index < image->ncmds; index += 1, cmd = (struct load_command*)((char*)cmd + cmd->cmdsize))
        {
            switch(cmd->cmd)
            {
                case LC_SEGMENT_64: {
                    struct segment_command_64* segcmd = (struct segment_command_64*)cmd;
                    if (!strcmp(segcmd->segname, SEG_TEXT))
                        seg_text = segcmd;
                    else if (!strcmp(segcmd->segname, SEG_LINKEDIT))
                        seg_linkedit = segcmd;
                    break;
                }
    
                case LC_SYMTAB:
                    symtab = (struct symtab_command*)cmd;
                    break;
    
                default:
                    break;
            }
        }
    
        if ((seg_text == NULL) || (seg_linkedit == NULL) || (symtab == NULL))
            return NULL;
    
        unsigned long vm_slide = (unsigned long)image - (unsigned long)seg_text->vmaddr;
        unsigned long file_slide = ((unsigned long)seg_linkedit->vmaddr - (unsigned long)seg_text->vmaddr) - seg_linkedit->fileoff;
        struct nlist_64 *symbase = (struct nlist_64*)((unsigned long)image + (symtab->symoff + file_slide));
        char *strings = (char*)((unsigned long)image + (symtab->stroff + file_slide));
        struct nlist_64 *sym;
    
        for (index = 0, sym = symbase; index < symtab->nsyms; index += 1, sym += 1)
        {
            if (sym->n_un.n_strx != 0 && !strcmp(symbol, strings + sym->n_un.n_strx))
            {
                unsigned long address = vm_slide + sym->n_value;
                if (sym->n_desc & N_ARM_THUMB_DEF)
                    return (void*)(address | 1);
                else
                    return (void*)(address);
            }
        }   
    #endif
    
        return NULL;
    }
    
    typedef void (*NSAutoreleaseNoPoolFunc) (void* obj);
    
    void getNSAutoreleaseNoPool() { 
        const struct mach_header* img = NSAddImage("/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", NSADDIMAGE_OPTION_NONE);
        NSAutoreleaseNoPoolFunc f = (NSAutoreleaseNoPoolFunc) FindSymbol((struct mach_header*)img, "___NSAutoreleaseNoPool");
    
        printf("func: %p\n", f);
    
        if(f) {
            NSObject* foo = [[NSObject alloc] init];
            f(foo);
        }
    }
    

    It gets the same function pointer as within GDB.

    Note that you wont see the common NSAutoreleaseNoPool log:

    2014-02-18 14:46:26.583 a.out[24989:a0b] *** __NSAutoreleaseNoPool(): Object 0x7fff71154190 of class NSCFString autoreleased with no pool in place - just leaking
    

    The standard backtrace, when that happens, is this:

    (gdb) bt
    #0  0x00007fff8724bd34 in __NSAutoreleaseNoPool ()
    #1  0x00007fff87196e79 in _CFAutoreleasePoolAddObject ()
    #2  0x00007fff87196be6 in -[NSObject(NSObject) autorelease] ()
    

    The actual NSLog call is done in _CFAutoreleasePoolAddObject.

    A note about __NSAutoreleaseNoPool, from Foundation/NSDebug.h:

    /****************   Autorelease pool debugging  ****************/
    
    // Functions used as interesting breakpoints in a debugger
    // void __NSAutoreleaseNoPool(void *object);
        // Called to log the "Object X of class Y autoreleased with no
        // pool in place - just leaking" message.  If an environment
        // variable named "NSAutoreleaseHaltOnNoPool" is set with string
        // value "YES", the function will automatically break in the
        // debugger (or terminate the process).
    
    // void __NSAutoreleaseFreedObject(void *freedObject);
        // Called when a previously freed object would be released
        // by an autorelease pool.  If an environment variable named
        // "NSAutoreleaseHaltOnFreedObject" is set with string value
        // "YES", the function will automatically break in the debugger
        // (or terminate the process).
    

    So, if you want to debug such cases, either start up GDB and issue b __NSAutoreleaseNoPool to setup the breakpoint on this function. Or do an export NSAutoreleaseHaltOnNoPool=1 in your shell.

    __NSAutoreleaseNoPool is pretty simple:

    (gdb) disassemble 
    Dump of assembler code for function __NSAutoreleaseNoPool:
    0x00007fff8724bd30 <__NSAutoreleaseNoPool+0>:   push   %rbp
    0x00007fff8724bd31 <__NSAutoreleaseNoPool+1>:   mov    %rsp,%rbp
    0x00007fff8724bd34 <__NSAutoreleaseNoPool+4>:   nop    
    0x00007fff8724bd35 <__NSAutoreleaseNoPool+5>:   nopl   0x0(%rax)
    0x00007fff8724bd39 <__NSAutoreleaseNoPool+9>:   lea    0x2ced8(%rip),%rdi        # 0x7fff87278c18 <__PRETTY_FUNCTION__.27904+480>
    0x00007fff8724bd40 <__NSAutoreleaseNoPool+16>:  callq  0x7fff871439e0 <__CFgetenv>
    0x00007fff8724bd45 <__NSAutoreleaseNoPool+21>:  test   %rax,%rax
    0x00007fff8724bd48 <__NSAutoreleaseNoPool+24>:  je     0x7fff8724bd55 <__NSAutoreleaseNoPool+37>
    0x00007fff8724bd4a <__NSAutoreleaseNoPool+26>:  movzbl (%rax),%eax
    0x00007fff8724bd4d <__NSAutoreleaseNoPool+29>:  cmp    $0x59,%al
    0x00007fff8724bd4f <__NSAutoreleaseNoPool+31>:  je     0x7fff8724bd60 <__NSAutoreleaseNoPool+48>
    0x00007fff8724bd51 <__NSAutoreleaseNoPool+33>:  cmp    $0x79,%al
    0x00007fff8724bd53 <__NSAutoreleaseNoPool+35>:  je     0x7fff8724bd60 <__NSAutoreleaseNoPool+48>
    0x00007fff8724bd55 <__NSAutoreleaseNoPool+37>:  leaveq 
    0x00007fff8724bd56 <__NSAutoreleaseNoPool+38>:  retq   
    0x00007fff8724bd57 <__NSAutoreleaseNoPool+39>:  nopw   0x0(%rax,%rax,1)
    0x00007fff8724bd60 <__NSAutoreleaseNoPool+48>:  int3   
    0x00007fff8724bd61 <__NSAutoreleaseNoPool+49>:  callq  0x7fff872609c2 
    0x00007fff8724bd66 <__NSAutoreleaseNoPool+54>:  mov    %eax,%edi
    0x00007fff8724bd68 <__NSAutoreleaseNoPool+56>:  mov    $0x9,%esi
    0x00007fff8724bd6d <__NSAutoreleaseNoPool+61>:  leaveq 
    0x00007fff8724bd6e <__NSAutoreleaseNoPool+62>:  jmpq   0x7fff87260a16 
    0x00007fff8724bd73 <__NSAutoreleaseNoPool+67>:  nopw   0x0(%rax,%rax,1)
    0x00007fff8724bd79 <__NSAutoreleaseNoPool+73>:  nopl   0x0(%rax)
    End of assembler dump.
    

    For a practical example, see demo_NSAutoreleaseNoPool.mm.

    0 讨论(0)
提交回复
热议问题