# OSX Lazy Symbol Pointer Binding
주의 : 이 글은 의식에 흐름에 맡겨 작성되었습니다.
## Lazy Symbol Pointer
OSX의 바이너리의 경우 Dynamic하게 함수를 호출할 수 있음.
Linux의 got랑 비슷하며, OSX에서는 이를 Lazy Symbol Pointer라 부름.
None-Lazy Symbol Pointer는 반대로 Static하게 박혀있는 값임.
이 값은 아래의 binding하는 과정에서 쓰기 위해 사용됨.
## Explorer
malloc을 처음 실행할 때, `__stubs`위에 있는 malloc함수로 날라감.
```
jmp cs:_malloc_ptr
```
`_malloc_ptr`의 경우 실행한 적이 없다면 `__stub_helper`영역을 가르킨다.
```
__stub_helper:0000000100000F2C loc_100000F2C: ; CODE XREF: __stub_helper:0000000100000F41↓j
__stub_helper:0000000100000F2C ; __stub_helper:0000000100000F4B↓j
__stub_helper:0000000100000F2C lea r11, off_100001008
__stub_helper:0000000100000F33 push r11
__stub_helper:0000000100000F35 jmp cs:dyld_stub_binder_ptr
__stub_helper:0000000100000F35 ; ---------------------------------------------------------------------------
__stub_helper:0000000100000F3B align 4
__stub_helper:0000000100000F3C push 0
__stub_helper:0000000100000F41 jmp loc_100000F2C
__stub_helper:0000000100000F46 ; ---------------------------------------------------------------------------
__stub_helper:0000000100000F46 push 0Eh
__stub_helper:0000000100000F4B jmp loc_100000F2C
__stub_helper:0000000100000F4B __stub_helper ends
```
`dyld_stub_binder_ptr`로 {0, 0x100001008}의 값을 가지고 간다.
```assembly
.align 2,0x90
.globl dyld_stub_binder
dyld_stub_binder:
pushq %rbp
movq %rsp,%rbp
subq $STACK_SIZE,%rsp # at this point stack is 16-byte aligned because two meta-parameters where pushed
movq %rdi,RDI_SAVE(%rsp) # save registers that might be used as parameters
movq %rsi,RSI_SAVE(%rsp)
movq %rdx,RDX_SAVE(%rsp)
movq %rcx,RCX_SAVE(%rsp)
movq %r8,R8_SAVE(%rsp)
movq %r9,R9_SAVE(%rsp)
movq %rax,RAX_SAVE(%rsp)
misaligned_stack_error_entering_dyld_stub_binder:
movdqa %xmm0,XMMM0_SAVE(%rsp)
movdqa %xmm1,XMMM1_SAVE(%rsp)
movdqa %xmm2,XMMM2_SAVE(%rsp)
movdqa %xmm3,XMMM3_SAVE(%rsp)
movdqa %xmm4,XMMM4_SAVE(%rsp)
movdqa %xmm5,XMMM5_SAVE(%rsp)
movdqa %xmm6,XMMM6_SAVE(%rsp)
movdqa %xmm7,XMMM7_SAVE(%rsp)
dyld_stub_binder_:
movq MH_PARAM_BP(%rbp),%rdi # call fastBindLazySymbol(loadercache, lazyinfo)
movq LP_PARAM_BP(%rbp),%rsi
call __Z21_dyld_fast_stub_entryPvl
movq %rax,%r11 # save target
movdqa XMMM0_SAVE(%rsp),%xmm0 # restore registers
movdqa XMMM1_SAVE(%rsp),%xmm1
movdqa XMMM2_SAVE(%rsp),%xmm2
movdqa XMMM3_SAVE(%rsp),%xmm3
movdqa XMMM4_SAVE(%rsp),%xmm4
movdqa XMMM5_SAVE(%rsp),%xmm5
movdqa XMMM6_SAVE(%rsp),%xmm6
movdqa XMMM7_SAVE(%rsp),%xmm7
movq RDI_SAVE(%rsp),%rdi
movq RSI_SAVE(%rsp),%rsi
movq RDX_SAVE(%rsp),%rdx
movq RCX_SAVE(%rsp),%rcx
movq R8_SAVE(%rsp),%r8
movq R9_SAVE(%rsp),%r9
movq RAX_SAVE(%rsp),%rax
addq $STACK_SIZE,%rsp
popq %rbp
addq $16,%rsp # remove meta-parameters
jmp *%r11 # jmp to target
```
안에서는 (loadercache, lazyinfo)를 인자로써, `_dyld_fast_stub_entry`를 호출.
```c
#if __i386__ || __x86_64__
__attribute__((visibility("hidden")))
void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo)
{
DYLD_NO_LOCK_THIS_BLOCK;
static void* (*p)(void*, long) = NULL;
if(p == NULL)
_dyld_func_lookup("__dyld_fast_stub_entry", (void**)&p);
return p(loadercache, lazyinfo);
}
#endif
```
p의 값이 존재하지 않는다면, "\_\_dyld\_fast\_stub\_entry"의 값을 인자로써 `_dyld_func_lookup`함수를 한번 수행하여 p에 값을 구해옴.
아래의 함수(dylib)에 들어가서 jmp rax를 통해 또 한번 이동을 하게됨.
```assembly
_dyld_func_lookup @ libdyld.dylib:
-> 0x7fff6b8bc508: 55 push rbp
0x7fff6b8bc509: 48 89 e5 mov rbp, rsp
0x7fff6b8bc50c: 48 8b 05 f5 9a 96 36 mov rax, qword ptr [rip + 0x36969af5] ; myDyldSection + 8
0x7fff6b8bc513: 5d pop rbp
0x7fff6b8bc514: ff e0 jmp rax
```
dyld의 영역에 있는 func_lookup으로 이동함
```assembly
_dyld_func_lookup @ dyld:
-> 0x100011e80: 55 push rbp
0x100011e81: 48 89 e5 mov rbp, rsp
0x100011e84: 41 57 push r15
0x100011e86: 41 56 push r14
0x100011e88: 41 54 push r12
0x100011e8a: 53 push rbx
0x100011e8b: 49 89 f6 mov r14, rsi
0x100011e8e: 49 89 ff mov r15, rdi
0x100011e91: 4c 8d 25 b8 e1 04 00 lea r12, [rip + 0x4e1b8] ; "__dyld_register_func_for_add_image"
0x100011e98: 48 8d 1d 49 c5 05 00 lea rbx, [rip + 0x5c549] ; dyld_funcs + 8
0x100011e9f: 4c 89 e7 mov rdi, r12
0x100011ea2: 4c 89 fe mov rsi, r15
0x100011ea5: e8 56 22 03 00 call 0x100044100 ; strcmp
0x100011eaa: 85 c0 test eax, eax
0x100011eac: 74 18 je 0x100011ec6 ; <+70>
0x100011eae: 4c 8b 63 08 mov r12, qword ptr [rbx + 0x8]
0x100011eb2: 48 83 c3 10 add rbx, 0x10
0x100011eb6: 4d 85 e4 test r12, r12
0x100011eb9: 75 e4 jne 0x100011e9f ; <+31>
0x100011ebb: 49 c7 06 00 00 00 00 mov qword ptr [r14], 0x0
0x100011ec2: 31 c0 xor eax, eax
0x100011ec4: eb 2b jmp 0x100011ef1 ; <+113>
0x100011ec6: 48 8b 03 mov rax, qword ptr [rbx]
0x100011ec9: 48 8d 0d 2a 00 00 00 lea rcx, [rip + 0x2a] ; unimplemented()
0x100011ed0: 48 39 c8 cmp rax, rcx
0x100011ed3: 75 14 jne 0x100011ee9 ; <+105>
0x100011ed5: 48 8d 3d 37 dd 04 00 lea rdi, [rip + 0x4dd37] ; "unimplemented dyld function: %s\n"
0x100011edc: 31 c0 xor eax, eax
0x100011ede: 4c 89 e6 mov rsi, r12
0x100011ee1: e8 db 2c ff ff call 0x100004bc1 ; dyld::log(char const*, ...)
0x100011ee6: 48 8b 03 mov rax, qword ptr [rbx]
0x100011ee9: 49 89 06 mov qword ptr [r14], rax
0x100011eec: b8 01 00 00 00 mov eax, 0x1
0x100011ef1: 5b pop rbx
0x100011ef2: 41 5c pop r12
0x100011ef4: 41 5e pop r14
0x100011ef6: 41 5f pop r15
0x100011ef8: 5d pop rbp
0x100011ef9: c3 ret
```
이 함수에서는 0x100060050의 주소에 있는 `__dyld_register_func_for_add_image`로 시작하여 우리는 binding하기 위한 스트링이랑 맞는 값을 찾는다.(strcmp로 비교)
> strcmp를 하는 문자열 리스트는 아래에 첨부 [Click](#bonus1)
>
위 스트링이 저장된 주소들의 경우 dyld_func라는 영역에서의 데이터를 가져오는 것이다.
```
(lldbinit) x/32gx 0x000000010006E3E0
0x10006e3e0: 0x0000000100060050 0x00000001000107d6
0x10006e3f0: 0x0000000100060073 0x0000000100010811
0x10006e400: 0x0000000100060099 0x0000000100012625
0x10006e410: 0x00000001000600a7 0x0000000100012564
0x10006e420: 0x00000001000600b6 0x0000000100012718
```
이는 대충 아래와 같이 생긴걸로 예측할 수 있다.
```
struct S{
*func_name
*func
} S_;
S_ dyld_func[N];
```
여기서 `__dyld_fast_stub_entry`의 값을 발견하면 이에 맞는 func를 리턴하여 p의 값으로 저장이 될 것이다.
확인해보자.
```
(lldbinit) re r r14
r14 = 0x00007fffa2228f58 libdyld.dylib`_dyld_fast_stub_entry(void*, long)::p
(lldbinit) x/gx 0x00007fffa2228f58
0x7fffa2228f58: 0x0000000100007b20 -> dyld::fastBindLazySymbol(ImageLoader**, unsigned long)
```
p의 값에 정상적으로 들어간 것을 알 수 있다.
```c
p(loadercache, lazyinfo);
```
이런식으로 원하는 값을 가져온 후 `jmp rax`의 코드로 인해 실행된다.
여기까지 함수 콜을 정리해보면 다음과 같다.
```
call malloc
-> dyld_stub_binder @ libdyld.dylib
-> _dyld_fast_stub_entry @ libdyld.dylib
-> _dyld_func_lookup @ libdyld.dylib
-> _dyld_func_lookup @ dyld
-> dyld::fastBindLazySymbol @ dyld
```
```c
uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset)
{
uintptr_t result = 0;
// get image
if ( *imageLoaderCache == NULL ) {
// save in cache
*imageLoaderCache = dyld::findMappedRange((uintptr_t)imageLoaderCache);
if ( *imageLoaderCache == NULL ) {
const char* message = "fast lazy binding from unknown image";
dyld::log("dyld: %s\n", message);
halt(message);
}
}
// bind lazy pointer and return it
try {
result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext,
(dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->acquireGlobalDyldLock : NULL,
(dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->releaseGlobalDyldLock : NULL);
}
catch (const char* message) {
dyld::log("dyld: lazy symbol binding failed: %s\n", message);
halt(message);
}
// return target address to glue which jumps to it with real parameters restored
return result;
}
```
**fastBindLazySymbol** 함수를 호출할 때 들어간 인자에 어떤 값이 들어가는지 한번 확인해보자.
```
(lldbinit) re r rdi rsi
rdi = 0x0000000100001008 (void *)0x0000000000000000
rsi = 0x0000000000000000
(lldbinit) x/gx $rdi
0x100001008: 0x0000000000000000
(lldbinit) x/32i 0000000100000F2C
0x100000f2c: 4c 8d 1d d5 00 00 00 lea r11, [rip + 0xd5] ; (void *)0x0000000000000000
0x100000f33: 41 53 push r11
0x100000f35: ff 25 c5 00 00 00 jmp qword ptr [rip + 0xc5] ; (void *)0x00007fff6b8bd214: dyld_stub_binder
0x100000f3b: 90 nop
0x100000f3c: 68 00 00 00 00 push 0x0
0x100000f41: e9 e6 ff ff ff jmp 0x100000f2c
```
`__stub_helper` 함수를 보면 0x100000f3c에서 push한 0x0이 **lazyBindingInfoOffset**, 0x100000f33에서 push한 0x0000000100001008이 **imageLoaderCache** 값이다.
> 0x0000000100001008의 주소는 **__nl_symbol_ptr**영역에 있는 값이다. (none-lazy symbol pointer)
>
현재 상황에서는 lazyBindingInfoOffset와 imageLoaderCache 둘다 0을 가지고 있다.
imageLoaderCache가 0이기 때문에 findMappedRange함수를 수행함
```c
//
// The MappedRanges structure is used for fast address->image lookups.
// The table is only updated when the dyld lock is held, so we don't
// need to worry about multiple writers. But readers may look at this
// data without holding the lock. Therefore, all updates must be done
// in an order that will never cause readers to see inconsistent data.
// The general rule is that if the image field is non-NULL then
// the other fields are valid.
//
struct MappedRanges
{
enum { count=400 };
struct {
ImageLoader* image;
uintptr_t start;
uintptr_t end;
} array[count];
MappedRanges* next;
};
static MappedRanges sMappedRangesStart;
...
ImageLoader* findMappedRange(uintptr_t target)
{
for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) {
for (int i=0; i < MappedRanges::count; ++i) {
if ( p->array[i].image != NULL ) {
if ( (p->array[i].start <= target) && (target < p->array[i].end) )
return p->array[i].image;
}
}
}
return NULL;
}
```
여기서는 0x100001008이 속하는 메모리 영역을 구한다.
영역을 구하면 fastBindLazySymbol로 돌아와서 아래의 코드를 수행함
```c
try {
result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext,
(dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->acquireGlobalDyldLock : NULL,
(dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->releaseGlobalDyldLock : NULL);
}
```
imageLoaderCache->doBindFastLazySymbol을 호출하고 결과를 return하는걸로 봐서 이제 디버깅이 끝나가는 것 같다.
여기서 리턴된 값을 `_lazy_symbol_ptr`에서 malloc으로 들어갈 것이다.
그럼 doBindFastLazySymbol소스를 볼까!
```c
uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context,
void (*lock)(), void (*unlock)())
{
// <rdar://problem/8663923> race condition with flat-namespace lazy binding
if ( this->usesTwoLevelNameSpace() ) {
// two-level namespace lookup does not require lock because dependents can't be unloaded before this image
}
else {
// acquire dyld global lock
if ( lock != NULL )
lock();
}
const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off;
const uint8_t* const end = &start[fDyldInfo->lazy_bind_size];
uint8_t segIndex;
uintptr_t segOffset;
int libraryOrdinal;
const char* symbolName;
bool doneAfterBind;
uintptr_t result;
do {
if ( ! getLazyBindingInfo(lazyBindingInfoOffset, start, end, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) )
dyld::throwf("bad lazy bind info");
if ( segIndex >= fSegmentsCount )
dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)",
segIndex, fSegmentsCount-1);
if ( segOffset > segSize(segIndex) )
dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segIndex));
uintptr_t address = segActualLoadAddress(segIndex) + segOffset;
result = this->bindAt(context, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true);
// <rdar://problem/24140465> Some old apps had multiple lazy symbols bound at once
} while (!doneAfterBind && !context.strictMachORequired);
if ( !this->usesTwoLevelNameSpace() ) {
// release dyld global lock
if ( unlock != NULL )
unlock();
}
return result;
}
```
doBindFastLazySymbol에서는 getLazyBindingInfo를 호출하여 bind에 필요한 정보 (e.g. symbolName)등을 가지고 온다.
```
ImageLoaderMachOCompressed::doBindFastLazySymbol @ dyld:
-> 0x100023312: e8 83 91 ff ff call 0x10001c49a ; ImageLoaderMachO::getLazyBindingInfo(unsigned int&, unsigned char const*, unsigned char const*, unsigned char*, unsigned long*, int*, char const**, bool*)
(lldbinit) re r rdi rsi rdx rcx r8 r9
rdi = 0x00007ffeefbff250
rsi = 0x0000000100002020
rdx = 0x0000000100002040
rcx = 0x00007ffeefbff267
r8 = 0x00007ffeefbff258
r9 = 0x00007ffeefbff254
(lldbinit) x/gx $rsp
0x7ffeefbff210: 0x00007ffeefbff228
(lldbinit) x/gx $rsp+8
0x7ffeefbff218: 0x00007ffeefbff266
```
getLazyBindingInfo호출 시의 인자 확인!
```c
bool ImageLoaderMachO::getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd,
uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind)
{
if ( lazyBindingInfoOffset > (lazyInfoEnd-lazyInfoStart) )
return false;
uint8_t type = BIND_TYPE_POINTER;
uint8_t symboFlags = 0;
bool done = false;
const uint8_t* p = &lazyInfoStart[lazyBindingInfoOffset];
while ( !done && (p < lazyInfoEnd) ) {
uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
uint8_t opcode = *p & BIND_OPCODE_MASK;
++p;
switch (opcode) {
case BIND_OPCODE_DONE:
*doneAfterBind = false;
return true;
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
*ordinal = immediate;
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
*ordinal = (int)read_uleb128(p, lazyInfoEnd);
break;
case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
// the special ordinals are negative numbers
if ( immediate == 0 )
*ordinal = 0;
else {
int8_t signExtended = BIND_OPCODE_MASK | immediate;
*ordinal = signExtended;
}
break;
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
*symbolName = (char*)p;
symboFlags = immediate;
while (*p != '\0')
++p;
++p;
break;
case BIND_OPCODE_SET_TYPE_IMM:
type = immediate;
break;
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
*segIndex = immediate;
*segOffset = read_uleb128(p, lazyInfoEnd);
break;
case BIND_OPCODE_DO_BIND:
*doneAfterBind = ((*p & BIND_OPCODE_MASK) == BIND_OPCODE_DONE);
lazyBindingInfoOffset += p - &lazyInfoStart[lazyBindingInfoOffset];
return true;
break;
case BIND_OPCODE_SET_ADDEND_SLEB:
case BIND_OPCODE_ADD_ADDR_ULEB:
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
default:
return false;
}
}
return false;
}
```
자 우선 lazyInfoStart의 주소부터 시작함
```
(lldbinit) x/2gx 0x0000000100002020
0x100002020: 0x6c616d5f40111072 0x1872009000636f6c
```
맨 첫 바이트는 0x72다.
```
uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
uint8_t opcode = *p & BIND_OPCODE_MASK;
```
위 코드에 의해 immediate는 0x2, opcode는 0x70이 된다.
0x70은 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB이다.[Bind Macro](#bind_macro)
그렇기 때문에 아래의 case문에 들어간다.
```c
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
*segIndex = immediate;
*segOffset = read_uleb128(p, lazyInfoEnd);
break;
```
read_uleb128? 뭐지 보자.
```c
static uint64_t
read_uleb128 (const uint8_t ** offset, const uint8_t * end)
{
uint64_t result = 0;
int bit = 0;
do {
uint64_t b;
if (*offset == end)
return (uint64_t) -1;
b = **offset & 0x7f;
if (bit >= 64 || b << bit >> bit != b)
result = (uint64_t) -1;
else
result |= b << bit, bit += 7;
} while (*(*offset)++ >= 0x80);
return result;
}
```
ㅇㅇ 그런가보다.
0x10을 uleb128에서 integer로 변환시키니까 0x10그대로인듯함
segIndex는 0x2, segOffset는 0x10
그 다음은 0x11일 경우니까.. original에 0x1들어감
```c
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
*ordinal = immediate;
break;
```
그 다음은 0x40일 경우다.
```c
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
*symbolName = (char*)p;
symboFlags = immediate;
while (*p != '\0')
++p;
++p;
break;
```
오 이름 주소 가져온다.
0x0000000100002024를 symbolName에 넣을듯.
symbolFlags에는 0이 들어가고, 포인터를 스트링의 끝으로 지정한다.
그러면 마지막은 \x00이므로 함수가 마무리된다.
```c
case BIND_OPCODE_DONE:
*doneAfterBind = false;
return true;
break;
```
이제 리턴하면 대충 이런값들을 가지고 있을것이다.
```
segIndex = 0x02
segOffset = 0x10
libraryOrdinal = 0x1
symbolName = 0x0000000100002024 '_malloc'
doneAfterBind = 0x0
```
자 이제 getLazyBindingInfo함수가 마무리되었으니, 다시 doBindFastLazySymbol로 돌아오면 아래의 코드를 실행할 것이다.
```c
uintptr_t address = segActualLoadAddress(segIndex) + segOffset;
result = this->bindAt(context, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true);
```
address에는 실제 segIndex에 맞는 주소 0x0000000100001000를 리턴해주고 segOffset만큼 더하여 0x0000000100001010의 값을 얻는다.
아래를 보면 알다시피 이 주소는 lazy\_symbol\_ptr에서 \_malloc\_ptr의 주소이다.
```c
__la_symbol_ptr:0000000100001010 ; void *(__cdecl *malloc_ptr)(size_t)
__la_symbol_ptr:0000000100001010 _malloc_ptr dq offset __imp__malloc ; DATA XREF: _malloc↑r
```
이제 이를 바탕으로 bindAt을 함수를 실행한다.
```c
uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName,
uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg,
LastLookup* last, bool runResolver)
{
const ImageLoader* targetImage;
uintptr_t symbolAddress;
// resolve symbol
symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver);
// do actual update
return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg);
}
```
resolve함수에서는 symbolName에 맞는 함수 주소를 반환하여 symbolAddress에 저장함
지금의 경우 symbolAddress는 0x00007FFF6BA7BD61이다.
```c
(lldbinit) x/32i $rax
0x7fff6ba7bd61: 55 push rbp
0x7fff6ba7bd62: 48 89 e5 mov rbp, rsp
0x7fff6ba7bd65: 53 push rbx
0x7fff6ba7bd66: 50 push rax
0x7fff6ba7bd67: 48 89 f8 mov rax, rdi
0x7fff6ba7bd6a: 48 8d 3d 8f d2 7c 36 lea rdi, [rip + 0x367cd28f] ; virtual_default_zone
0x7fff6ba7bd71: 48 89 c6 mov rsi, rax
0x7fff6ba7bd74: e8 1d 00 00 00 call 0x7fff6ba7bd96 ; malloc_zone_malloc
0x7fff6ba7bd79: 48 89 c3 mov rbx, rax
0x7fff6ba7bd7c: 48 85 c0 test rax, rax
0x7fff6ba7bd7f: 75 0b jne 0x7fff6ba7bd8c ; <+43>
0x7fff6ba7bd81: e8 be 21 02 00 call 0x7fff6ba9df44 ; symbol stub for: __error
0x7fff6ba7bd86: c7 00 0c 00 00 00 mov dword ptr [rax], 0xc
0x7fff6ba7bd8c: 48 89 d8 mov rax, rbx
0x7fff6ba7bd8f: 48 83 c4 08 add rsp, 0x8
0x7fff6ba7bd93: 5b pop rbx
0x7fff6ba7bd94: 5d pop rbp
0x7fff6ba7bd95: c3 ret
```
누가 봐도 malloc로직임.
그 후에 드디어 원하는 함수가 bind 된다!! 끝끝
최종 함수 콜 스택(큼직한것만)
```
call malloc
-> dyld_stub_binder @ libdyld.dylib
-> _dyld_fast_stub_entry @ libdyld.dylib
-> _dyld_func_lookup @ libdyld.dylib
-> _dyld_func_lookup @ dyld
-> dyld::fastBindLazySymbol @ dyld
-> ImageLoaderMachOCompressed::doBindFastLazySymbol
-> ImageLoaderMachO::getLazyBindingInfo
-> ImageLoaderMachOCompressed::bindAt
-> ImageLoaderMachOCompressed::resolve
-> ImageLoaderMachOCompressed::bindLocation
```
## Bonus1
```
0x100060050: "__dyld_register_func_for_add_image"
0x100060073: "__dyld_register_func_for_remove_image"
0x100060099: "__dyld_dladdr"
0x1000600a7: "__dyld_dlclose"
0x1000600b6: "__dyld_dlerror"
0x1000600c5: "__dyld_dlopen_internal"
0x1000600dc: "__dyld_dlsym_internal"
0x1000600f2: "__dyld_dlopen_preflight_internal"
0x100060113: "__dyld_dlopen"
0x100060121: "__dyld_dlsym"
0x10006012e: "__dyld_dlopen_preflight"
0x100060146: "__dyld_image_count"
0x100060159: "__dyld_get_image_header"
0x100060171: "__dyld_get_image_vmaddr_slide"
0x10006018f: "__dyld_get_image_name"
0x1000601a5: "__dyld_get_image_slide"
0x1000601bc: "__dyld__NSGetExecutablePath"
0x1000601d8: "__dyld_register_thread_helpers"
0x1000601f7: "__dyld_fork_child"
0x100060209: "__dyld_make_delayed_module_initializer_calls"
0x100060236: "__dyld_set_variable"
0x10006024a: "__dyld_add_image_reexport"
0x100060264: "__dyld_get_all_image_infos"
0x10006027f: "__dyld_find_unwind_sections"
0x10006029b: "__dyld_fast_stub_entry"
0x1000602b2: "__dyld_image_path_containing_address"
0x1000602d7: "__dyld_shared_cache_some_image_overridden"
0x100060301: "__dyld_process_is_restricted"
0x10006031e: "__dyld_dynamic_interpose"
0x100060337: "__dyld_shared_cache_file_path"
0x100060355: "__dyld_get_image_header_containing_address"
0x100060380: "__dyld_is_memory_immutable"
0x10006039b: "__dyld_objc_notify_register"
0x1000603b7: "__dyld_get_shared_cache_uuid"
0x1000603d4: "__dyld_get_shared_cache_range"
0x1000603f2: "__dyld_images_for_addresses"
0x10006040e: "__dyld_register_for_image_loads"
0x10006042e: "__dyld_lookup_and_bind"
0x100060445: "__dyld_lookup_and_bind_with_hint"
0x100060466: "__dyld_lookup_and_bind_fully"
0x100060483: "__dyld_install_handlers"
0x10006049b: "__dyld_link_edit_error"
0x1000604b2: "__dyld_unlink_module"
0x1000604c7: "__dyld_bind_fully_image_containing_address"
0x1000604f2: "__dyld_image_containing_address"
0x100060512: "__dyld_register_binding_handler"
0x100060532: "__dyld_NSNameOfSymbol"
0x100060548: "__dyld_NSAddressOfSymbol"
0x100060561: "__dyld_NSModuleForSymbol"
0x10006057a: "__dyld_NSLookupAndBindSymbol"
0x100060597: "__dyld_NSLookupAndBindSymbolWithHint"
0x1000605bc: "__dyld_NSLookupSymbolInModule"
0x1000605da: "__dyld_NSLookupSymbolInImage"
0x1000605f7: "__dyld_NSMakePrivateModulePublic"
0x100060618: "__dyld_NSIsSymbolNameDefined"
0x100060635: "__dyld_NSIsSymbolNameDefinedWithHint"
0x10006065a: "__dyld_NSIsSymbolNameDefinedInImage"
0x10006067e: "__dyld_NSNameOfModule"
0x100060694: "__dyld_NSLibraryNameForModule"
0x1000606b2: "__dyld_NSAddLibrary"
0x1000606c6: "__dyld_NSAddLibraryWithSearching"
0x1000606e7: "__dyld_NSAddImage"
0x1000606f9: "__dyld_launched_prebound"
0x100060712: "__dyld_all_twolevel_modules_prebound"
0x100060737: "__dyld_call_module_initializers_for_dylib"
0x100060761: "__dyld_NSCreateObjectFileImageFromFile"
0x100060788: "__dyld_NSCreateObjectFileImageFromMemory"
0x1000607b1: "__dyld_NSDestroyObjectFileImage"
0x1000607d1: "__dyld_NSLinkModule"
0x1000607e5: "__dyld_NSSymbolDefinitionCountInObjectFileImage"
0x100060815: "__dyld_NSSymbolDefinitionNameInObjectFileImage"
0x100060844: "__dyld_NSIsSymbolDefinedInObjectFileImage"
0x10006086e: "__dyld_NSSymbolReferenceNameInObjectFileImage"
0x10006089c: "__dyld_NSSymbolReferenceCountInObjectFileImage"
0x1000608cb: "__dyld_NSGetSectionDataInObjectFileImage"
```
## BIND_Macro
```
BIND_OPCODE_DO_BIND 0x90
BIND_OPCODE_DONE 0x00
BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 0x10
BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70
BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40
BIND_OPCODE_SET_TYPE_IMM 0x50
BIND_TYPE_POINTER 0x01
BIND_OPCODE_MASK 0xF0
BIND_IMMEDIATE_MASK 0x0F
```