Raw symbol names in inline assembly
2024-1-30 16:0:0 Author: maskray.me(查看原文) 阅读量:10 收藏

GCC's aarch64 port, from the beginning (2012), supports the constraint "S" to reference a symbol or label. This is nice, as you can create an artificial reference for linker garbage collection, defining sections to hold symbol addresses, or even enabling more creative applications.

1
2
3
4
5
6
7
8
namespace ns { extern int a[4]; }
void fun();
void foo() {
label:
asm(".pushsection .xxx,\"aw\"; .dc.a %0; .popsection" :: "S"(&ns::a[3]));
asm(".reloc ., BFD_RELOC_NONE, %0" :: "S"(fun));
asm("// %0" :: "S"(&&label));
}

Clang 7 also implemented "S".

Using the generic r or m constraint in such cases would instruct GCC to generate instructions to compute the address, which can be wasteful if the materialized address isn't actually needed.

1
2
3
4

asm("// %0" :: "r"(fun));


C++ templates can make this easier to use.

1
2
3
template <class T, T &x>
void ref() { asm (".reloc ., BFD_RELOC_NONE, %0" :: "S"(x)); }
void use() { ref<decltype(ns::a), ns::a>(); }

RISC-V

GCC's RISC-V port, from the beginning (2017), supports the constraint "S" for a similar purpose as well. However, the operand cannot be a preemptible symbol (non-local symbol in GCC's term). I have just filed [PATCH] RISC-V: Allow constraint "S" even if the symbol does not bind locally to relax the condition.

x86

Unfortunately, there had been no suitable constraint for x86 for a long time. We can use the constraint "i" with the modifier "p" to print raw symbol name without syntax-specific prefixes, but it does not work when:

  • -mcmodel=large is used
  • or the symbol is preemptible (similar to RISC-V's "S" problem)
1
2
3
4
5
void foo() {
label:
asm("// %p0" :: "i"(foo));
asm("// %p0" :: "i"(&&label));
}

I filed the feature request for a new constraint in May 2022 and eventually went ahead and implement it by myself. The patch landed today, catching up the GCC 14 release. I have also implemented "Ws" for Clang 18.1.

BTW, you can also the modifier "c".

Require a constant operand and print the constant expression with no punctuation.

I think having such a long list of modifiers is unfortunate.

Applications

Linux kernel's config JUMP_LABEL is a noticeable user of this raw symbol name feature. Many ports just use the constraint "i", which is kinda abuse.

1
2
3
4
5
// gcc/common.md
(define_constraint "i"
"Matches a general integer constant."
(and (match_test "CONSTANT_P (op)")
(match_test "!flag_pic || LEGITIMATE_PIC_OPERAND_P (op)")))

That said, in the non-PIC mode, "i" does works with a constant, symbol reference, or label reference. In the PIC mode, "i" on a symbol reference is rejected by certain GCC ports (e.g. aarch64). I went ahead and sent an arm64 patch:)


文章来源: https://maskray.me/blog/2024-01-30-raw-symbol-names-in-inline-assembly
如有侵权请联系:admin#unsafe.sh