Repository: pwndbg/pwndbg
Pull request: Dump the TLS address by default
I am creating a new PR as I messed up the previous one: #2829 

This dumps by default the TLS address with `dt tcbhead_t <tls_address>` if there is a `tcbhead_t`. If there is not, use telescope to dump it.
It will print by default until the `__glibc_unused2` on x86-64. If there isn't one, it will print just 10 lines.
There is an argument `-a` to print the full output, which is told to the user when it runs and the output is truncated.

It also changes the TLS tests to adapt to the new changes. I plan on adding more specific tests related to x86-64 and i386 in the near future.

Closes #2814 

Problem statement:
Command request: dump fs at offset (x86-64)
```
pwndbg> p/x $fs:[0x30]
A syntax error in expression, near `:[0x30]'.
```

We can have a command that would:
1) save current state
2) assemble a `mov rax, qword ptr fs:[IDX]` instruction at $RIP
3) execute it
4) revert state
5) print the FS:[0x30] value

maybe: `dumpfs`? or `dump-fs-reg`? We need a good name for it (cc: @patryk4815 @OBarronCS @CptGibbon )

Hints from issue discussion:

Commit message: Update pwndbg/commands/tls.py

Patch to review:
diff --git a/pwndbg/commands/tls.py b/pwndbg/commands/tls.py
index 0eb37880cf7..f11ff746a3a 100644
--- a/pwndbg/commands/tls.py
+++ b/pwndbg/commands/tls.py
@@ -15,6 +15,7 @@
 import pwndbg.color.memory as M
 import pwndbg.commands
 import pwndbg.commands.context
+import pwndbg.commands.telescope
 import pwndbg.dbg
 from pwndbg.color import message
 from pwndbg.commands import CommandCategory
@@ -32,11 +33,13 @@
     help="Try to get the address of TLS by calling pthread_self().",
 )
 
+parser.add_argument("-a", "--all", action="store_true", help="Do not truncate the dump output.")
+
 
 @pwndbg.commands.ArgparsedCommand(parser, category=CommandCategory.LINUX)
 @pwndbg.commands.OnlyWhenRunning
 @pwndbg.commands.OnlyWhenUserspace
-def tls(pthread_self=False) -> None:
+def tls(pthread_self=False, all: bool = False) -> None:
     tls_base = (
         pwndbg.aglib.tls.find_address_with_register()
         if not pthread_self
@@ -46,6 +49,49 @@ def tls(pthread_self=False) -> None:
         print(message.success("Thread Local Storage (TLS) base: %#x" % tls_base))
         print(message.success("TLS is located at:"))
         print(message.notice(pwndbg.aglib.vmmap.find(tls_base)))
+
+        # Trying to `dt tcbhead_t <tls_base>` it by default
+        # If there is no tcbhead_t type, we can just use telescope.
+        typename = "tcbhead_t"
+        address = tls_base
+        # dt doesn't seem to return anything in case it succeeds.
+        output = str(pwndbg.aglib.dt.dt("tcbhead_t", addr=tls_base))
+
+        print(message.success("Dumping the address:"))
+        if output == "Type not found.":
+            pwndbg.commands.telescope.telescope(tls_base, 10)
+        else:
+            lines = output.splitlines()
+
+            if all or len(lines) <= 10:
+                print(message.notice(output))
+            else:
+                index = None
+                for i, line in enumerate(lines):
+                    if "__glibc_unused2" in line:
+                        index = i
+                        break
+
+                if index is not None:
+                    end_index = index + 2
+                    for line in lines[:end_index]:
+                        print(message.notice(line))
+                    print(message.notice("\t[...]"))
+                    print(
+                        message.hint(
+                            "Output truncated. Rerun with option -a to display the full output."
+                        )
+                    )
+                # In case there is a tcbhead_t but there is no __glibc_unused2
+                else:
+                    for line in lines[:10]:
+                        print(message.notice(line))
+                    print(message.notice("\t[...]"))
+                    print(
+                        message.hint(
+                            "Output truncated. Rerun with option -a to display the full output."
+                        )
+                    )
         return
     print(message.error("Couldn't find Thread Local Storage (TLS) base."))
     if not pthread_self:
diff --git a/tests/gdb-tests/tests/test_command_tls.py b/tests/gdb-tests/tests/test_command_tls.py
index 3b3ddcee6ab..960fe59e7a2 100644
--- a/tests/gdb-tests/tests/test_command_tls.py
+++ b/tests/gdb-tests/tests/test_command_tls.py
@@ -27,16 +27,27 @@ def test_tls_address_and_command(start_binary, binary):
 
     assert pwndbg.aglib.tls.find_address_with_pthread_self() == expected_tls_address
 
+    output = gdb.execute("tls", to_string=True)
+
+    assert f"Thread Local Storage (TLS) base: {expected_tls_address:#x}" in output
+    assert "TLS is located at:\n" and f"{pwndbg.aglib.vmmap.find(expected_tls_address)}\n" in output
+    assert "Output truncated. Rerun with option -a to display the full output." in output
+
+    output_pthread = gdb.execute("tls --pthread-self", to_string=True)
+
+    assert f"Thread Local Storage (TLS) base: {expected_tls_address:#x}" in output_pthread
     assert (
-        gdb.execute("tls", to_string=True)
-        == f"""Thread Local Storage (TLS) base: {expected_tls_address:#x}
-TLS is located at:
-{pwndbg.aglib.vmmap.find(expected_tls_address)}\n"""
+        "TLS is located at:"
+        and f"{pwndbg.aglib.vmmap.find(expected_tls_address)}\n" in output_pthread
     )
+    assert "Output truncated. Rerun with option -a to display the full output." in output_pthread
+
+    # Argument `-a`
+    output_all = gdb.execute("tls --all", to_string=True)
 
+    assert f"Thread Local Storage (TLS) base: {expected_tls_address:#x}" in output_all
     assert (
-        gdb.execute("tls --pthread-self", to_string=True)
-        == f"""Thread Local Storage (TLS) base: {expected_tls_address:#x}
-TLS is located at:
-{pwndbg.aglib.vmmap.find(expected_tls_address)}\n"""
+        "TLS is located at:\n"
+        and f"{pwndbg.aglib.vmmap.find(expected_tls_address)}\n" in output_all
     )
+    assert "Output truncated. Rerun with option -a to display the full output." not in output_all


Code review comments:
diff_hunk: @@ -46,6 +49,49 @@ def tls(pthread_self=False) -> None:
         print(message.success("Thread Local Storage (TLS) base: %#x" % tls_base))
         print(message.success("TLS is located at:"))
         print(message.notice(pwndbg.aglib.vmmap.find(tls_base)))
+
+        # Trying to `dt tcbhead_t <tls_base>` it by default
+        # If there is no tcbhead_t type, we can just use telescope.
+        typename = "tcbhead_t"
+        address = tls_base
path: pwndbg/commands/tls.py
text: @user1:
```suggestion
```