s390/decompressor: add symbols support
authorVasily Gorbik <gor@linux.ibm.com>
Wed, 11 Nov 2020 09:59:40 +0000 (10:59 +0100)
committerHeiko Carstens <hca@linux.ibm.com>
Fri, 20 Nov 2020 18:19:11 +0000 (19:19 +0100)
Information printed by print_pgm_check_info() is crucial for
debugging decompressor problems. Printing instruction addresses is
better than nothing, but turns further debugging into tedious job of
figuring out which function those addresses correspond to.

This change adds simplistic symbols resolution support. And adds %pS
format specifier support to decompressor_printk().

Decompressor symbols list is extracted and sorted with
nm -n -S:
...
0000000000010000 0000000000000014 T startup
0000000000010014 00000000000000b0 t startup_normal
0000000000010180 00000000000000b2 t startup_kdump
...

Then functions are filtered and contracted to a form:
"10000 14 startup\0""10014 b0 startup_normal\0""10180 b2 startup_kdump\0"
...
Which makes it trivial to find beginning of an entry and names are 0
terminated, so could be used as is. Symbols are binary-searched.

To get symbols list with final addresses and then get it into the
decompressor's image the same trick as for kallsyms is used.
Decompressor's vmlinux is linked twice.

Symbols are stored in .decompressor.syms section, current size is about
2kb.

Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
arch/s390/boot/compressed/.gitignore
arch/s390/boot/compressed/Makefile
arch/s390/boot/compressed/vmlinux.lds.S
arch/s390/boot/pgm_check_info.c

index 765a08f1bd778960cd8ba08e59149f1a19e9cb93..01d93832cf4acc9f81cf68618f4ce4205b51831a 100644 (file)
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 vmlinux
 vmlinux.lds
+vmlinux.syms
index b235ed95a3d895b5fa5cc173351ab378d2ab7aeb..e0502222706a1e0f7422d8aeb64837ad413e40f5 100644 (file)
@@ -10,21 +10,39 @@ GCOV_PROFILE := n
 UBSAN_SANITIZE := n
 KASAN_SANITIZE := n
 
-obj-y  := $(if $(CONFIG_KERNEL_UNCOMPRESSED),,decompressor.o) piggy.o info.o
+obj-y  := $(if $(CONFIG_KERNEL_UNCOMPRESSED),,decompressor.o) info.o
+obj-all := $(obj-y) piggy.o syms.o
 targets        := vmlinux.lds vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2
 targets += vmlinux.bin.xz vmlinux.bin.lzma vmlinux.bin.lzo vmlinux.bin.lz4
-targets += info.bin $(obj-y)
+targets += info.bin syms.bin vmlinux.syms $(obj-all)
 
 KBUILD_AFLAGS := $(KBUILD_AFLAGS_DECOMPRESSOR)
 KBUILD_CFLAGS := $(KBUILD_CFLAGS_DECOMPRESSOR)
 OBJCOPYFLAGS :=
 
 OBJECTS := $(addprefix $(obj)/,$(obj-y))
+OBJECTS_ALL := $(addprefix $(obj)/,$(obj-all))
 
 LDFLAGS_vmlinux := --oformat $(LD_BFD) -e startup -T
-$(obj)/vmlinux: $(obj)/vmlinux.lds $(objtree)/arch/s390/boot/startup.a $(OBJECTS) FORCE
+$(obj)/vmlinux: $(obj)/vmlinux.lds $(objtree)/arch/s390/boot/startup.a $(OBJECTS_ALL) FORCE
        $(call if_changed,ld)
 
+LDFLAGS_vmlinux.syms := --oformat $(LD_BFD) -e startup -T
+$(obj)/vmlinux.syms: $(obj)/vmlinux.lds $(objtree)/arch/s390/boot/startup.a $(OBJECTS) FORCE
+       $(call if_changed,ld)
+
+quiet_cmd_dumpsyms = DUMPSYMS $<
+define cmd_dumpsyms
+       $(NM) -n -S --format=bsd "$<" | $(PERL) -ne '/(\w+)\s+(\w+)\s+[tT]\s+(\w+)/ and printf "%x %x %s\0",hex $$1,hex $$2,$$3' > "$@"
+endef
+
+$(obj)/syms.bin: $(obj)/vmlinux.syms FORCE
+       $(call if_changed,dumpsyms)
+
+OBJCOPYFLAGS_syms.o := -I binary -O elf64-s390 -B s390:64-bit --rename-section .data=.decompressor.syms
+$(obj)/syms.o: $(obj)/syms.bin FORCE
+       $(call if_changed,objcopy)
+
 OBJCOPYFLAGS_info.bin := -O binary --only-section=.vmlinux.info --set-section-flags .vmlinux.info=load
 $(obj)/info.bin: vmlinux FORCE
        $(call if_changed,objcopy)
index 9427e2cd0c1542bf4adaab8edcbb789f8f84f26f..152ac6f875c0a4b26bf9caa6cc02a9dd9dda0838 100644 (file)
@@ -82,6 +82,14 @@ SECTIONS
                *(.vmlinux.info)
        }
 
+       .decompressor.syms : {
+               . += 1; /* make sure we have \0 before the first entry */
+               . = ALIGN(2);
+               _decompressor_syms_start = .;
+               *(.decompressor.syms)
+               _decompressor_syms_end = .;
+       }
+
 #ifdef CONFIG_KERNEL_UNCOMPRESSED
        . = 0x100000;
 #else
index 829548c2244459fe9a5b2b089e764014a8bb455c..8ba8136adb4d66d5521e8eba3446aa06af4625fb 100644 (file)
@@ -19,6 +19,65 @@ static char *as_hex(char *dst, unsigned long val, int pad)
        return end;
 }
 
+static char *symstart(char *p)
+{
+       while (*p)
+               p--;
+       return p + 1;
+}
+
+extern char _decompressor_syms_start[], _decompressor_syms_end[];
+static noinline char *findsym(unsigned long ip, unsigned short *off, unsigned short *len)
+{
+       /* symbol entries are in a form "10000 c4 startup\0" */
+       char *a = _decompressor_syms_start;
+       char *b = _decompressor_syms_end;
+       unsigned long start;
+       unsigned long size;
+       char *pivot;
+       char *endp;
+
+       while (a < b) {
+               pivot = symstart(a + (b - a) / 2);
+               start = simple_strtoull(pivot, &endp, 16);
+               size = simple_strtoull(endp + 1, &endp, 16);
+               if (ip < start) {
+                       b = pivot;
+                       continue;
+               }
+               if (ip > start + size) {
+                       a = pivot + strlen(pivot) + 1;
+                       continue;
+               }
+               *off = ip - start;
+               *len = size;
+               return endp + 1;
+       }
+       return NULL;
+}
+
+static noinline char *strsym(void *ip)
+{
+       static char buf[64];
+       unsigned short off;
+       unsigned short len;
+       char *p;
+
+       p = findsym((unsigned long)ip, &off, &len);
+       if (p) {
+               strncpy(buf, p, sizeof(buf));
+               /* reserve 15 bytes for offset/len in symbol+0x1234/0x1234 */
+               p = buf + strnlen(buf, sizeof(buf) - 15);
+               strcpy(p, "+0x");
+               p = as_hex(p + 3, off, 0);
+               strcpy(p, "/0x");
+               as_hex(p + 3, len, 0);
+       } else {
+               as_hex(buf, (unsigned long)ip, 16);
+       }
+       return buf;
+}
+
 void decompressor_printk(const char *fmt, ...)
 {
        char buf[1024] = { 0 };
@@ -38,6 +97,11 @@ void decompressor_printk(const char *fmt, ...)
                case 's':
                        p = buf + strlcat(buf, va_arg(args, char *), sizeof(buf));
                        break;
+               case 'p':
+                       if (*++fmt != 'S')
+                               goto out;
+                       p = buf + strlcat(buf, strsym(va_arg(args, void *)), sizeof(buf));
+                       break;
                case 'l':
                        if (*++fmt != 'x' || end - p <= max(sizeof(long) * 2, pad))
                                goto out;
@@ -67,9 +131,10 @@ void print_pgm_check_info(void)
                            S390_lowcore.pgm_code, S390_lowcore.pgm_ilc >> 1);
        if (kaslr_enabled)
                decompressor_printk("Kernel random base: %lx\n", __kaslr_offset);
-       decompressor_printk("PSW : %016lx %016lx\n",
+       decompressor_printk("PSW : %016lx %016lx (%pS)\n",
                            S390_lowcore.psw_save_area.mask,
-                           S390_lowcore.psw_save_area.addr);
+                           S390_lowcore.psw_save_area.addr,
+                           (void *)S390_lowcore.psw_save_area.addr);
        decompressor_printk(
                "      R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x P:%x AS:%x CC:%x PM:%x RI:%x EA:%x\n",
                psw->per, psw->dat, psw->io, psw->ext, psw->key, psw->mcheck,