---
# System prepended metadata

title: OSX Lazy Symbol Pointer Binding

---

# 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
```