Tests with non-Cygwin executables show that other toolchains (LLVM LLD,
MS link without /RELEASE option) leave the PE checksum field as zero.
Peflags+rebase 4.6.6 still work correctly then, but rebase prints a
misleading warning 'Checksum update failed' in this case. With the
attached patch, a zero checksum is left as is and no message is printed.
This currently does not affect typical Cygwin use cases, so there is IMO
no urgent need for a new package.
BTW, there was possibly no announcement for the rebase 4.6.6 package.
--
Regards,
Christian
From 855be8d72a1f96d0a3cfcb0a97fbe12a9a912748 Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.fra...@t-online.de>
Date: Mon, 9 Oct 2023 09:56:05 +0200
Subject: [PATCH] Fix handling of unset PE checksums
Leave unset (zero) checksums as is unless checksum update is
explicitly requested. Remove misleading peflags warning
'Checksum update failed'.
Signed-off-by: Christian Franke <christian.fra...@t-online.de>
---
imagehelper/objectfile.cc | 9 ++++++--
peflags.c | 45 ++++++++++++++++++++++-----------------
rebase.c | 7 +++---
3 files changed, 36 insertions(+), 25 deletions(-)
diff --git a/imagehelper/objectfile.cc b/imagehelper/objectfile.cc
index 651fcce..ce4b444 100755
--- a/imagehelper/objectfile.cc
+++ b/imagehelper/objectfile.cc
@@ -497,13 +497,18 @@ BOOL LinkedObjectFile::updateCheckSum()
p_checksum = &getNTHeader64 ()->OptionalHeader.CheckSum;
else
p_checksum = &getNTHeader32 ()->OptionalHeader.CheckSum;
+ uint32_t checksum = *p_checksum;
+
+ // Leave unset checksum as is
+ if (!checksum)
+ return TRUE;
// Add changes from NT header to checksum updated by relocs
uint16_t checksum16;
if (relocs)
checksum16 = relocs->getCheckSum16();
else
- checksum16 = pe32_checksum_update_begin(*p_checksum, FileSize);
+ checksum16 = pe32_checksum_update_begin(checksum, FileSize);
if (is64bit ())
checksum16 = pe32_checksum_update_add(checksum16, &old_ntheader64,
@@ -514,7 +519,7 @@ BOOL LinkedObjectFile::updateCheckSum()
getNTHeader32 (),
sizeof(old_ntheader32));
- uint32_t checksum = pe32_checksum_update_end(checksum16, FileSize);
+ checksum = pe32_checksum_update_end(checksum16, FileSize);
if (!checksum)
return FALSE;
diff --git a/peflags.c b/peflags.c
index 917088b..14ea78d 100644
--- a/peflags.c
+++ b/peflags.c
@@ -508,7 +508,7 @@ do_mark (const char *pathname)
}
- /* Update the checksum. */
+ /* Update the checksum if set. */
changed = FALSE;
{
const void *p_oldheader, *p_newheader;
@@ -529,20 +529,24 @@ do_mark (const char *pathname)
p_checksum = &pep->ntheader32->OptionalHeader.CheckSum;
}
- hdr_checksum = pe32_checksum_update_end(
- pe32_checksum_update_add(
- pe32_checksum_update_begin(*p_checksum, pep->filesize),
- p_oldheader, p_newheader, headersize
- ),
- pep->filesize
- );
-
- if (!hdr_checksum)
- fprintf(stderr, "%s: Checksum update failed\n", pathname);
- else if (*p_checksum != hdr_checksum)
+ hdr_checksum = *p_checksum;
+ if (hdr_checksum)
{
- changed = TRUE;
- *p_checksum = hdr_checksum;
+ ULONG new_hdr_checksum = pe32_checksum_update_end (
+ pe32_checksum_update_add (
+ pe32_checksum_update_begin (hdr_checksum, pep->filesize),
+ p_oldheader, p_newheader, headersize
+ ),
+ pep->filesize
+ );
+
+ /* Update the checksum or set it to 0 if the old value is invalid. */
+ if (new_hdr_checksum != hdr_checksum
+ && (mark_any || handle_any_sizeof == DO_WRITE))
+ {
+ changed = TRUE;
+ *p_checksum = hdr_checksum = new_hdr_checksum;
+ }
}
}
@@ -650,7 +654,7 @@ static void
do_checksum (const char *pathname, int indent, int changed, ULONG hdr_checksum)
{
const char name[] = "PE file checksum";
- if (checksum_option > 1)
+ if (checksum_option > 1 && (checksum_option > 2 || hdr_checksum))
{
int fd;
unsigned new_checksum, old_checksum;
@@ -684,10 +688,11 @@ do_checksum (const char *pathname, int indent, int
changed, ULONG hdr_checksum)
printf ("%*s%-24s: 0x%08x (*needs update to 0x%08x*)\n", indent, "",
name,
old_checksum, new_checksum);
}
- else /* (checksum_option == 1) */
+ else /* (checksum_option == 1 || (checksum_option == 2 && !hdr_checksum)) */
{
- printf ("%*s%-24s: 0x%08x (%schanged, not verified)\n", indent, "", name,
- hdr_checksum, (changed ? "" : "un"));
+ printf ("%*s%-24s: 0x%08x (%schanged%s)\n", indent, "", name,
+ hdr_checksum, (changed ? "" : "un"),
+ (hdr_checksum ? ", not verified" : ""));
}
}
@@ -1265,8 +1270,8 @@ help (FILE *f)
" separately in another file.\n"
" -k, --checksum [BOOL] Displays (no argument), verifies (false) or\n"
" always fixes (true) the file checksum in the
PE\n"
-" header. By default, the checksum is updated\n"
-" without reading the whole file under the\n"
+" header. By default, a set (nonzero) checksum
is\n"
+" updated without reading the whole file under
the\n"
" assumption that the original checksum was\n"
" correct.\n"
" -x, --stack-reserve [NUM] Reserved stack size of the process in bytes.\n"
diff --git a/rebase.c b/rebase.c
index 6a531d0..3325c5e 100644
--- a/rebase.c
+++ b/rebase.c
@@ -1676,9 +1676,10 @@ Rebase PE files, usually DLLs, to a specified address or
address range.\n\
-c, --checksum Fix the file's checksum in the PE header if the
file\n\
has been successfully rebased. This also bumps
the\n\
file's modification time (like -t) if the checksum\n\
- has been changed. By default, the checksum is\n\
- updated without reading the whole file under the\n\
- assumption that the original checksum was correct.\n\
+ has been changed. By default, a set (nonzero)\n\
+ checksum is updated without reading the whole file\n\
+ under the assumption that the original checksum
was\n\
+ correct.\n\
-d, --down Treat the BaseAddress as upper ceiling and rebase\n\
files top-down from there. Without this option
the\n\
files are rebased from BaseAddress bottom-up.\n\
--
2.39.0