@ -12,7 +12,11 @@ import pwndbg.aglib.vmmap
def __call_pthread_self ( ) - > int :
""" Get the address of TLS by calling pthread_self(). """
"""
Retrieve the address of the ` struct pthread_t ` for the current thread by
calling pthread_self ( ) . This address can be used to locate the base address
of the Thread Local Storage ( TLS ) .
"""
if pwndbg . dbg . selected_inferior ( ) . symbol_address_from_name ( " pthread_self " ) is None :
return 0
try :
@ -26,10 +30,14 @@ def __call_pthread_self() -> int:
def find_address_with_pthread_self ( ) - > int :
""" Get the address of TLS with pthread_self(). """
if pwndbg . aglib . arch . current not in ( " x86-64 " , " i386 " , " arm " ) :
# Note: we should support aarch64 if it's possible that TPIDR_EL0 register can not be accessed.
"""
Get the base address of the Thread Local Storage ( TLS ) for the current thread using
the pthread_self ( ) function . The returned address points to the ` struct tcbhead_t ` ,
which serves as the header for TLS and thread - specific metadata .
"""
if pwndbg . aglib . arch . current not in ( " x86-64 " , " i386 " , " arm " , " aarch64 " ) :
return 0
result = __call_pthread_self ( )
if result < = 0 :
# pthread_self() is not valid
@ -43,20 +51,34 @@ def find_address_with_pthread_self() -> int:
# For i386 and x86-64, the return value of the pthread_self() is the address of TLS, because the value is self reference of the TLS: https://elixir.bootlin.com/glibc/glibc-2.37/source/nptl/pthread_create.c#L671
# But for arm, the implementation of THREAD_SELF is different, we need to add sizeof(struct pthread) to the result to get the address of TLS.
if pwndbg . aglib . arch . current == " arm " :
# 0x4c0 is sizeof(struct pthread)
# TODO: we might need to adjust the value if the size of struct pthread is changed in the future.
result + = 0x4C0
if pwndbg . aglib . arch . current in ( " arm " , " aarch64 " ) :
pthread_type = pwndbg . aglib . typeinfo . load ( " struct pthread " )
if pthread_type is None :
# Type 'pthread' not found
return 0
result + = pthread_type . sizeof
return result
def find_address_with_register ( ) - > int :
""" Get the address of TLS with register. """
"""
Get the base address of the Thread Local Storage ( TLS ) for the current thread using
a CPU register . The returned address points to the ` struct tcbhead_t ` , which is the
entry point for TLS and thread - specific metadata .
"""
if pwndbg . aglib . arch . current == " x86-64 " :
return int ( pwndbg . aglib . regs . fsbase )
elif pwndbg . aglib . arch . current == " i386 " :
return int ( pwndbg . aglib . regs . gsbase )
elif pwndbg . aglib . arch . current == " aarch64 " :
return int ( pwndbg . aglib . regs . TPIDR_EL0 )
# TODO: is it possible that we can get the address of TLS with register on arm?
# FIXME: cleanup/remove `TPIDR_EL0` register, it was renamed to `tpidr` since GDB13+
return int ( pwndbg . aglib . regs . tpidr or pwndbg . aglib . regs . TPIDR_EL0 or 0 )
elif pwndbg . aglib . arch . current == " arm " :
# TODO: linux ptrace for 64bit kernel?
# In FreeBSD tls is under `tpidruro` register.
# In Linux, the `tpidruro` register isn't available via ptrace in the 32-bit
# kernel but it is available for an aarch32 program running under an arm64
# kernel via the ptrace compat interface.
return int ( pwndbg . aglib . regs . tpidruro or 0 )
return 0