访问 C 文件中的汇编宏函数/指令
Access assembly macro function / directives in a C file
我想知道是否可以在另一个 C 文件中访问像 .directive
这样的汇编指令或像 %macro my_macro
这样的宏
文件:macroasm.S
%macro my_macro 1
mov rsp, 1
%endmacro
有没有什么方法可以在 C 文件中调用和执行 my_macro
宏并用 nasm 和 gcc 编译它们?
与运行时函数调用不同,宏是编译时替换。 asm 和 C 是不同的语言,所以这个问题唯一有意义的方法是你可以从 inline-asm 使用的 asm 宏。
gcc 的 asm 输出必须由 GAS 或兼容的 assembler 理解 GAS 指令 assembled。 (https://sourceware.org/binutils/docs/as/)。内联 asm 允许您将手写内容直接发送到 asm 编译器输出中,成为编译器提供给 assembler.
的完整 assembler 源文件的一部分
使用像 %macro
这样的 NASM 语法不能在 GNU C 内联汇编中工作,因为可以 assemble 常规 gcc 输出的 assembler 无法理解NASM 个指令。
但是如果你愿意,你可以使用 GAS .macro
。 (https://sourceware.org/binutils/docs/as/Macro.html)。我不推荐它; GAS 宏不太好用。与 NASM 相比,语法感觉笨拙。但既然你问了,你就是这样做的。
asm(".include \"macro-defs.S\"");
在 C 的顶部将允许您稍后在该编译单元中使用来自内联 asm 的那些宏。 (假设 gcc 没有对输出 asm 中的内容进行重新排序。)
但是当然你必须知道宏做什么才能为内联汇编语句编写正确的约束,所以它真的不是超级有用。
例子
macro-defs.S
(GAS 语法,而不是 NASM)。也许我应该将其命名为 .s
,因为我们只使用 asm 指令 .include
,而不使用 C 预处理器 #include
。 (这对 C 来说是有问题的:你不能 #include
something inside 双引号字符串。)所以无论如何,我们不能在这里使用 CPP 宏,只能使用 asm宏。
#.altmacro # needed for some things, makes other things harder
#
# clobbers RDX and RAX
.macro fenced_rdtsc64 dst
lfence # make sure earlier stuff is done
rdtsc
lfence # don't allow later stuff to start before time is read
shl , %rdx # allow OoO exec of these with the timed interval
lea (%rax, %rdx), \dst
.endm
# repeats pause n times. Probably not useful, just a silly example.
# for exponential backoff in a spinloop, you want a *runtime* repeat count.
.macro pause_n count
pause # the machine instruction, not a macro
.if \count-1
pause_n "(\count-1)" # recursion is GAS equivalent of NASM %rep
.endif
.endm
这些宏可用于 foo.S
:
.include "macro-defs.S"
# inefficient: the subtraction really only needs to use the low 32 bits of the count
# so using a macro that merges the high half is a waste
.globl foo
foo:
fenced_rdtsc64 %rcx # start
pause_n 4
fenced_rdtsc64 %rax # end
sub %rcx, %rax
ret
并通过来自 main.c
的内联汇编(它也以正常方式调用 foo()
)。
#include <stdio.h>
asm(".include \"macro-defs.S\"");
long long foo(void);
int main(void) {
long long start, end;
asm volatile("fenced_rdtsc64 %[dst]"
: [dst]"=r" (start)
:
: "rax", "rdx" // forces it to avoid these as output regs, unfortunately
);
printf("foo rdtsc ticks: call1 %lld call2 %lld\n", foo(), foo());
asm volatile("fenced_rdtsc64 %[dst]"
: [dst]"=r" (end)
:
: "rax", "rdx");
printf("printf rdtsc ticks: %lld\n", end-start);
}
使用 gcc -O3 -Wall main.c foo.S
编译(我使用 gcc7.3,-fpie 是默认值)。
运行 它与 for i in {1..50};do ./a.out;done
给出这样的输出(在我的 i7-6700k 上,其中 pause
需要大约 100 个核心时钟周期,硬件 P 状态会提高速度有负载时很快):
... (variable number of lines before the frequency shift)
foo rdtsc ticks: call1 3006 call2 3014
printf rdtsc ticks: 727810
foo rdtsc ticks: call1 3006 call2 3022
printf rdtsc ticks: 707376
foo rdtsc ticks: call1 3006 call2 3017
printf rdtsc ticks: 746375
foo rdtsc ticks: call1 3006 call2 3029
printf rdtsc ticks: 684239
foo rdtsc ticks: call1 3006 call2 3010
printf rdtsc ticks: 652724
foo rdtsc ticks: call1 616 call2 620 # gcc chose to evalute from right to left
printf rdtsc ticks: 133282
foo rdtsc ticks: call1 618 call2 618 # so call1 is with it hot in uop cache
printf rdtsc ticks: 133984
foo rdtsc ticks: call1 616 call2 618
printf rdtsc ticks: 133284
foo rdtsc ticks: call1 614 call2 618
foo
的asm,如果我们disassemble(用objdump -drwC -Mintel a.out
)看看宏是如何扩展的:
# I maybe should have used AT&T syntax disassembly like the source
# You can do that if you want, on your own desktop, leaving out -Mintel
00000000000006ba <foo>:
6ba: 0f ae e8 lfence
6bd: 0f 31 rdtsc
6bf: 0f ae e8 lfence
6c2: 48 c1 e2 20 shl rdx,0x20
6c6: 48 8d 0c 10 lea rcx,[rax+rdx*1] # macro expanded with RCX
6ca: f3 90 pause # pause_n 4 expanded to 4 pause instructions
6cc: f3 90 pause
6ce: f3 90 pause
6d0: f3 90 pause
6d2: 0f ae e8 lfence
6d5: 0f 31 rdtsc
6d7: 0f ae e8 lfence
6da: 48 c1 e2 20 shl rdx,0x20
6de: 48 8d 04 10 lea rax,[rax+rdx*1] # macro expanded with RAX
6e2: 48 29 c8 sub rax,rcx
6e5: c3 ret
编译器生成的 asm(包括我们的内联 asm)是:
0000000000000540 <main>:
540: 55 push rbp
541: 53 push rbx
542: 48 83 ec 08 sub rsp,0x8
546: 0f ae e8 lfence # first inline asm
549: 0f 31 rdtsc
54b: 0f ae e8 lfence
54e: 48 c1 e2 20 shl rdx,0x20
552: 48 8d 1c 10 lea rbx,[rax+rdx*1] # The compiler picked RBX for the output operand
# and substituted fenced_rdtsc64 %rbx into the asm template
556: e8 5f 01 00 00 call 6ba <foo>
55b: 48 89 c5 mov rbp,rax # save the return value, not a macro so it couldn't ask for a more convenient register
55e: e8 57 01 00 00 call 6ba <foo>
563: 48 89 ea mov rdx,rbp
566: 48 8d 3d 0b 02 00 00 lea rdi,[rip+0x20b] # 778 <_IO_stdin_used+0x8> # the string literal
56d: 48 89 c6 mov rsi,rax
570: 31 c0 xor eax,eax
572: e8 b9 ff ff ff call 530 <printf@plt>
577: 0f ae e8 lfence # 2nd inline asm
57a: 0f 31 rdtsc
57c: 0f ae e8 lfence
57f: 48 c1 e2 20 shl rdx,0x20
583: 48 8d 34 10 lea rsi,[rax+rdx*1] # compiler picked RSI this time
587: 48 8d 3d 1a 02 00 00 lea rdi,[rip+0x21a] # 7a8 <_IO_stdin_used+0x38>
58e: 48 29 de sub rsi,rbx # where it wanted it as the 2nd arg to printf(.., end-start)
591: 31 c0 xor eax,eax
593: e8 98 ff ff ff call 530 <printf@plt>
598: 48 83 c4 08 add rsp,0x8
59c: 31 c0 xor eax,eax
59e: 5b pop rbx
59f: 5d pop rbp
5a0: c3 ret
我想知道是否可以在另一个 C 文件中访问像 .directive
这样的汇编指令或像 %macro my_macro
这样的宏
文件:macroasm.S
%macro my_macro 1
mov rsp, 1
%endmacro
有没有什么方法可以在 C 文件中调用和执行 my_macro
宏并用 nasm 和 gcc 编译它们?
与运行时函数调用不同,宏是编译时替换。 asm 和 C 是不同的语言,所以这个问题唯一有意义的方法是你可以从 inline-asm 使用的 asm 宏。
gcc 的 asm 输出必须由 GAS 或兼容的 assembler 理解 GAS 指令 assembled。 (https://sourceware.org/binutils/docs/as/)。内联 asm 允许您将手写内容直接发送到 asm 编译器输出中,成为编译器提供给 assembler.
的完整 assembler 源文件的一部分使用像 %macro
这样的 NASM 语法不能在 GNU C 内联汇编中工作,因为可以 assemble 常规 gcc 输出的 assembler 无法理解NASM 个指令。
但是如果你愿意,你可以使用 GAS .macro
。 (https://sourceware.org/binutils/docs/as/Macro.html)。我不推荐它; GAS 宏不太好用。与 NASM 相比,语法感觉笨拙。但既然你问了,你就是这样做的。
asm(".include \"macro-defs.S\"");
在 C 的顶部将允许您稍后在该编译单元中使用来自内联 asm 的那些宏。 (假设 gcc 没有对输出 asm 中的内容进行重新排序。)
但是当然你必须知道宏做什么才能为内联汇编语句编写正确的约束,所以它真的不是超级有用。
例子
macro-defs.S
(GAS 语法,而不是 NASM)。也许我应该将其命名为 .s
,因为我们只使用 asm 指令 .include
,而不使用 C 预处理器 #include
。 (这对 C 来说是有问题的:你不能 #include
something inside 双引号字符串。)所以无论如何,我们不能在这里使用 CPP 宏,只能使用 asm宏。
#.altmacro # needed for some things, makes other things harder
#
# clobbers RDX and RAX
.macro fenced_rdtsc64 dst
lfence # make sure earlier stuff is done
rdtsc
lfence # don't allow later stuff to start before time is read
shl , %rdx # allow OoO exec of these with the timed interval
lea (%rax, %rdx), \dst
.endm
# repeats pause n times. Probably not useful, just a silly example.
# for exponential backoff in a spinloop, you want a *runtime* repeat count.
.macro pause_n count
pause # the machine instruction, not a macro
.if \count-1
pause_n "(\count-1)" # recursion is GAS equivalent of NASM %rep
.endif
.endm
这些宏可用于 foo.S
:
.include "macro-defs.S"
# inefficient: the subtraction really only needs to use the low 32 bits of the count
# so using a macro that merges the high half is a waste
.globl foo
foo:
fenced_rdtsc64 %rcx # start
pause_n 4
fenced_rdtsc64 %rax # end
sub %rcx, %rax
ret
并通过来自 main.c
的内联汇编(它也以正常方式调用 foo()
)。
#include <stdio.h>
asm(".include \"macro-defs.S\"");
long long foo(void);
int main(void) {
long long start, end;
asm volatile("fenced_rdtsc64 %[dst]"
: [dst]"=r" (start)
:
: "rax", "rdx" // forces it to avoid these as output regs, unfortunately
);
printf("foo rdtsc ticks: call1 %lld call2 %lld\n", foo(), foo());
asm volatile("fenced_rdtsc64 %[dst]"
: [dst]"=r" (end)
:
: "rax", "rdx");
printf("printf rdtsc ticks: %lld\n", end-start);
}
使用 gcc -O3 -Wall main.c foo.S
编译(我使用 gcc7.3,-fpie 是默认值)。
运行 它与 for i in {1..50};do ./a.out;done
给出这样的输出(在我的 i7-6700k 上,其中 pause
需要大约 100 个核心时钟周期,硬件 P 状态会提高速度有负载时很快):
... (variable number of lines before the frequency shift)
foo rdtsc ticks: call1 3006 call2 3014
printf rdtsc ticks: 727810
foo rdtsc ticks: call1 3006 call2 3022
printf rdtsc ticks: 707376
foo rdtsc ticks: call1 3006 call2 3017
printf rdtsc ticks: 746375
foo rdtsc ticks: call1 3006 call2 3029
printf rdtsc ticks: 684239
foo rdtsc ticks: call1 3006 call2 3010
printf rdtsc ticks: 652724
foo rdtsc ticks: call1 616 call2 620 # gcc chose to evalute from right to left
printf rdtsc ticks: 133282
foo rdtsc ticks: call1 618 call2 618 # so call1 is with it hot in uop cache
printf rdtsc ticks: 133984
foo rdtsc ticks: call1 616 call2 618
printf rdtsc ticks: 133284
foo rdtsc ticks: call1 614 call2 618
foo
的asm,如果我们disassemble(用objdump -drwC -Mintel a.out
)看看宏是如何扩展的:
# I maybe should have used AT&T syntax disassembly like the source
# You can do that if you want, on your own desktop, leaving out -Mintel
00000000000006ba <foo>:
6ba: 0f ae e8 lfence
6bd: 0f 31 rdtsc
6bf: 0f ae e8 lfence
6c2: 48 c1 e2 20 shl rdx,0x20
6c6: 48 8d 0c 10 lea rcx,[rax+rdx*1] # macro expanded with RCX
6ca: f3 90 pause # pause_n 4 expanded to 4 pause instructions
6cc: f3 90 pause
6ce: f3 90 pause
6d0: f3 90 pause
6d2: 0f ae e8 lfence
6d5: 0f 31 rdtsc
6d7: 0f ae e8 lfence
6da: 48 c1 e2 20 shl rdx,0x20
6de: 48 8d 04 10 lea rax,[rax+rdx*1] # macro expanded with RAX
6e2: 48 29 c8 sub rax,rcx
6e5: c3 ret
编译器生成的 asm(包括我们的内联 asm)是:
0000000000000540 <main>:
540: 55 push rbp
541: 53 push rbx
542: 48 83 ec 08 sub rsp,0x8
546: 0f ae e8 lfence # first inline asm
549: 0f 31 rdtsc
54b: 0f ae e8 lfence
54e: 48 c1 e2 20 shl rdx,0x20
552: 48 8d 1c 10 lea rbx,[rax+rdx*1] # The compiler picked RBX for the output operand
# and substituted fenced_rdtsc64 %rbx into the asm template
556: e8 5f 01 00 00 call 6ba <foo>
55b: 48 89 c5 mov rbp,rax # save the return value, not a macro so it couldn't ask for a more convenient register
55e: e8 57 01 00 00 call 6ba <foo>
563: 48 89 ea mov rdx,rbp
566: 48 8d 3d 0b 02 00 00 lea rdi,[rip+0x20b] # 778 <_IO_stdin_used+0x8> # the string literal
56d: 48 89 c6 mov rsi,rax
570: 31 c0 xor eax,eax
572: e8 b9 ff ff ff call 530 <printf@plt>
577: 0f ae e8 lfence # 2nd inline asm
57a: 0f 31 rdtsc
57c: 0f ae e8 lfence
57f: 48 c1 e2 20 shl rdx,0x20
583: 48 8d 34 10 lea rsi,[rax+rdx*1] # compiler picked RSI this time
587: 48 8d 3d 1a 02 00 00 lea rdi,[rip+0x21a] # 7a8 <_IO_stdin_used+0x38>
58e: 48 29 de sub rsi,rbx # where it wanted it as the 2nd arg to printf(.., end-start)
591: 31 c0 xor eax,eax
593: e8 98 ff ff ff call 530 <printf@plt>
598: 48 83 c4 08 add rsp,0x8
59c: 31 c0 xor eax,eax
59e: 5b pop rbx
59f: 5d pop rbp
5a0: c3 ret