它不是真正的“汇编”,因为这个宏本身没有指令。
它只是一个插入instr
(传递给宏的指令)的宏,它具有一个输入操作数from
、一个立即(常量)输入操作数type
和一个输出操作数to
。
在特定的二进制部分中也有记录pushsection
该指令地址的部分。如果内核愿意,这允许内核在其代码中找到这些位置。popsection
pv_table
最后一部分是 asm 约束和操作数。它列出了编译器将替换%0
和替换%1
的内容%2
。 %0
是第一个列出的 ( "=r"(to)
),这意味着 %0 将是任何通用寄存器,即存储在宏参数中的输出操作数to
。其他 2 个是相似的,除了它们是输入操作数:from
是一个寄存器,所以得到"r"
,但是type
是立即数,所以是"i"
有关详细信息,请参阅http://gcc.gnu.org/onlinedocs/gcc-4.8.1/gcc/Extended-Asm.html#Extended-Asm
所以考虑内核中的这段代码(http://lxr.linux.no/linux+v3.9.4/arch/arm/include/asm/memory.h#L172)
static inline unsigned long __virt_to_phys(unsigned long x)
{ unsigned long t;
__pv_stub(x, t, "add", __PV_BITS_31_24);
return t;
}
__pv_stub
将等同于t = x + __PV_BITS_31_24
( instr
== add
, from
== x
, to
== t
, type
== __PV_BITS_31_24
)
所以你可能想知道为什么有人会做这么复杂的事情而不是只写t = x + __PV_BITS_31_24
代码。
原因就是我上面提到的pv_table。所有这些语句的地址都记录在特定的 elf 部分中。在某些情况下,内核会在运行时修补这些指令(因此需要能够轻松找到所有指令),因此需要一个表。
ARM 端口正是在这里完成的:http: //lxr.linux.no/linux+v3.9.4/arch/arm/kernel/head.S#L541
只有在使用 CONFIG_ARM_PATCH_PHYS_VIRT 编译内核时才使用它:
CONFIG_ARM_PATCH_PHYS_VIRT:
Patch phys-to-virt and virt-to-phys translation functions at
boot and module load time according to the position of the
kernel in system memory.
This can only be used with non-XIP MMU kernels where the base
of physical memory is at a 16MB boundary, or theoretically 64K
for the MSM machine class.