[PATCH] scripts/gdb/symbols: Determine KASLR offset on s390

Ilya Leoshkevich posted 1 patch 11 months, 1 week ago
scripts/gdb/linux/symbols.py | 31 ++++++++++++++++++++++++++++++-
scripts/gdb/linux/utils.py   | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 65 insertions(+), 1 deletion(-)
[PATCH] scripts/gdb/symbols: Determine KASLR offset on s390
Posted by Ilya Leoshkevich 11 months, 1 week ago
Use QEMU's qemu.PhyMemMode [1] functionality to read vmcore from the
physical memory the same way the existing dump tooling does this.
Gracefully handle non-QEMU targets, early boot, and memory corruptions;
print a warning if such situation is detected.

[1] https://qemu-project.gitlab.io/qemu/system/gdb.html#examining-physical-memory

Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
---
 scripts/gdb/linux/symbols.py | 31 ++++++++++++++++++++++++++++++-
 scripts/gdb/linux/utils.py   | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index f6c1b063775a..3126329c7f26 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -14,6 +14,7 @@
 import gdb
 import os
 import re
+import struct
 
 from linux import modules, utils, constants
 
@@ -53,6 +54,29 @@ if hasattr(gdb, 'Breakpoint'):
             return False
 
 
+def get_vmcore_s390():
+    with utils.qemu_phy_mem_mode():
+        vmcore_info = 0x0e0c
+        paddr_vmcoreinfo_note = gdb.parse_and_eval("*(unsigned long long *)" +
+                                                   hex(vmcore_info))
+        inferior = gdb.selected_inferior()
+        elf_note = inferior.read_memory(paddr_vmcoreinfo_note, 12)
+        n_namesz, n_descsz, n_type = struct.unpack(">III", elf_note)
+        desc_paddr = paddr_vmcoreinfo_note + len(elf_note) + n_namesz + 1
+        return gdb.parse_and_eval("(char *)" + hex(desc_paddr)).string()
+
+
+def get_kerneloffset():
+    if utils.is_target_arch('s390'):
+        try:
+            vmcore_str = get_vmcore_s390()
+        except gdb.error as e:
+            gdb.write("{}\n".format(e))
+            return None
+        return utils.parse_vmcore(vmcore_str).kerneloffset
+    return None
+
+
 class LxSymbols(gdb.Command):
     """(Re-)load symbols of Linux kernel and currently loaded modules.
 
@@ -155,7 +179,12 @@ lx-symbols command."""
                 obj.filename.endswith('vmlinux.debug')):
                 orig_vmlinux = obj.filename
         gdb.execute("symbol-file", to_string=True)
-        gdb.execute("symbol-file {0}".format(orig_vmlinux))
+        kerneloffset = get_kerneloffset()
+        if kerneloffset is None:
+            offset_arg = ""
+        else:
+            offset_arg = " -o " + hex(kerneloffset)
+        gdb.execute("symbol-file {0}{1}".format(orig_vmlinux, offset_arg))
 
         self.loaded_modules = []
         module_list = modules.module_list()
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index 245ab297ea84..03ebdccf5f69 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -11,6 +11,11 @@
 # This work is licensed under the terms of the GNU GPL version 2.
 #
 
+import contextlib
+import dataclasses
+import re
+import typing
+
 import gdb
 
 
@@ -216,3 +221,33 @@ def gdb_eval_or_none(expresssion):
         return gdb.parse_and_eval(expresssion)
     except gdb.error:
         return None
+
+
+@contextlib.contextmanager
+def qemu_phy_mem_mode():
+    connection = gdb.selected_inferior().connection
+    orig = connection.send_packet("qqemu.PhyMemMode")
+    if orig not in b"01":
+        raise gdb.error("Unexpected qemu.PhyMemMode")
+    orig = orig.decode()
+    if connection.send_packet("Qqemu.PhyMemMode:1") != b"OK":
+        raise gdb.error("Failed to set qemu.PhyMemMode")
+    try:
+        yield
+    finally:
+        if connection.send_packet("Qqemu.PhyMemMode:" + orig) != b"OK":
+            raise gdb.error("Failed to restore qemu.PhyMemMode")
+
+
+@dataclasses.dataclass
+class VmCore:
+    kerneloffset: typing.Optional[int]
+
+
+def parse_vmcore(s):
+    match = re.search(r"KERNELOFFSET=([0-9a-f]+)", s)
+    if match is None:
+        kerneloffset = None
+    else:
+        kerneloffset = int(match.group(1), 16)
+    return VmCore(kerneloffset=kerneloffset)
-- 
2.48.1
Re: [PATCH] scripts/gdb/symbols: Determine KASLR offset on s390
Posted by Alexander Gordeev 11 months, 1 week ago
On Mon, Mar 03, 2025 at 12:03:58PM +0100, Ilya Leoshkevich wrote:
> Use QEMU's qemu.PhyMemMode [1] functionality to read vmcore from the
> physical memory the same way the existing dump tooling does this.
> Gracefully handle non-QEMU targets, early boot, and memory corruptions;
> print a warning if such situation is detected.
> 
> [1] https://qemu-project.gitlab.io/qemu/system/gdb.html#examining-physical-memory
> 
> Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
> ---
>  scripts/gdb/linux/symbols.py | 31 ++++++++++++++++++++++++++++++-
>  scripts/gdb/linux/utils.py   | 35 +++++++++++++++++++++++++++++++++++
>  2 files changed, 65 insertions(+), 1 deletion(-)

Can not tell for the gdb part, but for the s390 bits:

Acked-by: Alexander Gordeev <agordeev@linux.ibm.com>
Re: [PATCH] scripts/gdb/symbols: Determine KASLR offset on s390
Posted by Jan Kiszka 11 months, 1 week ago
On 03.03.25 12:03, Ilya Leoshkevich wrote:
> Use QEMU's qemu.PhyMemMode [1] functionality to read vmcore from the
> physical memory the same way the existing dump tooling does this.
> Gracefully handle non-QEMU targets, early boot, and memory corruptions;
> print a warning if such situation is detected.
> 
> [1] https://qemu-project.gitlab.io/qemu/system/gdb.html#examining-physical-memory
> 
> Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
> ---
>  scripts/gdb/linux/symbols.py | 31 ++++++++++++++++++++++++++++++-
>  scripts/gdb/linux/utils.py   | 35 +++++++++++++++++++++++++++++++++++
>  2 files changed, 65 insertions(+), 1 deletion(-)
> 
> diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
> index f6c1b063775a..3126329c7f26 100644
> --- a/scripts/gdb/linux/symbols.py
> +++ b/scripts/gdb/linux/symbols.py
> @@ -14,6 +14,7 @@
>  import gdb
>  import os
>  import re
> +import struct
>  
>  from linux import modules, utils, constants
>  
> @@ -53,6 +54,29 @@ if hasattr(gdb, 'Breakpoint'):
>              return False
>  
>  
> +def get_vmcore_s390():
> +    with utils.qemu_phy_mem_mode():
> +        vmcore_info = 0x0e0c
> +        paddr_vmcoreinfo_note = gdb.parse_and_eval("*(unsigned long long *)" +
> +                                                   hex(vmcore_info))
> +        inferior = gdb.selected_inferior()
> +        elf_note = inferior.read_memory(paddr_vmcoreinfo_note, 12)
> +        n_namesz, n_descsz, n_type = struct.unpack(">III", elf_note)
> +        desc_paddr = paddr_vmcoreinfo_note + len(elf_note) + n_namesz + 1
> +        return gdb.parse_and_eval("(char *)" + hex(desc_paddr)).string()
> +
> +
> +def get_kerneloffset():
> +    if utils.is_target_arch('s390'):
> +        try:
> +            vmcore_str = get_vmcore_s390()
> +        except gdb.error as e:
> +            gdb.write("{}\n".format(e))
> +            return None
> +        return utils.parse_vmcore(vmcore_str).kerneloffset
> +    return None
> +
> +
>  class LxSymbols(gdb.Command):
>      """(Re-)load symbols of Linux kernel and currently loaded modules.
>  
> @@ -155,7 +179,12 @@ lx-symbols command."""
>                  obj.filename.endswith('vmlinux.debug')):
>                  orig_vmlinux = obj.filename
>          gdb.execute("symbol-file", to_string=True)
> -        gdb.execute("symbol-file {0}".format(orig_vmlinux))
> +        kerneloffset = get_kerneloffset()
> +        if kerneloffset is None:
> +            offset_arg = ""
> +        else:
> +            offset_arg = " -o " + hex(kerneloffset)
> +        gdb.execute("symbol-file {0}{1}".format(orig_vmlinux, offset_arg))
>  
>          self.loaded_modules = []
>          module_list = modules.module_list()
> diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
> index 245ab297ea84..03ebdccf5f69 100644
> --- a/scripts/gdb/linux/utils.py
> +++ b/scripts/gdb/linux/utils.py
> @@ -11,6 +11,11 @@
>  # This work is licensed under the terms of the GNU GPL version 2.
>  #
>  
> +import contextlib
> +import dataclasses
> +import re
> +import typing
> +
>  import gdb
>  
>  
> @@ -216,3 +221,33 @@ def gdb_eval_or_none(expresssion):
>          return gdb.parse_and_eval(expresssion)
>      except gdb.error:
>          return None
> +
> +
> +@contextlib.contextmanager
> +def qemu_phy_mem_mode():
> +    connection = gdb.selected_inferior().connection
> +    orig = connection.send_packet("qqemu.PhyMemMode")
> +    if orig not in b"01":
> +        raise gdb.error("Unexpected qemu.PhyMemMode")
> +    orig = orig.decode()
> +    if connection.send_packet("Qqemu.PhyMemMode:1") != b"OK":
> +        raise gdb.error("Failed to set qemu.PhyMemMode")
> +    try:
> +        yield
> +    finally:
> +        if connection.send_packet("Qqemu.PhyMemMode:" + orig) != b"OK":
> +            raise gdb.error("Failed to restore qemu.PhyMemMode")
> +
> +
> +@dataclasses.dataclass
> +class VmCore:
> +    kerneloffset: typing.Optional[int]
> +
> +
> +def parse_vmcore(s):
> +    match = re.search(r"KERNELOFFSET=([0-9a-f]+)", s)
> +    if match is None:
> +        kerneloffset = None
> +    else:
> +        kerneloffset = int(match.group(1), 16)
> +    return VmCore(kerneloffset=kerneloffset)

Nice trick with qemu. Can't comment on the s390-specifics in this, but
the rest looks fine to me. Just wish there was something similar for
other the archs.

Acked-by: Jan Kiszka <jan.kiszka@siemens.com>

Jan

-- 
Siemens AG, Foundational Technologies
Linux Expert Center