在查看SwiftUI应用程序的反汇编时,我一直看到这些结构:
-> 0x100237af4 <+0>: adrp x16, 29 0x100237af8 <+4>: ldr x16, [x16, #0xcb0] 0x100237afc <+8>: br x16
看起来好像是从内存中的下一页获取地址,然后在那里进行跳转(或br-anch)。)这些的目的是什么?
br
huus2vyu1#
这些是进口存根。长话短说,当你调用一个在同一个二进制中定义的函数时,链接器可以直接将其转换为bl指令,但当你调用另一个二进制中的函数时,这通常是不可能的。首先,由于ASLR,您不知道该二进制文件在内存中相对于您的位置,虽然理论上您可以通过在加载时重写指令来解决这个问题(我相信x86_64 kexts会这样做),这将违反苹果在arm64上严格的协同设计政策,你仍然会被限制在±128MB。这远远不足以到达dyld_shared_cache中的所有Apple库。所以链接器所做的是发出一个序列,如您所示,并使bl到达那里。然后存根从数据页(不受协同设计的影响,因此可以在加载时修改)加载真实的函数的地址并分支到它。如果和当进口被解决(i.即,该数据页上的指针被更新到其最终值)取决于一些因素,包括体系结构(ARM64对ARM64E)、工具链版本、符号是否被标记为弱导入等。一般来说,符号要么是惰性绑定的,要么是非惰性绑定的。在前一种情况下,所有函数指针最初都被设置为指向dyld中的一个函数,这将在第一次使用导入时进行实际解析(如果有的话)。在后一种情况下,所有导入都由dyld在加载时解析。近年来,Apple越来越多地转向后一种模式,因为它允许他们在主二进制文件运行之前将包含函数指针的页面设置为只读,这可以加强安全性。
bl
1条答案
按热度按时间huus2vyu1#
这些是进口存根。
长话短说,当你调用一个在同一个二进制中定义的函数时,链接器可以直接将其转换为
bl
指令,但当你调用另一个二进制中的函数时,这通常是不可能的。首先,由于ASLR,您不知道该二进制文件在内存中相对于您的位置,虽然理论上您可以通过在加载时重写指令来解决这个问题(我相信x86_64 kexts会这样做),这将违反苹果在arm64上严格的协同设计政策,你仍然会被限制在±128MB。这远远不足以到达dyld_shared_cache中的所有Apple库。所以链接器所做的是发出一个序列,如您所示,并使
bl
到达那里。然后存根从数据页(不受协同设计的影响,因此可以在加载时修改)加载真实的函数的地址并分支到它。如果和当进口被解决(i.即,该数据页上的指针被更新到其最终值)取决于一些因素,包括体系结构(ARM64对ARM64E)、工具链版本、符号是否被标记为弱导入等。
一般来说,符号要么是惰性绑定的,要么是非惰性绑定的。在前一种情况下,所有函数指针最初都被设置为指向dyld中的一个函数,这将在第一次使用导入时进行实际解析(如果有的话)。在后一种情况下,所有导入都由dyld在加载时解析。
近年来,Apple越来越多地转向后一种模式,因为它允许他们在主二进制文件运行之前将包含函数指针的页面设置为只读,这可以加强安全性。