diff --git a/common/common.h b/common/common.h index 37f93dc5..2bf277b7 100644 --- a/common/common.h +++ b/common/common.h @@ -905,7 +905,7 @@ void release_global_lock(); // u32 compute_crc32(u32 crc, u8 *buf, i64 len); -std::vector crc32_solve(i64 datalen, u32 current, u32 want); +std::vector crc32_solve(u32 current, u32 desired); // // compress.cc diff --git a/common/crc32.cc b/common/crc32.cc index fe37196c..d77cf5d7 100644 --- a/common/crc32.cc +++ b/common/crc32.cc @@ -1,42 +1,3 @@ -// This file contains a function to "forge" a CRC. That is, given a piece -// of data and a desired CRC32 value, crc32_solve() returns a binary blob -// to add to the end of the original data to yield the desired CRC32 -// value. A trailing garbage is ignored for many bianry file formats, so -// you can create a file with a desired CRC using crc32_solve(). We need -// it for --separate-debug-info. -// -// The code in this file is based on Mark Adler's "spoof" program. You can -// obtain the original copy of it at the following URL: -// -// https://github.com/madler/spoof/blob/master/spoof.c -// -// Below is the original license: - -/* spoof.c -- modify a message to have a desired CRC - - Copyright (C) 2012, 2014, 2016, 2018, 2021 Mark Adler - - This software is provided 'as-is', without any express or implied warranty. - In no event will the authors be held liable for any damages arising from the - use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not claim - that you wrote the original software. If you use this software in a - product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Mark Adler - madler@alumni.caltech.edu - - */ - #include "common.h" #include @@ -44,101 +5,29 @@ namespace mold { -static constexpr i64 deg = 32; -static constexpr u32 poly = 0xedb88320; +// This function "forges" a CRC. That is, given the current and a desired +// CRC32 value, crc32_solve() returns a binary blob to add to the end of +// the original data to yield the desired CRC. Trailing garbage is ignored +// by many bianry file formats, so you can create a file with a desired +// CRC using crc32_solve(). We need it for --separate-debug-file. +std::vector crc32_solve(u32 current, u32 desired) { + constexpr u32 poly = 0xedb88320; + u32 x = ~desired; -using Mat = std::array; - -static constexpr u32 gf2_matrix_times(const Mat &mat, u32 vec) { - u32 n = 0; - for (i64 i = 0; vec; vec >>= 1, i++) - if (vec & 1) - n ^= mat[i]; - return n; -} - -static constexpr Mat gf2_matrix_square(const Mat &mat) { - Mat sq; - for (i64 i = 0; i < deg; i++) - sq[i] = gf2_matrix_times(mat, mat[i]); - return sq; -} - -static consteval std::array get_crc_zero_powers() { - std::array p; - - p[1][0] = poly; - for (i64 n = 1; n < deg; n++) - p[1][n] = 1 << (n - 1); - - p[0] = gf2_matrix_square(p[1]); - p[1] = gf2_matrix_square(p[0]); - p[0] = gf2_matrix_square(p[1]); - p[1] = gf2_matrix_square(p[0]); - - for (i64 i = 2; i < 64; i++) - p[i] = gf2_matrix_square(p[i - 1]); - return p; -} - -// Efficiently apply len zero bytes to crc, returning the resulting crc. -static u32 crc_zeros(u32 crc, i64 len) { - static constexpr std::array power = get_crc_zero_powers(); - - // apply len zeros to crc - if (crc) - for (i64 n = 0; len; len >>= 1, n++) - if (len & 1) - crc = gf2_matrix_times(power[n], crc); - return crc; -} - -// Solve M x = c for x -static std::vector gf2_matrix_solve(std::vector M, u32 c) { - i64 cols = M.size(); - i64 rows = deg; - - // create adjoining identity matrix - std::vector> inv(cols); - for (i64 i = 0; i < cols; i++) { - inv[i].resize(cols); - inv[i][i] = 1; + // Each iteration computes x = (x * x^-1) mod poly. + for (i64 i = 0; i < 32; i++) { + x = std::rotl(x, 1); + x ^= (x & 1) * (poly << 1); } - for (i64 j = 0; j < rows; j++) { - u32 pos = 1 << j; + x ^= ~current; - if ((M[j] & pos) == 0) { - i64 k; - for (k = j + 1; k < cols; k++) - if (M[k] & pos) - break; - - if (k == cols) { - std::cerr << "mold: internal error: crc32_solve: no solution\n"; - exit(1); - } - - std::swap(M[j], M[k]); - std::swap(inv[j], inv[k]); - } - - for (i64 k = 0; k < cols; k++) { - if (k != j && (M[k] & pos)) { - M[k] ^= M[j]; - for (i64 i = 0; i < cols; i++) - inv[k][i] = inv[k][i] ^ inv[j][i]; - } - } - } - - // multiply inverse by c to get result x - std::vector x(cols); - for (i64 j = 0; c; c >>= 1, j++) - if (c & 1) - for (i64 i = 0; i < cols; i++) - x[i] = x[i] ^ inv[j][i]; - return x; + std::vector out(4); + out[0] = x; + out[1] = x >> 8; + out[2] = x >> 16; + out[3] = x >> 24; + return out; } // Compute a CRC for given data in parallel @@ -168,30 +57,4 @@ u32 compute_crc32(u32 crc, u8 *buf, i64 len) { return crc; } -// Given input data and a desired CRC value, this function returns -// a binary blob such that if the blob is appended to the end of the -// input data, the entire data's CRC value becomes the desired CRC. -std::vector crc32_solve(i64 datalen, u32 current, u32 desired) { - // Compute the CRC for the given data and the all-zero trailer - constexpr i64 trailer_len = 16; - current = ~crc_zeros(~current, trailer_len); - - // Compute CRCs for all bits in the trailer - std::vector mat; - for (i64 i = 0; i < trailer_len * 8; i++) { - u8 buf[trailer_len] = {}; - buf[i / 8] = 1 << (i % 8); - mat.push_back(~crc32_z(~crc_zeros(0, datalen), buf, sizeof(buf))); - } - - // Find desired trailer data - std::vector sol = gf2_matrix_solve(mat, desired ^ current); - - std::vector out(trailer_len); - for (i64 i = 0; i < trailer_len * 8; i++) - if (sol[i]) - out[i / 8] |= 1 << (i % 8); - return out; -} - } // namespace mold diff --git a/elf/passes.cc b/elf/passes.cc index b4ac71fc..dabbb7b8 100644 --- a/elf/passes.cc +++ b/elf/passes.cc @@ -3102,13 +3102,13 @@ void write_separate_debug_file(Context &ctx) { // Reverse-compute a CRC32 value so that the CRC32 checksum embedded to // the .gnu_debuglink section in the main executable matches with the // debug info file's CRC32 checksum. - std::vector &buf2 = ctx.output_file->buf2; - i64 datalen = filesize + buf2.size(); - u32 crc = compute_crc32(0, ctx.buf, filesize); - crc = compute_crc32(crc, buf2.data(), buf2.size()); - std::vector trailer = crc32_solve(datalen, crc, ctx.gnu_debuglink->crc32); + std::vector &buf2 = ctx.output_file->buf2; + if (!buf2.empty()) + crc = compute_crc32(crc, buf2.data(), buf2.size()); + + std::vector trailer = crc32_solve(crc, ctx.gnu_debuglink->crc32); append(ctx.output_file->buf2, trailer); ctx.output_file->close(ctx); }