aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSilvio Traversaro <silvio@traversaro.it>2022-12-28 15:23:51 +0100
committerGitHub <noreply@github.com>2022-12-28 15:23:51 +0100
commit6444294ee354796536c34b54e154c0538a4d1eaf (patch)
treeae319ec881c2ffb7ccdfe4c0cadad1cb60a53c26
parent8dceebdac727a2f9d7022a71b7800589e662d9b8 (diff)
parentd78c6266893b44d3bd9662a4cf387d79fdb7153f (diff)
downloaddlfcn-win32-6444294ee354796536c34b54e154c0538a4d1eaf.tar.gz
dlfcn-win32-6444294ee354796536c34b54e154c0538a4d1eaf.tar.bz2
dlfcn-win32-6444294ee354796536c34b54e154c0538a4d1eaf.zip
Merge pull request #107 from driver1998/arm64
Add Windows ARM64 support
-rw-r--r--src/dlfcn.c64
-rw-r--r--tests/test-dladdr.c9
2 files changed, 71 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__)
684static 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 */
690static BOOL is_import_thunk( const void *addr ) 717static 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 )
698static void *get_address_from_import_address_table( void *iat, DWORD iat_size, const void *addr ) 736static 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;
diff --git a/tests/test-dladdr.c b/tests/test-dladdr.c
index e2f6c01..5568f23 100644
--- a/tests/test-dladdr.c
+++ b/tests/test-dladdr.c
@@ -140,12 +140,21 @@ __declspec(dllexport)
140#endif 140#endif
141int main(int argc, char **argv) 141int main(int argc, char **argv)
142{ 142{
143#if defined(_M_ARM64) || defined(__aarch64__)
144 /* points to non reachable address */
145 unsigned char zero_thunk_address[12] = { 0x10, 0x00, 0x00, 0x90, 0x10, 0x02, 0x40, 0xF9, 0x00, 0x02, 0x1F, 0xD6 };
146 /* points to executable base */
147 unsigned char invalid_thunk_address[12] = { 0x10, 0x00, 0x00, 0xb0, 0x10, 0x06, 0x47, 0xF9, 0x00, 0x02, 0x1F, 0xD6 };
148 /* no import thunk */
149 unsigned char no_import_thunk[12] = { 0x11, 0x00, 0x00, 0xb0, 0x31, 0x06, 0x47, 0xF9, 0x20, 0x02, 0x1F, 0xD6 };
150#else
143 /* points to non reachable address */ 151 /* points to non reachable address */
144 unsigned char zero_thunk_address[6] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; 152 unsigned char zero_thunk_address[6] = { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 };
145 /* points to executable base */ 153 /* points to executable base */
146 unsigned char invalid_thunk_address[6] = { 0xFF, 0x25, 0x00, 0x00, 0x40, 0x00 }; 154 unsigned char invalid_thunk_address[6] = { 0xFF, 0x25, 0x00, 0x00, 0x40, 0x00 };
147 /* no import thunk */ 155 /* no import thunk */
148 unsigned char no_import_thunk[6] = { 0xFF, 0x26, 0x00, 0x00, 0x40, 0x00 }; 156 unsigned char no_import_thunk[6] = { 0xFF, 0x26, 0x00, 0x00, 0x40, 0x00 };
157#endif
149 int result = 0; 158 int result = 0;
150 UNUSED(argv); 159 UNUSED(argv);
151 160