android/kernel/kernel_utils.c: Support probing version for ARM64 kernel images.
The ARM64 kernel images are not compressed but contain a GZip header that
appears _after_ the actual 'Linux version ' string. This patch modifies the
probing code to handle this gracefully.
+ Update unit tests.
Change-Id: Id486e9ba29c894bc6f7f863789f120c7c8ab22a0
diff --git a/android/kernel/kernel_utils.cpp b/android/kernel/kernel_utils.cpp
index cbc4f4e..2237357 100755
--- a/android/kernel/kernel_utils.cpp
+++ b/android/kernel/kernel_utils.cpp
@@ -25,7 +25,7 @@
#include <string.h>
#include <strings.h>
-#define DEBUG_KERNEL 0
+#define DEBUG_KERNEL 1
#define KERNEL_LOG LOG_IF(INFO, DEBUG_KERNEL)
#define KERNEL_PLOG PLOG_IF(INFO, DEBUG_KERNEL)
@@ -34,14 +34,13 @@
using android::base::PodVector;
-namespace android {
-namespace kernel {
+namespace {
-static const char kVersionStringPrefix[] = "Linux version ";
-static const size_t kVersionStringPrefixLen = sizeof(kVersionStringPrefix) - 1U;
+const char kLinuxVersionStringPrefix[] = "Linux version ";
+const size_t kLinuxVersionStringPrefixLen =
+ sizeof(kLinuxVersionStringPrefix) - 1U;
-} // namespace kernel
-} // namespace android
+} // namespace
#ifndef __APPLE__
size_t strlcpy(char* dst, const char * src, size_t size)
@@ -84,13 +83,13 @@
bool android_parseLinuxVersionString(const char* versionString,
KernelVersion* kernelVersion) {
- if (strncmp(versionString, android::kernel::kVersionStringPrefix,
- android::kernel::kVersionStringPrefixLen)) {
+ if (strncmp(versionString, kLinuxVersionStringPrefix,
+ kLinuxVersionStringPrefixLen)) {
KERNEL_ERROR << "unsupported kernel version string:" << versionString;
return false;
}
// skip past the prefix to the version number
- versionString += android::kernel::kVersionStringPrefixLen;
+ versionString += kLinuxVersionStringPrefixLen;
uint32_t temp = 0;
for (int i = 0; i < 3; i++) {
@@ -139,6 +138,8 @@
return false;
}
+ const char* versionStringStart = NULL;
+
if (0 == memcmp(kElfHeader, kernelFileData, sizeof(kElfHeader))) {
// this is an uncompressed ELF file (probably mips)
uncompressedKernel = kernelFileData;
@@ -156,37 +157,49 @@
return false;
}
- size_t compressedKernelLen = kernelFileSize -
- (compressedKernel - kernelFileData);
+ // Special case: certain images, like the ARM64 one, contain a GZip
+ // header _after_ the actual Linux version string. So first try to
+ // see if there is something before the header.
+ versionStringStart = (const char*)memmem(
+ kernelFileData,
+ (compressedKernel - kernelFileData),
+ kLinuxVersionStringPrefix,
+ kLinuxVersionStringPrefixLen);
- // inflate ratios for all prebuilt kernels on 2014-07-14 is 1.9:1 ~
- // 3.43:1 not sure how big the uncompressed size is, so make an
- // absurdly large buffer
- uncompressedKernelLen = compressedKernelLen * 10;
- uncompressed.resize(uncompressedKernelLen);
- uncompressedKernel = uncompressed.begin();
+ if (!versionStringStart) {
+ size_t compressedKernelLen = kernelFileSize -
+ (compressedKernel - kernelFileData);
- bool zOk = uncompress_gzipStream(uncompressed.begin(),
- &uncompressedKernelLen,
- compressedKernel,
- compressedKernelLen);
- if (!zOk) {
- KERNEL_ERROR << "Kernel decompression error";
- // it may have been partially decompressed, so we're going to
- // try to find the version string anyway
+ // inflate ratios for all prebuilt kernels on 2014-07-14 is 1.9:1 ~
+ // 3.43:1 not sure how big the uncompressed size is, so make an
+ // absurdly large buffer
+ uncompressedKernelLen = compressedKernelLen * 10;
+ uncompressed.resize(uncompressedKernelLen);
+ uncompressedKernel = uncompressed.begin();
+
+ bool zOk = uncompress_gzipStream(uncompressed.begin(),
+ &uncompressedKernelLen,
+ compressedKernel,
+ compressedKernelLen);
+ if (!zOk) {
+ KERNEL_ERROR << "Kernel decompression error";
+ // it may have been partially decompressed, so we're going to
+ // try to find the version string anyway
+ }
}
}
- // okay, now we have a pointer to an uncompressed kernel, let's find the
- // version string
- const char* versionStringStart = (const char*)memmem(
- uncompressedKernel,
- uncompressedKernelLen,
- android::kernel::kVersionStringPrefix,
- android::kernel::kVersionStringPrefixLen);
if (!versionStringStart) {
- KERNEL_ERROR << "Could not find 'Linux version ' in kernel!";
- return false;
+ versionStringStart = (const char*)memmem(
+ uncompressedKernel,
+ uncompressedKernelLen,
+ kLinuxVersionStringPrefix,
+ kLinuxVersionStringPrefixLen);
+
+ if (!versionStringStart) {
+ KERNEL_ERROR << "Could not find 'Linux version ' in kernel!";
+ return false;
+ }
}
strlcpy(dst, versionStringStart, dstLen);
diff --git a/android/kernel/kernel_utils_unittest.cpp b/android/kernel/kernel_utils_unittest.cpp
index 40ad06c..f24845a 100755
--- a/android/kernel/kernel_utils_unittest.cpp
+++ b/android/kernel/kernel_utils_unittest.cpp
@@ -85,6 +85,27 @@
0x00, 0x00, 0x00
};
+ // a mock semi-compressed kernel
+ // an unspecified number of bytes, followed by a version string,
+ // followed by a gzip header
+ static const unsigned char kMockKernelSemiCompressed[] = {
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x39, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x76,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x33, 0x2e, 0x31, 0x30, 0x2e,
+ 0x30, 0x2b, 0x20, 0x28, 0x76, 0x68, 0x61, 0x72, 0x72, 0x6f, 0x6e, 0x40,
+ 0x74, 0x69, 0x66, 0x61, 0x2e, 0x6d, 0x74, 0x76, 0x2e, 0x63, 0x6f, 0x72,
+ 0x70, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x29, 0x20, 0x28, 0x67, 0x63, 0x63, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6f, 0x6e, 0x20, 0x34, 0x2e, 0x37, 0x20, 0x28, 0x47, 0x43, 0x43, 0x29,
+ 0x20, 0x29, 0x20, 0x23, 0x31, 0x20, 0x50, 0x52, 0x45, 0x45, 0x4d, 0x50,
+ 0x54, 0x20, 0x53, 0x61, 0x74, 0x20, 0x4a, 0x61, 0x6e, 0x20, 0x35, 0x20,
+ 0x32, 0x3a, 0x34, 0x35, 0x3a, 0x32, 0x34, 0x20, 0x50, 0x44, 0x54, 0x20,
+ 0x32, 0x30, 0x30, 0x38, 0x0a, 0x00, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35,
+ 0x37, 0x38, 0x39, 0x1f, 0x8b, 0x08, 0x00, 0x3e, 0xa9, 0xc7, 0x53, 0x00,
+ 0x03, 0x0b, 0xf7, 0x70, 0x0c, 0x71, 0x0d, 0x73, 0x0d, 0x02, 0x00, 0xff,
+ 0xdf, 0x22, 0x88, 0x08, 0x00, 0x00, 0x00
+ };
+
char kernelVersionString[256];
kernelVersionString[0] = 0;
@@ -103,8 +124,16 @@
sizeof(kernelVersionString)));
EXPECT_STREQ(kMockKernelVersion, kernelVersionString);
+ kernelVersionString[0] = 0;
+ EXPECT_EQ(true, android_imageProbeKernelVersionString(
+ kMockKernelSemiCompressed,
+ sizeof(kMockKernelSemiCompressed),
+ kernelVersionString,
+ sizeof(kernelVersionString)));
+ EXPECT_STREQ(kMockKernelVersion, kernelVersionString);
+
kernelVersionString[0] = 127;
- EXPECT_EQ(false, android_imageProbeKernelVersionString(
+ EXPECT_FALSE(android_imageProbeKernelVersionString(
0,
0,
kernelVersionString,
@@ -112,7 +141,7 @@
EXPECT_EQ(127, kernelVersionString[0]);
kernelVersionString[0] = 127;
- EXPECT_EQ(false, android_imageProbeKernelVersionString(
+ EXPECT_FALSE(android_imageProbeKernelVersionString(
kMockKernelElfNoString,
sizeof(kMockKernelElfNoString),
kernelVersionString,
diff --git a/android/kernel/testing/print_mock_kernel_data.sh b/android/kernel/testing/print_mock_kernel_data.sh
index 1a1a5cb..c6ce113 100755
--- a/android/kernel/testing/print_mock_kernel_data.sh
+++ b/android/kernel/testing/print_mock_kernel_data.sh
@@ -26,6 +26,15 @@
cat $TMP_FILE | xxd -i
printf "};\n\n"
+printf "// a mock semi-compressed kernel\n"
+printf "// an unspecified number of bytes, followed by a version string,\n"
+printf "// followed by a gzip header\n"
+printf "static const unsigned char kMockKernelSemiCompressed[] = {\n"
+printf "01234567899abcdef${LINUX_VERSION}0123455789" > $TMP_FILE
+printf "WHATEVER" | gzip >> $TMP_FILE
+cat $TMP_FILE | xxd -i
+printf "};\n\n"
+
rm $TMP_FILE