diff options
| author | GH Cao <driver1998.ms@outlook.com> | 2022-12-08 21:37:10 +0800 |
|---|---|---|
| committer | GH Cao <driver1998.ms@outlook.com> | 2022-12-27 22:10:21 +0800 |
| commit | d78c6266893b44d3bd9662a4cf387d79fdb7153f (patch) | |
| tree | ae319ec881c2ffb7ccdfe4c0cadad1cb60a53c26 /src | |
| parent | 8dceebdac727a2f9d7022a71b7800589e662d9b8 (diff) | |
| download | dlfcn-win32-d78c6266893b44d3bd9662a4cf387d79fdb7153f.tar.gz dlfcn-win32-d78c6266893b44d3bd9662a4cf387d79fdb7153f.tar.bz2 dlfcn-win32-d78c6266893b44d3bd9662a4cf387d79fdb7153f.zip | |
Add Windows ARM64 support
Diffstat (limited to 'src')
| -rw-r--r-- | src/dlfcn.c | 64 |
1 files changed, 62 insertions, 2 deletions
diff --git a/src/dlfcn.c b/src/dlfcn.c index 3a7ca64..1402df2 100644 --- a/src/dlfcn.c +++ b/src/dlfcn.c | |||
| @@ -680,16 +680,54 @@ static BOOL is_valid_address( const void *addr ) | |||
| 680 | return TRUE; | 680 | return TRUE; |
| 681 | } | 681 | } |
| 682 | 682 | ||
| 683 | #if defined(_M_ARM64) || defined(__aarch64__) | ||
| 684 | static INT64 sign_extend(UINT64 value, UINT bits) | ||
| 685 | { | ||
| 686 | const UINT left = 64 - bits; | ||
| 687 | const INT64 m1 = -1; | ||
| 688 | const INT64 wide = (INT64) (value << left); | ||
| 689 | const INT64 sign = ( wide < 0 ) ? ( m1 << left ) : 0; | ||
| 690 | |||
| 691 | return value | sign; | ||
| 692 | } | ||
| 693 | #endif | ||
| 694 | |||
| 683 | /* Return state if address points to an import thunk | 695 | /* Return state if address points to an import thunk |
| 684 | * | 696 | * |
| 685 | * An import thunk is setup with a 'jmp' instruction followed by an | 697 | * On x86, an import thunk is setup with a 'jmp' instruction followed by an |
| 686 | * absolute address (32bit) or relative offset (64bit) pointing into | 698 | * absolute address (32bit) or relative offset (64bit) pointing into |
| 687 | * the import address table (iat), which is partially maintained by | 699 | * the import address table (iat), which is partially maintained by |
| 688 | * the runtime linker. | 700 | * the runtime linker. |
| 701 | * | ||
| 702 | * On ARM64, an import thunk is also a relative jump pointing into the | ||
| 703 | * import address table, implemented by the following three instructions: | ||
| 704 | * | ||
| 705 | * adrp x16, [page_offset] | ||
| 706 | * Calculates the page address (aligned to 4KB) the IAT is at, based | ||
| 707 | * on the value of x16, with page_offset. | ||
| 708 | * | ||
| 709 | * ldr x16, [x16, offset] | ||
| 710 | * Calculates the final IAT address, x16 <- x16 + offset. | ||
| 711 | * | ||
| 712 | * br x16 | ||
| 713 | * Jump to the address in x16. | ||
| 714 | * | ||
| 715 | * The register used here is hardcoded to be x16. | ||
| 689 | */ | 716 | */ |
| 690 | static BOOL is_import_thunk( const void *addr ) | 717 | static BOOL is_import_thunk( const void *addr ) |
| 691 | { | 718 | { |
| 719 | #if defined(_M_ARM64) || defined(__aarch64__) | ||
| 720 | ULONG opCode1 = * (ULONG *) ( (BYTE *) addr ); | ||
| 721 | ULONG opCode2 = * (ULONG *) ( (BYTE *) addr + 4 ); | ||
| 722 | ULONG opCode3 = * (ULONG *) ( (BYTE *) addr + 8 ); | ||
| 723 | |||
| 724 | return (opCode1 & 0x9f00001f) == 0x90000010 /* adrp x16, [page_offset] */ | ||
| 725 | && (opCode2 & 0xffe003ff) == 0xf9400210 /* ldr x16, [x16, offset] */ | ||
| 726 | && opCode3 == 0xd61f0200 /* br x16 */ | ||
| 727 | ? TRUE : FALSE; | ||
| 728 | #else | ||
| 692 | return *(short *) addr == 0x25ff ? TRUE : FALSE; | 729 | return *(short *) addr == 0x25ff ? TRUE : FALSE; |
| 730 | #endif | ||
| 693 | } | 731 | } |
| 694 | 732 | ||
| 695 | /* Return adress from the import address table (iat), | 733 | /* Return adress from the import address table (iat), |
| @@ -698,11 +736,32 @@ static BOOL is_import_thunk( const void *addr ) | |||
| 698 | static void *get_address_from_import_address_table( void *iat, DWORD iat_size, const void *addr ) | 736 | static void *get_address_from_import_address_table( void *iat, DWORD iat_size, const void *addr ) |
| 699 | { | 737 | { |
| 700 | BYTE *thkp = (BYTE *) addr; | 738 | BYTE *thkp = (BYTE *) addr; |
| 739 | #if defined(_M_ARM64) || defined(__aarch64__) | ||
| 740 | /* | ||
| 741 | * typical import thunk in ARM64: | ||
| 742 | * 0x7ff772ae78c0 <+25760>: adrp x16, 1 | ||
| 743 | * 0x7ff772ae78c4 <+25764>: ldr x16, [x16, #0xdc0] | ||
| 744 | * 0x7ff772ae78c8 <+25768>: br x16 | ||
| 745 | */ | ||
| 746 | ULONG opCode1 = * (ULONG *) ( (BYTE *) addr ); | ||
| 747 | ULONG opCode2 = * (ULONG *) ( (BYTE *) addr + 4 ); | ||
| 748 | |||
| 749 | /* Extract the offset from adrp instruction */ | ||
| 750 | UINT64 pageLow2 = (opCode1 >> 29) & 3; | ||
| 751 | UINT64 pageHigh19 = (opCode1 >> 5) & ~(~0ull << 19); | ||
| 752 | INT64 page = sign_extend((pageHigh19 << 2) | pageLow2, 21) << 12; | ||
| 753 | |||
| 754 | /* Extract the offset from ldr instruction */ | ||
| 755 | UINT64 offset = ((opCode2 >> 10) & ~(~0ull << 12)) << 3; | ||
| 756 | |||
| 757 | /* Calculate the final address */ | ||
| 758 | BYTE *ptr = (BYTE *) ( (ULONG64) thkp & ~0xfffull ) + page + offset; | ||
| 759 | #else | ||
| 701 | /* Get offset from thunk table (after instruction 0xff 0x25) | 760 | /* Get offset from thunk table (after instruction 0xff 0x25) |
| 702 | * 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00 | 761 | * 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00 |
| 703 | */ | 762 | */ |
| 704 | ULONG offset = *(ULONG *)( thkp + 2 ); | 763 | ULONG offset = *(ULONG *)( thkp + 2 ); |
| 705 | #ifdef _WIN64 | 764 | #if defined(_M_AMD64) || defined(__x86_64__) |
| 706 | /* On 64 bit the offset is relative | 765 | /* On 64 bit the offset is relative |
| 707 | * 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery> | 766 | * 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery> |
| 708 | * And can be also negative (MSVC in WDK) | 767 | * And can be also negative (MSVC in WDK) |
| @@ -716,6 +775,7 @@ static void *get_address_from_import_address_table( void *iat, DWORD iat_size, c | |||
| 716 | */ | 775 | */ |
| 717 | BYTE *ptr = (BYTE *) offset; | 776 | BYTE *ptr = (BYTE *) offset; |
| 718 | #endif | 777 | #endif |
| 778 | #endif | ||
| 719 | 779 | ||
| 720 | if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size ) | 780 | if( !is_valid_address( ptr ) || ptr < (BYTE *) iat || ptr > (BYTE *) iat + iat_size ) |
| 721 | return NULL; | 781 | return NULL; |
