• [PATCH 1/2] m68k: Use vDSO for thread pointer access

    From Stefan@3:633/10 to All on Thursday, January 08, 2026 07:50:01
    Optimize __m68k_read_tp() to read the thread pointer directly from
    the vDSO data page when available, avoiding the get_thread_area
    syscall overhead.

    The kernel maintains the thread pointer in a data page located at
    a fixed offset (PAGE_SIZE) before the vDSO ELF header. This value
    is updated on every context switch and set_thread_area() call.

    The implementation uses lazy initialization: on first call, it
    checks AT_SYSINFO_EHDR from auxv to locate the vDSO. If the vDSO
    is not available (older kernels), it falls back to the syscall.

    Reviewed-by:
    Tested-by:

    * sysdeps/unix/sysv/linux/m68k/m68k-helpers.c: Include stdint.h
    and sys/auxv.h.
    (struct m68k_vdso_data): New struct matching kernel layout.
    (__m68k_vdso_data): New static variable for cached vDSO pointer.
    (__m68k_vdso_get): New helper to initialize and return vDSO
    data pointer.
    (__m68k_read_tp): Use vDSO fast path when available, fall back
    to syscall otherwise.
    ---
    sysdeps/unix/sysv/linux/m68k/m68k-helpers.c | 77 ++++++++++++++++++++-
    1 file changed, 76 insertions(+), 1 deletion(-)

    diff --git a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c b/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    index 845aae51..1dee48e5 100644
    --- a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    +++ b/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    @@ -17,9 +17,84 @@
    <https://www.gnu.org/licenses/>. */

    #include <sysdep.h>
    +#include <stdint.h>
    +#include <sys/auxv.h>

    +/*
    + * vDSO data structure - must match the kernel's m68k_vdso_data struct.
    + * The TLS pointer is at offset 0 in the vDSO data page.
    + */
    +struct m68k_vdso_data
    +{
    + unsigned long tls_ptr;
    + unsigned long reserved[3];
    +};
    +
    +/*
    + * Pointer to the vDSO data page.
    + * 0 = not yet initialized
    + * -1 = vDSO not available (fallback to syscall)
    + * other = valid vDSO data page address
    + */
    +static volatile struct m68k_vdso_data *__m68k_vdso_data;
    +
    +/*
    + * Initialize the vDSO pointer on first use.
  • From Florian Weimer@3:633/10 to All on Thursday, January 08, 2026 15:00:01
    * Stefan:

    +/*
    + * Initialize the vDSO pointer on first use.
    + * Returns the pointer to the vDSO data, or NULL if not available.
    + */
    +static inline struct m68k_vdso_data *
    +__m68k_vdso_get (void)
    +{
    + struct m68k_vdso_data *vdso = (struct m68k_vdso_data *) __m68k_vdso_data; +
    + if (__glibc_unlikely (vdso == NULL))
    + {
    + /* Not yet initialized - try to get vDSO base from auxv */
    + unsigned long sysinfo_ehdr = __getauxval (AT_SYSINFO_EHDR);
    + if (sysinfo_ehdr != 0)
    + {
    + /*
    + * The kernel passes the address of the vDSO ELF header (code page). + * The vDSO memory layout is:
    + * base + 0x0000: vvar (data page with TLS pointer, clock state)
    + * base + 0x1000: timer page (hardware timer registers)
    + * base + 0x2000: vDSO code (ELF, AT_SYSINFO_EHDR points here)
    + * So the data page is 2 * PAGE_SIZE before the code page.
    + */
    + vdso = (struct m68k_vdso_data *) (sysinfo_ehdr - 2 * 4096);
    + }
    + else
    + {
    + /* vDSO not available - mark as unavailable */
    + vdso = (struct m68k_vdso_data *) (uintptr_t) -1;
    + }
    + __m68k_vdso_data = vdso;
    + }

    Is this really the documented userspace interface? I would expect that
    we'd have to do a vDSO call to read the thread pointer.

    Thanks,
    Florian

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Adhemerval Zanella Netto@3:633/10 to All on Thursday, January 08, 2026 15:30:02


    On 08/01/26 03:18, Stefan wrote:
    Optimize __m68k_read_tp() to read the thread pointer directly from
    the vDSO data page when available, avoiding the get_thread_area
    syscall overhead.

    The kernel maintains the thread pointer in a data page located at
    a fixed offset (PAGE_SIZE) before the vDSO ELF header. This value
    is updated on every context switch and set_thread_area() call.

    The implementation uses lazy initialization: on first call, it
    checks AT_SYSINFO_EHDR from auxv to locate the vDSO. If the vDSO
    is not available (older kernels), it falls back to the syscall.


    When was the m68k vDSO support added on Linus tree? I removed some
    ancient code that was added based on a out-of-tree patches (commit 112a0ae18b831bf31f44d81b82666980312511d6) and afaik m68k vDSO support
    was never added on Linux.

    And booting a 6.19.0-rc4+ on qemu I don't see any vDSO page:

    $ uname -a
    Linux debian-m68k 6.19.0-rc4+ #3 Thu Jan 8 10:55:14 -03 2026 m68k GNU/Linux
    $ cat /proc/self/maps
    80000000-80007000 r-xp 00000000 08:02 261516 /usr/bin/cat
    80009000-8000a000 r--p 00007000 08:02 261516 /usr/bin/cat
    8000a000-8000b000 rw-p 00008000 08:02 261516 /usr/bin/cat
    8000b000-8002c000 rwxp 00000000 00:00 0 [heap]
    c0000000-c0021000 r-xp 00000000 08:02 269185 /usr/lib/m68k-linux-gnu/ld.so.1
    c0022000-c0024000 r--p 00022000 08:02 269185 /usr/lib/m68k-linux-gnu/ld.so.1
    c0024000-c0025000 rw-p 00024000 08:02 269185 /usr/lib/m68k-linux-gnu/ld.so.1
    c0027000-c0029000 rw-p 00000000 00:00 0
    c002a000-c01a8000 r-xp 00000000 08:02 269188 /usr/lib/m68k-linux-gnu/libc.so.6
    c01a8000-c01a9000 ---p 0017e000 08:02 269188 /usr/lib/m68k-linux-gnu/libc.so.6
    c01a9000-c01ae000 r--p 0017f000 08:02 269188 /usr/lib/m68k-linux-gnu/libc.so.6
    c01ae000-c01af000 rw-p 00184000 08:02 269188 /usr/lib/m68k-linux-gnu/libc.so.6
    c01af000-c01b9000 rw-p 00000000 00:00 0
    c01b9000-c03b9000 r--p 00000000 08:02 265523 /usr/lib/locale/locale-archive c03b9000-c03db000 rw-p 00000000 00:00 0
    ef9bf000-ef9e0000 rw-p 00000000 00:00 0 [stack]

    Even the qemu user emulation does not provide vDSO support:

    $ qemu-m68k ./elf/ld.so --library-path . /tmp/t-m68k
    80000000-80001000 r-xp 00000000 103:04 393350 /tmp/t-m68k
    80003000-80004000 r--p 00001000 103:04 393350 /tmp/t-m68k
    80004000-80005000 rw-p 00002000 103:04 393350 /tmp/t-m68k
    c0000000-c0001000 ---p 00000000 00:00 0
    c0001000-c0801000 rw-p 00000000 00:00 0 [stack]
    c0801000-c0802000 r-xp 00000000 00:00 0
    c0802000-c0804000 rw-p 00000000 00:00 0
    c0804000-c09ae000 r-xp 00000000 103:03 40012242 /home/azanella/Projects/glibc/build/m68k-linux-gnu/libc.so
    c09ae000-c09af000 ---p 001aa000 103:03 40012242 /home/azanella/Projects/glibc/build/m68k-linux-gnu/libc.so
    c09af000-c09b4000 r--p 001a9000 103:03 40012242 /home/azanella/Projects/glibc/build/m68k-linux-gnu/libc.so
    c09b4000-c09b5000 rw-p 001ae000 103:03 40012242 /home/azanella/Projects/glibc/build/m68k-linux-gnu/libc.so
    c09b5000-c09bf000 rw-p 00000000 00:00 0
    d0000000-d0023000 r-xp 00000000 103:03 40012241 /home/azanella/Projects/glibc/build/m68k-linux-gnu/elf/ld.so
    d0023000-d0024000 ---p 00000000 00:00 0
    d0024000-d0026000 r--p 00022000 103:03 40012241 /home/azanella/Projects/glibc/build/m68k-linux-gnu/elf/ld.so
    d0026000-d0027000 rw-p 00024000 103:03 40012241 /home/azanella/Projects/glibc/build/m68k-linux-gnu/elf/ld.so
    d0027000-d0048000 rw-p 00000000 00:00 0 [heap]

    Reviewed-by:
    Tested-by:

    I think you missed the entries here.


    * sysdeps/unix/sysv/linux/m68k/m68k-helpers.c: Include stdint.h
    and sys/auxv.h.
    (struct m68k_vdso_data): New struct matching kernel layout.
    (__m68k_vdso_data): New static variable for cached vDSO pointer.
    (__m68k_vdso_get): New helper to initialize and return vDSO
    data pointer.
    (__m68k_read_tp): Use vDSO fast path when available, fall back
    to syscall otherwise.

    No need to add a CL anymore (it is autogenerated during release).

    ---
    sysdeps/unix/sysv/linux/m68k/m68k-helpers.c | 77 ++++++++++++++++++++-
    1 file changed, 76 insertions(+), 1 deletion(-)

    diff --git a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c b/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    index 845aae51..1dee48e5 100644
    --- a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    +++ b/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    @@ -17,9 +17,84 @@
    <https://www.gnu.org/licenses/>. */

    #include <sysdep.h>
    +#include <stdint.h>
    +#include <sys/auxv.h>

    +/*
    + * vDSO data structure - must match the kernel's m68k_vdso_data struct.
    + * The TLS pointer is at offset 0 in the vDSO data page.
    + */
    +struct m68k_vdso_data
    +{
    + unsigned long tls_ptr;
    + unsigned long reserved[3];
    +};
    +
    +/*
    + * Pointer to the vDSO data page.
    + * 0 = not yet initialized
    + * -1 = vDSO not available (fallback to syscall)
    + * other = valid vDSO data page address
    + */
    +static volatile struct m68k_vdso_data *__m68k_vdso_data;

    This should be done at process initialization, similar how the other vDSO pointer are done at sysdeps/unix/sysv/linux/dl-vdso-setup.h. Most likely
    it would require a arch-specific hook where to add the logic.

    This would allow to optimize the call to avoid the branch to check if the
    vDSO is provided (with the fallback being the syscall), similar to how iFUNC are internally implemented; and by putting the pointer on the RELRO it would not require to add any hardening (as with PTR_MANGLE).

    +
    +/*
    + * Initialize the vDSO pointer on first use.
    + * Returns the pointer to the vDSO data, or NULL if not available.
    + */
    +static inline struct m68k_vdso_data *
    +__m68k_vdso_get (void)
    +{
    + struct m68k_vdso_data *vdso = (struct m68k_vdso_data *) __m68k_vdso_data; +
    + if (__glibc_unlikely (vdso == NULL))
    + {
    + /* Not yet initialized - try to get vDSO base from auxv */
    + unsigned long sysinfo_ehdr = __getauxval (AT_SYSINFO_EHDR);
    + if (sysinfo_ehdr != 0)
    + {
    + /*
    + * The kernel passes the address of the vDSO ELF header (code page). + * The vDSO memory layout is:
    + * base + 0x0000: vvar (data page with TLS pointer, clock state)
    + * base + 0x1000: timer page (hardware timer registers)
    + * base + 0x2000: vDSO code (ELF, AT_SYSINFO_EHDR points here)
    + * So the data page is 2 * PAGE_SIZE before the code page.
    + */
    + vdso = (struct m68k_vdso_data *) (sysinfo_ehdr - 2 * 4096);

    This is extremely fragile, meaning that if kernel vvar, timer page, or
    the vDSO code increase its size it would break. On other architecture
    this is transparent and vDSO can be extended any required vvar or code
    pages (and it was done for some architecture which extended code required
    pages to add new functionalities like the vgetrandom).

    This address needs to be provided by a vDSO symbol, complete transparent
    to the userland.

    Also, 4096 is wrong here because m68k/coldfire have 8k page sizes.


    + }
    + else
    + {
    + /* vDSO not available - mark as unavailable */
    + vdso = (struct m68k_vdso_data *) (uintptr_t) -1;
    + }
    + __m68k_vdso_data = vdso;
    + }
    +
    + /* Return NULL if vDSO is marked as unavailable */
    + if ((uintptr_t) vdso == (uintptr_t) -1)
    + return NULL;
    +
    + return vdso;
    +}
    +
    +/*
    + * Get the thread pointer.
    + *
    + * Fast path: Read directly from the vDSO data page if available.
    + * The kernel updates this on every context switch and set_thread_area() call.
    + *
    + * Slow path: Fall back to the get_thread_area syscall.
    + */
    void *
    __m68k_read_tp (void)
    {
    - return (void*) INTERNAL_SYSCALL_CALL (get_thread_area);
    + struct m68k_vdso_data *vdso = __m68k_vdso_get ();
    +
    + /* Fast path: use vDSO if available */
    + if (__glibc_likely (vdso != NULL))
    + return (void *) vdso->tls_ptr;
    +
    + /* Slow path: syscall fallback */
    + return (void *) INTERNAL_SYSCALL_CALL (get_thread_area);
    }

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Stefan Reinauer@3:633/10 to All on Friday, January 09, 2026 09:00:01
    Thank you for the detailed review, Adhemerval!

    On Thu, Jan 8, 2026 at 6:10?AM Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> wrote:



    On 08/01/26 03:18, Stefan wrote:
    Optimize __m68k_read_tp() to read the thread pointer directly from
    the vDSO data page when available, avoiding the get_thread_area
    syscall overhead.

    The kernel maintains the thread pointer in a data page located at
    a fixed offset (PAGE_SIZE) before the vDSO ELF header. This value
    is updated on every context switch and set_thread_area() call.

    The implementation uses lazy initialization: on first call, it
    checks AT_SYSINFO_EHDR from auxv to locate the vDSO. If the vDSO
    is not available (older kernels), it falls back to the syscall.


    When was the m68k vDSO support added on Linus tree? I removed some
    ancient code that was added based on a out-of-tree patches (commit 112a0ae18b831bf31f44d81b82666980312511d6) and afaik m68k vDSO support
    was never added on Linux.

    It hasn't been - these glibc patches depend on kernel patches that are currently under an initial review on linux-m68k. I should
    have made that dependency clear and included links to the kernel
    patches. The kernel patch series is at: https://marc.info/?l=linux-m68k&m=176785594629016&w=2

    diff --git a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c b/sysdeps/unix
    /sysv/linux/m68k/m68k-helpers.c
    index 845aae51..1dee48e5 100644
    --- a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    +++ b/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    @@ -17,9 +17,84 @@
    <https://www.gnu.org/licenses/>. */

    #include <sysdep.h>
    +#include <stdint.h>
    +#include <sys/auxv.h>

    +/*
    + * vDSO data structure - must match the kernel's m68k_vdso_data struct
    .
    + * The TLS pointer is at offset 0 in the vDSO data page.
    + */
    +struct m68k_vdso_data
    +{
    + unsigned long tls_ptr;
    + unsigned long reserved[3];
    +};
    +
    +/*
    + * Pointer to the vDSO data page.
    + * 0 = not yet initialized
    + * -1 = vDSO not available (fallback to syscall)
    + * other = valid vDSO data page address
    + */
    +static volatile struct m68k_vdso_data *__m68k_vdso_data;

    This should be done at process initialization, similar how the other vDSO pointer are done at sysdeps/unix/sysv/linux/dl-vdso-setup.h. Most likely
    it would require a arch-specific hook where to add the logic.

    This would allow to optimize the call to avoid the branch to check if the vDSO is provided (with the fallback being the syscall), similar to how iF
    UNC
    are internally implemented; and by putting the pointer on the RELRO it wo
    uld
    not require to add any hardening (as with PTR_MANGLE).

    You're right. I can rework this to:
    1. Initialize at process startup via dl-vdso-setup.h hooks
    2. Use an iFUNC-like pattern to avoid the branch on every call
    3. Store the pointer in RELRO to avoid PTR_MANGLE overhead

    +
    +/*
    + * Initialize the vDSO pointer on first use.
    + * Returns the pointer to the vDSO data, or NULL if not available.
    + */
    +static inline struct m68k_vdso_data *
    +__m68k_vdso_get (void)
    +{
    + struct m68k_vdso_data *vdso = (struct m68k_vdso_data *) __m68k_vds
    o_data;
    +
    + if (__glibc_unlikely (vdso == NULL))
    + {
    + /* Not yet initialized - try to get vDSO base from auxv */
    + unsigned long sysinfo_ehdr = __getauxval (AT_SYSINFO_EHDR);
    + if (sysinfo_ehdr != 0)
    + {
    + /*
    + * The kernel passes the address of the vDSO ELF header (code p
    age).
    + * The vDSO memory layout is:
    + * base + 0x0000: vvar (data page with TLS pointer, clock sta
    te)
    + * base + 0x1000: timer page (hardware timer registers)
    + * base + 0x2000: vDSO code (ELF, AT_SYSINFO_EHDR points here
    )
    + * So the data page is 2 * PAGE_SIZE before the code page.
    + */
    + vdso = (struct m68k_vdso_data *) (sysinfo_ehdr - 2 * 4096);

    This is extremely fragile, meaning that if kernel vvar, timer page, or
    the vDSO code increase its size it would break. On other architecture
    this is transparent and vDSO can be extended any required vvar or code
    pages (and it was done for some architecture which extended code required pages to add new functionalities like the vgetrandom).

    This is the same concern Florian raised. I'll rework the kernel side
    to export a __vdso_get_thread_area symbol that glibc can call, rather
    than having glibc calculate offsets into the vDSO memory layout.


    This address needs to be provided by a vDSO symbol, complete transparent
    to the userland.

    Also, 4096 is wrong here because m68k/coldfire have 8k page sizes.

    I have fixed this in a later revision in my internal tree to use __getauxval(AT_PAGESZ) for dynamic page size detection. I'll make sure
    the updated patch is sent once I address the other feedback.

    I'll revise both the kernel and glibc patches to follow the standard
    vDSO patterns before resubmitting.


    + }
    + else
    + {
    + /* vDSO not available - mark as unavailable */
    + vdso = (struct m68k_vdso_data *) (uintptr_t) -1;
    + }
    + __m68k_vdso_data = vdso;
    + }
    +
    + /* Return NULL if vDSO is marked as unavailable */
    + if ((uintptr_t) vdso == (uintptr_t) -1)
    + return NULL;
    +
    + return vdso;
    +}
    +
    +/*
    + * Get the thread pointer.
    + *
    + * Fast path: Read directly from the vDSO data page if available.
    + * The kernel updates this on every context switch and set_thread_area
    () call.
    + *
    + * Slow path: Fall back to the get_thread_area syscall.
    + */
    void *
    __m68k_read_tp (void)
    {
    - return (void*) INTERNAL_SYSCALL_CALL (get_thread_area);
    + struct m68k_vdso_data *vdso = __m68k_vdso_get ();
    +
    + /* Fast path: use vDSO if available */
    + if (__glibc_likely (vdso != NULL))
    + return (void *) vdso->tls_ptr;
    +
    + /* Slow path: syscall fallback */
    + return (void *) INTERNAL_SYSCALL_CALL (get_thread_area);
    }


    Stefan

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Stefan Reinauer@3:633/10 to All on Friday, January 09, 2026 09:00:01
    You raise a good point. The current implementation has glibc directly
    reading from the vvar data page, which tightly couples glibc to the
    kernel's internal vDSO memory layout.

    The more standard approach would be to export a __vdso_get_thread_area
    function from the vDSO that glibc calls via the normal vDSO symbol
    lookup mechanism. This would:

    1. Follow the established vDSO pattern used by clock_gettime,
    gettimeofday, etc.
    2. Allow the kernel to change the internal vvar layout without breaking g libc
    3. Keep the ABI contract at the function level rather than data
    structure level

    The tradeoff is a small amount of function call overhead, but that's
    still much faster than the syscall we're replacing.

    I can rework this to export __vdso_get_thread_area from the vDSO and
    have glibc use standard vDSO symbol lookup. Would that be the
    preferred approach?

    Stefan

    On Thu, Jan 8, 2026 at 5:33?AM Florian Weimer <fweimer@redhat.com>
    wrote:

    * Stefan:

    +/*
    + * Initialize the vDSO pointer on first use.
    + * Returns the pointer to the vDSO data, or NULL if not available.
    + */
    +static inline struct m68k_vdso_data *
    +__m68k_vdso_get (void)
    +{
    + struct m68k_vdso_data *vdso = (struct m68k_vdso_data *) __m68k_vds
    o_data;
    +
    + if (__glibc_unlikely (vdso == NULL))
    + {
    + /* Not yet initialized - try to get vDSO base from auxv */
    + unsigned long sysinfo_ehdr = __getauxval (AT_SYSINFO_EHDR);
    + if (sysinfo_ehdr != 0)
    + {
    + /*
    + * The kernel passes the address of the vDSO ELF header (code p
    age).
    + * The vDSO memory layout is:
    + * base + 0x0000: vvar (data page with TLS pointer, clock sta
    te)
    + * base + 0x1000: timer page (hardware timer registers)
    + * base + 0x2000: vDSO code (ELF, AT_SYSINFO_EHDR points here
    )
    + * So the data page is 2 * PAGE_SIZE before the code page.
    + */
    + vdso = (struct m68k_vdso_data *) (sysinfo_ehdr - 2 * 4096);
    + }
    + else
    + {
    + /* vDSO not available - mark as unavailable */
    + vdso = (struct m68k_vdso_data *) (uintptr_t) -1;
    + }
    + __m68k_vdso_data = vdso;
    + }

    Is this really the documented userspace interface? I would expect that
    we'd have to do a vDSO call to read the thread pointer.

    Thanks,
    Florian


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Florian Weimer@3:633/10 to All on Friday, January 09, 2026 09:30:01
    * Stefan Reinauer:

    You raise a good point. The current implementation has glibc directly
    reading from the vvar data page, which tightly couples glibc to the
    kernel's internal vDSO memory layout.

    The more standard approach would be to export a __vdso_get_thread_area function from the vDSO that glibc calls via the normal vDSO symbol
    lookup mechanism. This would:

    1. Follow the established vDSO pattern used by clock_gettime,
    gettimeofday, etc.
    2. Allow the kernel to change the internal vvar layout without breaking glibc
    3. Keep the ABI contract at the function level rather than data
    structure level

    The tradeoff is a small amount of function call overhead, but that's
    still much faster than the syscall we're replacing.

    I can rework this to export __vdso_get_thread_area from the vDSO and
    have glibc use standard vDSO symbol lookup. Would that be the
    preferred approach?

    Yes, please do it this way.

    Thanks,
    Florian

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Adhemerval Zanella Netto@3:633/10 to All on Friday, January 09, 2026 14:20:01


    On 08/01/26 19:34, Stefan Reinauer wrote:
    Thank you for the detailed review, Adhemerval!

    On Thu, Jan 8, 2026 at 6:10?AM Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> wrote:



    On 08/01/26 03:18, Stefan wrote:
    Optimize __m68k_read_tp() to read the thread pointer directly from
    the vDSO data page when available, avoiding the get_thread_area
    syscall overhead.

    The kernel maintains the thread pointer in a data page located at
    a fixed offset (PAGE_SIZE) before the vDSO ELF header. This value
    is updated on every context switch and set_thread_area() call.

    The implementation uses lazy initialization: on first call, it
    checks AT_SYSINFO_EHDR from auxv to locate the vDSO. If the vDSO
    is not available (older kernels), it falls back to the syscall.


    When was the m68k vDSO support added on Linus tree? I removed some
    ancient code that was added based on a out-of-tree patches (commit
    112a0ae18b831bf31f44d81b82666980312511d6) and afaik m68k vDSO support
    was never added on Linux.

    It hasn't been - these glibc patches depend on kernel patches that are currently under an initial review on linux-m68k. I should
    have made that dependency clear and included links to the kernel
    patches. The kernel patch series is at: https://marc.info/?l=linux-m68k&m=176785594629016&w=2

    I had the impression this was the case, adding the kernel links along
    with a [RFC] would give us more hint.

    Usually when kernel patches are sent for review we assume they are
    already in Linus tree.

    But thanks to sent it prior is become kABI, it give us time to narrow
    down possible issues.


    diff --git a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c b/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    index 845aae51..1dee48e5 100644
    --- a/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    +++ b/sysdeps/unix/sysv/linux/m68k/m68k-helpers.c
    @@ -17,9 +17,84 @@
    <https://www.gnu.org/licenses/>. */

    #include <sysdep.h>
    +#include <stdint.h>
    +#include <sys/auxv.h>

    +/*
    + * vDSO data structure - must match the kernel's m68k_vdso_data struct. >>> + * The TLS pointer is at offset 0 in the vDSO data page.
    + */
    +struct m68k_vdso_data
    +{
    + unsigned long tls_ptr;
    + unsigned long reserved[3];
    +};
    +
    +/*
    + * Pointer to the vDSO data page.
    + * 0 = not yet initialized
    + * -1 = vDSO not available (fallback to syscall)
    + * other = valid vDSO data page address
    + */
    +static volatile struct m68k_vdso_data *__m68k_vdso_data;

    This should be done at process initialization, similar how the other vDSO
    pointer are done at sysdeps/unix/sysv/linux/dl-vdso-setup.h. Most likely
    it would require a arch-specific hook where to add the logic.

    This would allow to optimize the call to avoid the branch to check if the
    vDSO is provided (with the fallback being the syscall), similar to how iFUNC >> are internally implemented; and by putting the pointer on the RELRO it would >> not require to add any hardening (as with PTR_MANGLE).

    You're right. I can rework this to:
    1. Initialize at process startup via dl-vdso-setup.h hooks
    2. Use an iFUNC-like pattern to avoid the branch on every call
    3. Store the pointer in RELRO to avoid PTR_MANGLE overhead

    +
    +/*
    + * Initialize the vDSO pointer on first use.
    + * Returns the pointer to the vDSO data, or NULL if not available.
    + */
    +static inline struct m68k_vdso_data *
    +__m68k_vdso_get (void)
    +{
    + struct m68k_vdso_data *vdso = (struct m68k_vdso_data *) __m68k_vdso_data;
    +
    + if (__glibc_unlikely (vdso == NULL))
    + {
    + /* Not yet initialized - try to get vDSO base from auxv */
    + unsigned long sysinfo_ehdr = __getauxval (AT_SYSINFO_EHDR);
    + if (sysinfo_ehdr != 0)
    + {
    + /*
    + * The kernel passes the address of the vDSO ELF header (code page).
    + * The vDSO memory layout is:
    + * base + 0x0000: vvar (data page with TLS pointer, clock state) >>> + * base + 0x1000: timer page (hardware timer registers)
    + * base + 0x2000: vDSO code (ELF, AT_SYSINFO_EHDR points here) >>> + * So the data page is 2 * PAGE_SIZE before the code page.
    + */
    + vdso = (struct m68k_vdso_data *) (sysinfo_ehdr - 2 * 4096);

    This is extremely fragile, meaning that if kernel vvar, timer page, or
    the vDSO code increase its size it would break. On other architecture
    this is transparent and vDSO can be extended any required vvar or code
    pages (and it was done for some architecture which extended code required
    pages to add new functionalities like the vgetrandom).

    This is the same concern Florian raised. I'll rework the kernel side
    to export a __vdso_get_thread_area symbol that glibc can call, rather
    than having glibc calculate offsets into the vDSO memory layout.


    This address needs to be provided by a vDSO symbol, complete transparent
    to the userland.

    Also, 4096 is wrong here because m68k/coldfire have 8k page sizes.

    I have fixed this in a later revision in my internal tree to use __getauxval(AT_PAGESZ) for dynamic page size detection. I'll make sure
    the updated patch is sent once I address the other feedback.

    We have GLRO(dl_pagesize) that already has the AUXV value of the pagesize,
    you just need to check if its initialization is done prior the vDSO setup (assuming you intend to initialize at program startup).

    But I think if the vDSO exports a __vdso_get_thread_area, there is no need
    to take the page size in consideration.


    I'll revise both the kernel and glibc patches to follow the standard
    vDSO patterns before resubmitting.


    + }
    + else
    + {
    + /* vDSO not available - mark as unavailable */
    + vdso = (struct m68k_vdso_data *) (uintptr_t) -1;
    + }
    + __m68k_vdso_data = vdso;
    + }
    +
    + /* Return NULL if vDSO is marked as unavailable */
    + if ((uintptr_t) vdso == (uintptr_t) -1)
    + return NULL;
    +
    + return vdso;
    +}
    +
    +/*
    + * Get the thread pointer.
    + *
    + * Fast path: Read directly from the vDSO data page if available.
    + * The kernel updates this on every context switch and set_thread_area() call.
    + *
    + * Slow path: Fall back to the get_thread_area syscall.
    + */
    void *
    __m68k_read_tp (void)
    {
    - return (void*) INTERNAL_SYSCALL_CALL (get_thread_area);
    + struct m68k_vdso_data *vdso = __m68k_vdso_get ();
    +
    + /* Fast path: use vDSO if available */
    + if (__glibc_likely (vdso != NULL))
    + return (void *) vdso->tls_ptr;
    +
    + /* Slow path: syscall fallback */
    + return (void *) INTERNAL_SYSCALL_CALL (get_thread_area);
    }


    Stefan

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)