tools/bpf/bpftool/link.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-)
From: Yuan Chen <chenyuan@kylinos.cn>
Adjust symbol matching logic to account for Control-flow Enforcement
Technology (CET) on x86/x86_64 systems. CET prefixes functions with
a 4-byte 'endbr' instruction, shifting the actual hook entry point to
symbol + 4.
Changed in PATCH v4:
* Refactor repeated code into a function.
* Add detection for the x86 architecture.
Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
---
tools/bpf/bpftool/link.c | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
index a773e05d5ade..717ca8c5ff83 100644
--- a/tools/bpf/bpftool/link.c
+++ b/tools/bpf/bpftool/link.c
@@ -282,6 +282,28 @@ get_addr_cookie_array(__u64 *addrs, __u64 *cookies, __u32 count)
return data;
}
+static bool
+symbol_matches_target(__u64 sym_addr, __u64 target_addr)
+{
+ if (sym_addr == target_addr)
+ return true;
+
+#if defined(__i386__) || defined(__x86_64__)
+ /*
+ * On x86 architectures with CET (Control-flow Enforcement Technology),
+ * function entry points have a 4-byte 'endbr' instruction prefix.
+ * This causes kprobe hooks to target the address *after* 'endbr'
+ * (symbol address + 4), preserving the CET instruction.
+ * Here we check if the symbol address matches the hook target address minus 4,
+ * indicating a CET-enabled function entry point.
+ */
+ if (sym_addr == target_addr - 4)
+ return true;
+#endif
+
+ return false;
+}
+
static void
show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
{
@@ -307,7 +329,7 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
goto error;
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (!symbol_matches_target(dd.sym_mapping[i].address, data[j].addr))
continue;
jsonw_start_object(json_wtr);
jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
@@ -744,7 +766,7 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
printf("\n\t%-16s %-16s %s", "addr", "cookie", "func [module]");
for (i = 0; i < dd.sym_count; i++) {
- if (dd.sym_mapping[i].address != data[j].addr)
+ if (!symbol_matches_target(dd.sym_mapping[i].address, data[j].addr))
continue;
printf("\n\t%016lx %-16llx %s",
dd.sym_mapping[i].address, data[j].cookie, dd.sym_mapping[i].name);
--
2.25.1
2025-07-22 10:00 UTC+0800 ~ chenyuan_fl@163.com
> From: Yuan Chen <chenyuan@kylinos.cn>
>
> Adjust symbol matching logic to account for Control-flow Enforcement
> Technology (CET) on x86/x86_64 systems. CET prefixes functions with
> a 4-byte 'endbr' instruction, shifting the actual hook entry point to
> symbol + 4.
>
> Changed in PATCH v4:
> * Refactor repeated code into a function.
> * Add detection for the x86 architecture.
>
> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
> ---
> tools/bpf/bpftool/link.c | 26 ++++++++++++++++++++++++--
> 1 file changed, 24 insertions(+), 2 deletions(-)
>
> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
> index a773e05d5ade..717ca8c5ff83 100644
> --- a/tools/bpf/bpftool/link.c
> +++ b/tools/bpf/bpftool/link.c
> @@ -282,6 +282,28 @@ get_addr_cookie_array(__u64 *addrs, __u64 *cookies, __u32 count)
> return data;
> }
>
> +static bool
> +symbol_matches_target(__u64 sym_addr, __u64 target_addr)
> +{
> + if (sym_addr == target_addr)
> + return true;
> +
> +#if defined(__i386__) || defined(__x86_64__)
Do you really need it for __i386__ as well? My understanding was that
CET would apply only to 64-bit?
Thanks,
Quentin
You are absolutely right. My initial assumption was incorrect - while endbr32 can technically be
compiled for i386, I've verified in the kernel configuration that X86_KERNEL_IBT explicitly
depends on X86_64:
.config - Linux/i386 6.16.0-rc3 Kernel Configuration
> Search (X86_KERNEL_IBT) > Processor type and features > Search (X86_KERNEL_IBT)
Symbol: X86_KERNEL_IBT [=n]
Type : bool
Defined at arch/x86/Kconfig:1771
Prompt: Indirect Branch Tracking
Depends on: X86_64 [=n] && CC_HAS_IBT [=y] && HAVE_OBJTOOL [=n] && (!LD_IS_LLD [=n] || LLD_VERSION [=0]>=140000)
This confirms CET is indeed 64-bit exclusive in the current implementation. I'll revise the patch
immediately to remove i386 support.
Thanks for catching this!
Best regards,
Yuan Chen
At 2025-07-22 22:23:23, "Quentin Monnet" <qmo@kernel.org> wrote:
>2025-07-22 10:00 UTC+0800 ~ chenyuan_fl@163.com
>> From: Yuan Chen <chenyuan@kylinos.cn>
>>
>> Adjust symbol matching logic to account for Control-flow Enforcement
>> Technology (CET) on x86/x86_64 systems. CET prefixes functions with
>> a 4-byte 'endbr' instruction, shifting the actual hook entry point to
>> symbol + 4.
>>
>> Changed in PATCH v4:
>> * Refactor repeated code into a function.
>> * Add detection for the x86 architecture.
>>
>> Signed-off-by: Yuan Chen <chenyuan@kylinos.cn>
>> ---
>> tools/bpf/bpftool/link.c | 26 ++++++++++++++++++++++++--
>> 1 file changed, 24 insertions(+), 2 deletions(-)
>>
>> diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
>> index a773e05d5ade..717ca8c5ff83 100644
>> --- a/tools/bpf/bpftool/link.c
>> +++ b/tools/bpf/bpftool/link.c
>> @@ -282,6 +282,28 @@ get_addr_cookie_array(__u64 *addrs, __u64 *cookies, __u32 count)
>> return data;
>> }
>>
>> +static bool
>> +symbol_matches_target(__u64 sym_addr, __u64 target_addr)
>> +{
>> + if (sym_addr == target_addr)
>> + return true;
>> +
>> +#if defined(__i386__) || defined(__x86_64__)
>
>
>Do you really need it for __i386__ as well? My understanding was that
>CET would apply only to 64-bit?
>
>Thanks,
>Quentin
© 2016 - 2026 Red Hat, Inc.