Hello community,
here is the log from the commit of package crash
checked in at Thu Mar 6 00:26:48 CET 2008.
--------
--- crash/crash.changes 2008-02-29 20:24:47.000000000 +0100
+++ crash/crash.changes 2008-03-05 11:21:55.000000000 +0100
@@ -1,0 +2,6 @@
+Wed Mar 5 10:10:01 CET 2008 - bwalle@suse.de
+
+- crash-compressed-kernel: implement support for compressed kernel
+ images (vmlinux.gz, not bzImage of course)
+
+-------------------------------------------------------------------
New:
----
crash-compressed-kernel
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ crash.spec ++++++
--- /var/tmp/diff_new_pack.X11736/_old 2008-03-06 00:23:32.000000000 +0100
+++ /var/tmp/diff_new_pack.X11736/_new 2008-03-06 00:23:32.000000000 +0100
@@ -20,7 +20,7 @@
Url: http://people.redhat.com/anderson/
Summary: Crash utility for live systems; netdump, diskdump, LKCD or mcore dumpfiles
Version: 4.0.6.1
-Release: 1
+Release: 3
%define crash_version %(echo %{version} | cut -d . -f 1-2)
%define crash_subversion %(echo %{version} | cut -d . -f 3-4)
%define whitepaper_version 2003
@@ -39,6 +39,7 @@
Patch3: crash-search-also-in-lib-crash
Patch4: crash-fixed-uninitialised
Patch5: crash-sles9-time.patch
+Patch6: crash-compressed-kernel
BuildRequires: bison flex ncurses-devel zlib-devel
%description
@@ -116,6 +117,7 @@
%patch3 -p1
%patch4 -p1
%patch5 -p1
+%patch6 -p1
## SIAL patches
# cd sial-scripts-%{scripts_version}
## Patch here
@@ -178,6 +180,9 @@
%endif
%changelog
+* Wed Mar 05 2008 bwalle@suse.de
+- crash-compressed-kernel: implement support for compressed kernel
+ images (vmlinux.gz, not bzImage of course)
* Fri Feb 29 2008 bwalle@suse.de
- update to crash 4.0-6.1
o Support for 2.6.25 x86_64 kernels
++++++ crash-compressed-kernel ++++++
From: Bernhard Walle
Subject: [PATCH] Implement support for compressed kernel images
This patch implements support for compressed kernel images. The patch uses zlib
(which is already required, so no additional dependency is added). It
uncompresses the kernel to a temporary file (in $TMPDIR or /tmp if $TMPDIR
is unset) and operates on that file. That was necessary because the kernel file
name is used in various places and it would be too heavy to modify BFD calls
to operate on data instead on operating directly on the file.
The patch matches sure that the debuginfo file is still found (because the
original location differs from the tempfile location). It is therefore
necessary to keep the original file name in pc->orig_namelist (new member).
TODO:
- Find all places where pc->namelist is printed to the user and change this to
pc->orig_namelist because the temporary file may confuse the user.
Signed-off-by: Bernhard Walle
---
defs.h | 5 +
main.c | 24 ++++---
symbols.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
3 files changed, 202 insertions(+), 34 deletions(-)
--- a/defs.h
+++ b/defs.h
@@ -93,7 +93,7 @@
#define HIST_BLKSIZE (4096)
#define STREQ(A, B) ((A != NULL) && (B != NULL) && (strcmp((char *)(A), (char *)(B)) == 0))
-#define STRNEQ(A, B) ((A != NULL) && (B != NULL) && \
+#define STRNEQ(A, B) (((A) != NULL) && ((B) != NULL) && \
(strncmp((char *)(A), (char *)(B), strlen((char *)(B))) == 0))
#define BZERO(S, N) (memset(S, NULLCHAR, N))
#define BCOPY(S, D, C) (memcpy(D, S, C))
@@ -300,6 +300,8 @@ struct program_context {
char *prompt; /* this program's prompt */
unsigned long long flags; /* flags from above */
char *namelist; /* linux namelist */
+ char *orig_namelist; /* original namelist when namelist is
+ uncompressed at runtime */
char *dumpfile; /* dumpfile or /dev/kmem */
char *live_memsrc; /* live memory driver */
char *system_map; /* get symbol values from System.map */
@@ -3286,6 +3288,7 @@ void dump_offset_table(char *, ulong);
int is_elf_file(char *);
int is_elf_file_in_dir(const char *file, const char *dir);
int is_kernel(char *);
+int is_kernel_uncompress(char *file, char *new_filename);
int file_elf_version(char *);
int is_system_map(char *);
int select_namelist(char *);
--- a/main.c
+++ b/main.c
@@ -305,6 +305,7 @@ main(int argc, char **argv)
* Take the kernel and dumpfile arguments in either order.
*/
while (argv[optind]) {
+ char uncompressed_file[PATH_MAX] = "";
if (is_remote_daemon(argv[optind])) {
if (pc->flags & DUMPFILE_TYPES) {
@@ -317,21 +318,28 @@ main(int argc, char **argv)
continue;
}
- if (!file_exists(argv[optind], NULL)) {
- error(INFO, "%s: %s\n", argv[optind], strerror(ENOENT));
- program_usage(SHORT_FORM);
- } else if (!is_readable(argv[optind]))
+ if (!file_exists(argv[optind], NULL)) {
+ error(INFO, "%s: %s\n", argv[optind], strerror(ENOENT));
program_usage(SHORT_FORM);
+ } else if (!is_readable(argv[optind]))
+ program_usage(SHORT_FORM);
+
+ if (is_kernel_uncompress(argv[optind], uncompressed_file)) {
+ char *file = strlen(uncompressed_file)
+ ? strdup(uncompressed_file)
+ : argv[optind];
- if (is_kernel(argv[optind])) {
if (pc->namelist || pc->server_namelist) {
- if (!select_namelist(argv[optind])) {
+ if (!select_namelist(file)) {
error(INFO,
"too many namelist arguments\n");
program_usage(SHORT_FORM);
}
- } else
- pc->namelist = argv[optind];
+ } else {
+ pc->namelist = file;
+ /* always set this, also if it's the same */
+ pc->orig_namelist = argv[optind];
+ }
} else if (!(pc->flags & KERNEL_DEBUG_QUERY)) {
--- a/symbols.c
+++ b/symbols.c
@@ -56,7 +56,7 @@ static void symname_hash_init(void);
static void symname_hash_install(struct syment *);
static struct syment *symname_hash_search(char *);
static void gnu_qsort(bfd *, void *, long, unsigned int, asymbol *, asymbol *);
-static int check_gnu_debuglink(bfd *);
+static int check_gnu_debuglink(bfd *, int no_bfd_reset);
static int separate_debug_file_exists(const char *, unsigned long, int *);
static int store_module_kallsyms_v1(struct load_module *, int, int, char *);
static int store_module_kallsyms_v2(struct load_module *, int, int, char *);
@@ -156,6 +156,14 @@ symtab_init(void)
if (file_elf_version(pc->namelist) == EV_DWARFEXTRACT)
pc->flags |= KERNTYPES;
+ /*
+ * on gzip compressed kernel images, we need to set
+ * namelist_debug so that GDB is able to find debuginfo
+ */
+ if (!(LKCD_KERNTYPES()) && !STRNEQ(pc->namelist, pc->orig_namelist))
+ if (!check_gnu_debuglink(st->bfd, TRUE))
+ no_debugging_data(FATAL);
+
if (pc->flags & SYSMAP) {
bfd_map_over_sections(st->bfd, section_header_info,
VERIFY_SECTIONS);
@@ -179,7 +187,7 @@ symtab_init(void)
*/
if (!(LKCD_KERNTYPES()) &&
!(bfd_get_file_flags(st->bfd) & HAS_SYMS)) {
- if (!check_gnu_debuglink(st->bfd))
+ if (!check_gnu_debuglink(st->bfd, FALSE))
no_debugging_data(FATAL);
}
@@ -230,7 +238,7 @@ symtab_init(void)
* /usr/lib/debug/boot (since we know it's a Red Hat kernel)
*/
static int
-check_gnu_debuglink(bfd *bfd)
+check_gnu_debuglink(bfd *bfd, int no_bfd_reset)
{
int i, exists, found;
asection *sect;
@@ -266,15 +274,15 @@ check_gnu_debuglink(bfd *bfd)
contents, crc32);
if ((pc->debuginfo_file = (char *)
- malloc(((strlen(pc->namelist) + strlen("/.debug/") +
+ malloc(((strlen(pc->orig_namelist) + strlen("/.debug/") +
+ strlen(".debug") + strlen(" /usr/lib/debug/boot/ "))*10)
+ strlen(pc->namelist_debug ? pc->namelist_debug : " "))) == NULL)
error(FATAL, "debuginfo file name malloc: %s\n",
strerror(errno));
- filename = basename(pc->namelist);
- dirname = GETBUF(strlen(pc->namelist)+1);
- strcpy(dirname, pc->namelist);
+ filename = basename(pc->orig_namelist);
+ dirname = GETBUF(strlen(pc->orig_namelist)+1);
+ strcpy(dirname, pc->orig_namelist);
for (i = strlen(dirname)-1; i >= 0; i--)
{
@@ -379,13 +387,15 @@ check_gnu_debuglink(bfd *bfd)
reset_bfd:
- if ((st->bfd = bfd_openr(pc->debuginfo_file, NULL)) == NULL)
- error(FATAL, "cannot open object file: %s\n",
- pc->debuginfo_file);
-
- if (!bfd_check_format_matches(st->bfd, bfd_object, &matching))
- error(FATAL, "cannot determine object file format: %s\n",
- pc->debuginfo_file);
+ if (!no_bfd_reset) {
+ if ((st->bfd = bfd_openr(pc->debuginfo_file, NULL)) == NULL)
+ error(FATAL, "cannot open object file: %s\n",
+ pc->debuginfo_file);
+
+ if (!bfd_check_format_matches(st->bfd, bfd_object, &matching))
+ error(FATAL, "cannot determine object file format: %s\n",
+ pc->debuginfo_file);
+ }
FREEBUF(contents);
FREEBUF(dirname);
@@ -451,17 +461,17 @@ no_debugging_data(int error_type)
switch (error_type)
{
case INFO:
- error(INFO, "%s: no debugging data available\n", pc->namelist);
+ error(INFO, "%s: no debugging data available\n", pc->orig_namelist);
break;
case FATAL:
error(FATAL, "%s%s: no debugging data available\n",
- pc->flags & RUNTIME ? "" : "\n", pc->namelist);
+ pc->flags & RUNTIME ? "" : "\n", pc->orig_namelist);
clean_exit(1);
case WARNING:
error(FATAL, "\n%s: no debugging data available\n",
- pc->namelist);
+ pc->orig_namelist);
clean_exit(1);
}
}
@@ -2497,27 +2507,148 @@ is_elf_file(char *s)
}
/*
+ * Checks if the file is gzip-compressed. Just uses the magic checksum for
+ * this, doesn't check the contents.
+ */
+int is_gzip_file(const char *file)
+{
+ int fd = -1;
+ unsigned char buffer[2];
+
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ error(INFO, "%s: %s\n", file, strerror(errno));
+ return FALSE;
+ }
+
+ /* read the first 2 bytes */
+ if (read(fd, buffer, 2) != 2) {
+ error(INFO, "%s: %s\n", file, strerror(errno));
+ close(fd);
+ return FALSE;
+ }
+
+ close(fd);
+
+ return buffer[0] == 0x1f && buffer[1] == 0x8b;
+}
+
+/*
+ * Uncompresses the given file. Saves the name of the new file in new_file.
+ * Size of new_file must be large enough for PATH_MAX characters.
+ *
+ * Returns TRUE on success and FALSE on error.
+ */
+int uncompress_file(const char *file, char *new_file)
+{
+ gzFile source = NULL;
+ int target;
+ char buffer[BUFSIZE];
+ int bytes_read;
+ int retval = FALSE;
+ const char *tmpfile_suffix = "/vmlinux-crash-XXXXXX";
+
+ if (!new_file) {
+ error(WARNING, "uncompress_file: new_file must not be NULL.");
+ goto out;
+ }
+
+ source = gzopen(file, "r");
+ if (source == Z_NULL) {
+ error(WARNING, "Cannot open %s: %s\n", file, strerror(errno));
+ goto out;
+ }
+
+ /*
+ * construct the path where the temporary file resides. Follow the procedure
+ * which the mktemp shell utility uses:
+ * 1. If the user's TMPDIR environment variable is set, the directory
+ * contained therein is used.
+ * 2. If none of the above apply, /tmp is used.
+ */
+ if (getenv("TMPDIR")) {
+ size_t maxlen = PATH_MAX - strlen(tmpfile_suffix);
+ if (strlen(getenv("TMPDIR")) > maxlen) {
+ error(WARNING, "TMPDIR is too long.");
+ goto out;
+ }
+ strcpy(new_file, getenv("TMPDIR"));
+ } else
+ strcpy(new_file, "/tmp");
+
+ /* add the file name */
+ strcat(new_file, tmpfile_suffix);
+
+ target = mkstemp(new_file);
+ if (target < 0) {
+ error(WARNING, "Error in creating target file: %s\n", target);
+ goto out;
+ }
+
+ do {
+ bytes_read = gzread(source, buffer, BUFSIZE);
+ if (write(target, buffer, bytes_read) != bytes_read) {
+ error(WARNING, "Error in creating target file: %s\n", target);
+ goto out;
+ }
+ } while (bytes_read > 0);
+
+ retval = TRUE;
+
+out:
+ if (source)
+ gzclose(source);
+ if (target > 0)
+ close(target);
+
+ return retval;
+}
+
+/*
+ * Wrapper to delete a file in an on_exit() function.
+ * The file gets freed in this function, so you usually register
+ * that function with 'on_exit(remove_on_exit, strdup(filename))'
+ */
+static void remove_on_exit(int arg1, void *arg2)
+{
+ const char *file = (const char *)arg2;
+
+ remove(file);
+ free(arg2);
+}
+
+/*
* Verify a vmlinux file, issuing a warning for processor and endianness
* mismatches.
*/
int
-is_kernel(char *file)
+is_kernel_uncompress(char *file, char *new_file)
{
- int fd, swap;
+ gzFile fp;
+ int swap, err;
char eheader[BUFSIZE];
Elf32_Ehdr *elf32;
Elf64_Ehdr *elf64;
+ int is_gzipped;
- if ((fd = open(file, O_RDONLY)) < 0) {
+ /*
+ * if the file is gzipped and we cannot extract it, then
+ * we must say this is no kernel even if it would be one after
+ * unextracting ... (compatibility to is_kernel())
+ */
+ is_gzipped = is_gzip_file(file);
+ if (!new_file && is_gzipped)
+ return FALSE;
+
+ if ((fp = gzopen(file, "r")) == Z_NULL) {
error(INFO, "%s: %s\n", file, strerror(errno));
return FALSE;
}
- if (read(fd, eheader, BUFSIZE) != BUFSIZE) {
- /* error(INFO, "%s: %s\n", file, strerror(errno)); */
- close(fd);
+ if ((err = gzread(fp, eheader, BUFSIZE)) != BUFSIZE) {
+ error(INFO, "%s: %s\n", file, zError(err));
+ gzclose(fp);
return FALSE;
}
- close(fd);
+ gzclose(fp);
if (!STRNEQ(eheader, ELFMAG) || eheader[EI_VERSION] != EV_CURRENT)
return FALSE;
@@ -2583,9 +2714,35 @@ is_kernel(char *file)
}
bailout:
- return(is_bfd_format(file));
+ /* we must unextract it before presenting BFD */
+ if (is_gzipped) {
+ if (!uncompress_file(file, new_file)) {
+ error(INFO, "Uncompression failed for %s\n", file);
+ return FALSE;
+ }
+
+ /* if it's BFD, then make sure that it is deleted afterwards */
+ if (is_bfd_format(new_file)) {
+ on_exit(remove_on_exit, strdup(new_file));
+ return TRUE;
+ /* if not, delete it immediately */
+ } else {
+ remove(new_file);
+ return FALSE;
+ }
+ } else
+ return(is_bfd_format(file));
}
+/*
+ * Verify a vmlinux file, issuing a warning for processor and endianness
+ * mismatches.
+ */
+int
+is_kernel(char *file)
+{
+ return is_kernel_uncompress(file, NULL);
+}
/*
* Checks if file <file> in dir <dir> is a valid ELF file.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Remember to have fun...
---------------------------------------------------------------------
To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org
For additional commands, e-mail: opensuse-commit+help@opensuse.org