Bug 908454

Summary: get_kernel_version does not support zImage
Product: [openSUSE] openSUSE Distribution Reporter: Josua Mayer <josua.m>
Component: BasesystemAssignee: E-mail List <bnc-team-screening>
Status: RESOLVED FIXED QA Contact: E-mail List <qa-bugs>
Severity: Minor    
Priority: P5 - None CC: afaerber, agraf, awafaa, bugz57, bwiedemann, chcao, josua.m, lnussel, novell, ro, schwab, trenn, werner, whdu
Version: 13.2   
Target Milestone: ---   
Hardware: armv7   
OS: Other   
Whiteboard:
Found By: --- Services Priority:
Business Priority: Blocker: ---
Marketing QA Status: --- IT Deployment: ---
Attachments: The list of magics found by file 5.22

Description Josua Mayer 2014-12-04 22:45:22 UTC
/usr/bin/get_kernel_version program can not extract the version from zImages.
This prevents mkinitrd from detecting a kernel and creating a ramdisk when run without arguments, on ARM.
Comment 1 Bernhard Wiedemann 2014-12-05 08:29:56 UTC
works on x86_64
# get_kernel_version /boot/vmlinuz
3.16.6-2-desktop

is get_kernel_version architecture-specific?
Comment 2 Josua Mayer 2014-12-05 19:57:50 UTC
I would not say so. I think it is related to vmlinuz != zImage, or presence of a vmlinux.gz

running get_kernel_version /boot/zImage-3.14.14-cubox-i on my arm board prints nothing.
Running it instead on /boot/vmlinux-3.14.14-cubox-i.gz works as expected
(I dont have a vmlinuz installed for my kernel

If I remember right, adding that .gz was required to trick mkinitrd on 13.1, but that's not required anymore in 13.2. Its useless and I'll drop it completely as soon as I can.

So I installed kernel-default package and performed same tests:
get_kernel_version zImage-3.16.7-2-default : no output
get_kernel_version vmlinux-3.16.7-2-default.gz : works fine
There is no vmlinuz ......
Comment 3 Andreas Schwab 2014-12-08 09:38:47 UTC
get_kernel_version is architecture-specific.  It only supports uncompressed or gzip compressed kernels or x86 format kernels.
Comment 4 Weihua Du 2014-12-22 02:35:36 UTC
> get_kernel_version is architecture-specific.  It only supports uncompressed
> or gzip compressed kernels or x86 format kernels.

Are you okay to close it?
Comment 5 Ludwig Nussel 2015-04-28 12:14:02 UTC
*** Bug 928673 has been marked as a duplicate of this bug. ***
Comment 6 Ludwig Nussel 2015-04-28 12:15:54 UTC
we should drop get_kernel_version from aaa_base. mkinitrd can use e.g. file to determine the kernel version or parse the file name which has a standardized form for package installed kernels.
Comment 7 Ludwig Nussel 2015-04-28 12:16:32 UTC
reassigning to dracut maintainer for comment
Comment 8 Andreas Schwab 2015-05-05 07:11:05 UTC
On aarch64:

$ file -L /boot/Image
/boot/Image: MS-DOS executable, MZ for MS-DOS
$ get_kernel_version /boot/Image
4.0.0-1-default
Comment 9 Thomas Renninger 2015-05-07 14:11:39 UTC
Please wait until dracut version 041 finally hits factory.
Hopefully after fixing this one:
[Bug #927719] Tumbleweed Snapshot blocked: no multipath support in 20150416 (likely dracut issue)

I have some more patches to queue up for dracut Base:System, but I need to have the update accepted first.
Comment 10 Thomas Renninger 2015-05-08 09:00:42 UTC
Hoopsie, I mixed this up with install_kernel from dracut package.
I wonder why I got assigned to this (aaa_base) bug.
Ruediger is maintaining aaa_base.
Werner is maintaining file.

Summary:
We have following steps, bugs to fix (in this order):
1) file has to support all architectures (including aarch64, others?)
2) All users of get_kernel_version have to be replaced to use "file" instead,
   for example install_kernel in dracut
3) We want to get rid of get_kernel_version in aaa_base.
   Hm, or get_kernel_version in aaa_base uses file and formats the output
   in the same way it did before?

-> I like to re-assign this one. Question is to whom?

Re-assigning to Werner for now (kernel version support for aarch64 zImage?), adding Ruediger for comments.
Comment 11 Dr. Werner Fink 2015-05-08 09:38:51 UTC
Hmm ... without the attached Image and without the version of the file package on ARM I can say exactly nothing.  Btw: Do we support zImage format at all?

In file-5.22 we have

  # Linux ARM compressed kernel image
  # From: Kevin Cernekee <cernekee@gmail.com>
  36      lelong  0x016f2818      Linux kernel ARM boot executable zImage (little-endian)
  36      belong  0x016f2818      Linux kernel ARM boot executable zImage (big-endian)
Comment 12 Ruediger Oertel 2015-05-08 09:55:30 UTC
for reference: the original author of get_kernel_version is steffen.

get_kernel_version is perfectly simple in operation:
- if file starts with (037,0213,0236) decompress a chunk
- in the first 4k search for "Linux version " and get what's behind

can't be too hard to extend this for zImage, but I'd need to see such a file
Comment 13 Thomas Renninger 2015-05-08 12:01:50 UTC
Andreas: I thought this is about /sbin/installkernel from dracut.
There is:
case "$(uname -m)" in
    s390|s390x)
        BOOTFILE=image
        ;;
    ppc|ppc64)
        BOOTFILE=vmlinux
        ;;
    *)
        BOOTFILE=vmlinuz
        ;;
esac

If you need something specific for aarch64, please let me know or best send a tiny patch which shouldn't be hard to do, but needs ARM HW at hand...
Comment 14 Volker Kuhlmann 2015-05-09 21:44:10 UTC
> Hmm ... without the attached Image and without the version of the file package
> on ARM I can say exactly nothing.

The ARM zImage file belongs to the kernel-default package which you can
download from the 13.2 ARM repo (it's 6.5MB wasted space in bugzilla).
http://download.opensuse.org/ports/armv7hl/distribution/13.2/repo/oss/suse/armv7hl/kernel-default-3.16.7-11.1.armv7hl.rpm

That also has the file package with the version in its name, which is
file-5.19-3.8.1.armv7hl

> Btw: Do we support zImage format at all?

Can't say.

I like the idea of using file, because that already has all the magic
so why replicate it.
Comment 15 Volker Kuhlmann 2015-05-09 21:52:04 UTC
The bottom line is that mkinitrd doesn't work with no arguments (and a useless error msg):

# mkinitrd 
No kernel found in /boot or bad modules dir in /lib/modules

Currently this is because get_kernel_version fails on ARM zImage kernels. However I don't care about get_kernel_version, I care about mkinitrd, so changing mkinitrd to establish kernel version is a nother option.
Comment 16 Volker Kuhlmann 2015-05-09 21:55:21 UTC
Using file may not be so simple:

# file /boot/zImage-3.19.3-1-default
/boot/zImage-3.19.3-1-default: Linux kernel ARM boot executable zImage (little-endian)
# file -v
file-5.19

Perhaps that was the reason for the existence of get_kernel_version.
Any reason not to use just the file name (or link name, don't de-reference), as Ludwig suggested?
Comment 17 Ruediger Oertel 2015-05-10 09:31:25 UTC
file only tells the type, not the version we are after.
for reference:
http://morgue.openinkpot.org/wiki/Documentation/ZImageFormat

in short:
objdump -EL -b binary -D -m armv5t zImage-3.16.7-11-default | grep 8b1f | head -2
    4878:	00088b1f 	andeq	r8, r8, pc, lsl fp
    5220:	8b1ffd06 	blhi	0x804640

4878 hex = 18552 dec

dd if=zImage-3.16.7-11-default of=piggy.gz bs=1 skip=18552

# get_kernel_version piggy.gz 
3.16.7-11-default

no idea yet how to get a simple implementation for this without all the ELF magic
Comment 18 Dr. Werner Fink 2015-05-11 06:35:19 UTC
(In reply to Volker Kuhlmann from comment #16)

> # file /boot/zImage-3.19.3-1-default
> /boot/zImage-3.19.3-1-default: Linux kernel ARM boot executable zImage (little-endian)
> # file -v
> file-5.19

How does this fit with this?

(In reply to Andreas Schwab from comment #8)

> On aarch64:
> 
> $ file -L /boot/Image
> /boot/Image: MS-DOS executable, MZ for MS-DOS
> $ get_kernel_version /boot/Image
> 4.0.0-1-default

I'd like to know *which* version of file do you have used for your result?
Also, where can I find this Image
Comment 19 Andreas Schwab 2015-05-11 07:09:10 UTC
> I'd like to know *which* version of file do you have used for your result?

It's all latest Factory.

> Also, where can I find this Image

On seattle.arch.

> parse the file name

The file name can easily get lost or be inaccurate.
Comment 20 Dr. Werner Fink 2015-05-11 10:31:34 UTC
(In reply to Andreas Schwab from comment #19)

It seems that the kernel developers on aarch64 are too lazy or not
interested on how to detect their kernel images

 rpm2cpio kernel-default-4.0.1-1.1.ge3a374a.armv7hl.rpm | \
  cpio -i --to-stdout ./boot/zImage-4.0.1-1.ge3a374a-default | file -
 /dev/stdin: Linux kernel ARM boot executable zImage (little-endian)

 rpm2cpio kernel-default-4.0.1-1.1.ge3a374a.armv6hl.rpm | \
   cpio -i --to-stdout ./boot/zImage-4.0.1-1.ge3a374a-default | file -
 /dev/stdin: Linux kernel ARM boot executable zImage (little-endian)

 pm2cpio kernel-default-3.19.4-1.1.g74c332b.aarch64.rpm | \
   cpio -i --to-stdout ./boot/Image-3.19.4-1.g74c332b-default | file -
 /dev/stdin: MS-DOS executable, MZ for MS-DOS
 rpm2cpio kernel-default-3.19.4-1.1.g74c332b.aarch64.rpm | \
 cpio -i --to-stdout ./boot/Image-3.19.4-1.g74c332b-default | head -c 2
 MZ

... it is well known that MZ is the start point of most MS-DOS executables

From /usr/share/misc/magic

 # Linux ARM compressed kernel image
 # From: Kevin Cernekee <cernekee@gmail.com>
 36      lelong  0x016f2818      Linux kernel ARM boot executable zImage (little-endian)
 36      belong  0x016f2818      Linux kernel ARM boot executable zImage (big-endian)

 rpm2cpio kernel-default-4.0.1-1.1.ge3a374a.armv6hl.rpm | \
   cpio -i --to-stdout ./boot/zImage-4.0.1-1.ge3a374a-default | \
   head -c 40 | od -t x1
 0000000 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1 00 00 a0 e1
 *
 0000040 03 00 00 ea 18 28 6f 01

 rpm2cpio kernel-default-3.19.4-1.1.g74c332b.aarch64.rpm | \
 cpio -i --to-stdout ./boot/Image-3.19.4-1.g74c332b-default | head -c 40 | \
 od -t x1

 0000000 4d 5a 00 91 ff 03 00 14 00 00 08 00 00 00 00 00
 0000020 00 00 9e 00 00 00 00 00 00 00 00 00 00 00 00 00
 0000040 00 00 00 00 00 00 00 00
 0000050

 Hex 0x4d 0x5a *is* MZ
Comment 21 Andreas Schwab 2015-05-11 10:49:14 UTC
It's a standard EFI image.
Comment 22 Dr. Werner Fink 2015-05-11 10:55:11 UTC
(In reply to Andreas Schwab from comment #21)

In other words: it is a MS-DOS exe ... hey isn't that ``cool''.
Comment 23 Alexander Graf 2015-05-11 11:45:11 UTC
On AArch64 the EFI header gets generated without meta information that can tell us what the contents are.

On x86 the PE header includes metadata that allows tools like 'file' to identify it as a Linux kernel binary which we depend on to identify kernel binaries.

Andrew, do you think we could have the same on AArch64?
Comment 24 Andrew Wafaa 2015-05-11 13:16:48 UTC
(In reply to Alexander Graf from comment #23)
> On AArch64 the EFI header gets generated without meta information that can
> tell us what the contents are.
> 
> On x86 the PE header includes metadata that allows tools like 'file' to
> identify it as a Linux kernel binary which we depend on to identify kernel
> binaries.
> 
> Andrew, do you think we could have the same on AArch64?

It is possible to have this on AArch64. Can you provide details on what exactly you are looking for, is it a specific line or something else? Could you point to an example for x86?
Comment 25 Alexander Graf 2015-05-11 14:31:20 UTC
Looking a bit deeper, that particular header seems to be the x86 specific legacy boot loader:

        # Part 2 of the header, from the old setup.S
            
                .ascii  "HdrS"          # header signature
                .word   0x020d          # header version number (>= 0x0105)
                                        # or else old loadlin-1.5 will fail)
Comment 26 Alexander Graf 2015-05-11 14:33:14 UTC
Sorry, pressed enter too quickly.

This particular header seems to only exist on x86 systems. On PowerPC for example, the kernel only gets detected as ELF binary as well (OF boots ELF rather than PE natively).

So overall, I think the output of "file" is fully correct. If ARM people want to, they can also add an HdrS chunk to their header, but I don't think it's necessary.
Comment 27 Dr. Werner Fink 2015-05-11 14:40:58 UTC
(In reply to Alexander Graf from comment #26)

In other words, the program file is not able to detect the kernel on AArch64 nor on PowerPC64 as it is not designed that way.

Don't know how the small get_kernel_version(.c) program is able to detect the version of such a plain MZ-DOS or ELF kernel image binary without such a header, but it has the same problem as file has: no magics is no informations.
Comment 28 Andreas Schwab 2015-05-11 14:49:50 UTC
Yet get_kernel_version is able to do its job.
Comment 29 Dr. Werner Fink 2015-05-11 15:00:51 UTC
(In reply to Andreas Schwab from comment #28)

> Yet get_kernel_version is able to do its job.

Hmm ... then I don't know why the this bug has the subject

     get_kernel_version does not support zImage

...?
Comment 30 Andreas Schwab 2015-05-11 15:14:32 UTC
Someone needs to fix that then.
Comment 31 Andreas Färber 2015-05-11 15:53:17 UTC
(In reply to Andreas Schwab from comment #30)
> Someone needs to fix that then.

Maybe you should've opened a new bug for your AArch64 issue in the first place.

Werner, one issue was zImage on 32-bit arm, the other issue was Image on 64-bit aarch64. Different users, different systems, similar problems, HTE.
Comment 32 Alexander Graf 2015-05-11 21:20:52 UTC
Ok, let's please keep separate issues separate

First off, "file" reports the file type for a specific binary. This is "PE" for AArch64, ELF for PowerPC, "special ARM zImage" for 32bit ARM and "magic x86 Linux kernel" for x86 systems. It behaves as designed and is fully correct.

The "get_kernel_version" tool basically does a simple grep through the binary to look for a string "Linux version". Whatever follows that string is deemed the kernel version. It's a hack, but it's a working hack as long as the image is uncompressed.

Now if you take a look at the script in /sbin/mkinitrd:

  # rpm -qf /sbin/mkinitrd
  dracut-037-32.1.aarch64

then you'll see that this script calls get_kernel_version on our zImage

        if [ "$kernel_image" != "$initrd_image" -a \
             -n "$kernel_version" -a \
             -d "/lib/modules/$kernel_version" ]; then
                kernel_images="$kernel_images $boot_dir/$kernel_image"
                initrd_images="$initrd_images $boot_dir/$initrd_image"
        fi

However, since the zImage is (as its name suggests), compressed, we have a really hard time finding the kernel version inside that file. Before dracut, we had a hack in mkinitrd that would fall back to the vmlinux.gz file instead so that we get_kernel_version can do its job:

  https://github.com/openSUSE/mkinitrd/commit/3e9cb61ee500fcc732e2299c99fcdf3e1813547f

This code must have gotten lost in transition to dracut. So let's ask Thomas :).
Comment 33 Andreas Schwab 2015-05-12 06:34:45 UTC
get_kernel_version can uncompress the file, it just needs to find the signature.
Comment 34 Dr. Werner Fink 2015-05-12 06:42:12 UTC
(In reply to Andreas Faerber from comment #31)

There is nothing to fix as if you have a look into the code of get_kernel_version  you will see a brut force method which search for the string

  "Linux version "

for both little and big endian case in the binary which is ... if required ... uncompressed with

  "/bin/gzip -dc %s 2>/dev/null"

... with the file command you can not do this, as file was not written to dump symbols from an elf binary nor symbols from a compressed elf binary.

I've done an

  strace get_kernel_version /boot/vmlinux | & grep ^read | wc -l

on PowerPC64 and this reports  351 reads with each of 32768 bytes to find
the final "Linux version " ... this has nothing todo with magic headers of magic marks for elf binaries. The PowerPC64 kernel probed with the file command shows

  ELF 64-bit MSB shared object, 64-bit PowerPC or cisco 7500, version 1 (SYSV), statically linked, BuildID[sha1]=0x88bcd91b88298c500c6127aae9a4756811267a47, stripped
Comment 35 Andreas Schwab 2015-05-12 06:50:16 UTC
> ... with the file command you can not do this

That's why we have get_kernel_version.
Comment 36 Dr. Werner Fink 2015-05-12 06:59:45 UTC
(In reply to Andreas Schwab from comment #33)

Correct, but a zImage is not in gzip format and the MZ-DOS header fools get_kernel_version
Comment 37 Andreas Schwab 2015-05-12 07:13:43 UTC
zImage contains a gzip image.
Comment 38 Dr. Werner Fink 2015-05-12 07:28:02 UTC
Created attachment 633904 [details]
The list of magics found by file 5.22

(In reply to Andreas Schwab from comment #37)

Even if the image is gziped, it seems to be without magic header:

  typeset -i n=0
  while ((n++ < 65536)); do
    tail -c +$n < Image | file -k - | grep -v '/dev/stdin: data'
  done  > list
  grep -i gzip list

shows nothing about gzip
Comment 39 Dr. Werner Fink 2015-05-12 08:03:45 UTC
I've done a deeper search and found at the lines

80885:  gzip compressed data, reserved method, ASCII, has comment, encrypted, last modified: Fri Aug 29 06:40:32 2014, from Unix
81597:  gzip compressed data, reserved method, ASCII, has comment, encrypted, last modified: Fri Aug 29 07:35:28 2014, from Unix

but both

  tail -c +80885 Image | file -bz -
  tail -c +81597 Image | file -bz -

do show

  ERROR: zlib: invalid stored block lengths
Comment 40 Andreas Schwab 2015-05-12 08:18:57 UTC
Worksforme.

$ tail -c +19065 boot/zImage-4.0.1-1-default | zcat > zImage

gzip: stdin: decompression OK, trailing garbage ignored
$ get_kernel_version zImage
4.0.1-1-default
Comment 41 Dr. Werner Fink 2015-05-12 08:35:37 UTC
(In reply to Andreas Schwab from comment #40)

> ...Worksforme.
> 
> $ tail -c +19065 boot/zImage-4.0.1-1-default | zcat > zImage
> 
> gzip: stdin: decompression OK, trailing garbage ignored
> $ get_kernel_version zImage
> 4.0.1-1-default

Where can I find the rpm of this aarch64 kernel?

osc ls -b Kernel:stable kernel-default ARM/aarch64
_buildenv
_statistics
kernel-default-3.19.4-1.1.g74c332b.aarch64.rpm
kernel-default-3.19.4-1.1.g74c332b.nosrc.rpm
kernel-default-base-3.19.4-1.1.g74c332b.aarch64.rpm
kernel-default-base-debuginfo-3.19.4-1.1.g74c332b.aarch64.rpm
kernel-default-debuginfo-3.19.4-1.1.g74c332b.aarch64.rpm
kernel-default-debugsource-3.19.4-1.1.g74c332b.aarch64.rpm
kernel-default-devel-3.19.4-1.1.g74c332b.aarch64.rpm
Comment 42 Andreas Schwab 2015-05-12 08:37:54 UTC
It's all Factory.
Comment 43 Volker Kuhlmann 2015-05-12 20:45:46 UTC
So a zImage is an ELF binary that also contains a gziped chunk of kernel image from which the kernel version should be easily extractable.
file sees the ELF binary and stops (not unreasonably so).
Why does get_kernel_version fail then on so many architectures on a zImage?
Basically, it should scan the ELF file for a gzip header, decompress, proceed as it does for uncompresses kernel images on the decompressed data. Right?
Comment 44 Thomas Renninger 2015-06-29 16:43:48 UTC
> It's all Factory.
Hm, I added a fix to dracut based on the mkinitrd solution Alex Graf pointed me to. Thanks!
While the bug is against 13.2, can it be closed if only factory may be relevant?

If really needed I can add it to the next 13.2 dracut maintenance update which may take a while.

Hm, I close the bug fixed already.