1
1
mirror of https://github.com/rui314/mold.git synced 2024-09-11 13:06:59 +03:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Rui Ueyama
315dfd3244 Fix CI 2024-07-09 15:25:53 +09:00
Rui Ueyama
02ca830f42 Refactor 2024-07-09 15:07:41 +09:00
Rui Ueyama
18da5b654e Add --no-detach to write to a separate debug file in the foreground
--detach is the default.
2024-07-09 12:06:41 +09:00
Rui Ueyama
97a1e218c5 Simplify crc32_solve()
The code was originally written by Pete Cawley
https://gist.github.com/corsix/bdfc8f2f1dc0f28de39f74de9bf4f060
2024-07-09 10:15:26 +09:00
7 changed files with 102 additions and 234 deletions

View File

@ -735,7 +735,7 @@ template <typename Context>
class OutputFile { class OutputFile {
public: public:
static std::unique_ptr<OutputFile<Context>> static std::unique_ptr<OutputFile<Context>>
open(Context &ctx, std::string path, i64 filesize, i64 perm); open(Context &ctx, std::string path, i64 filesize, int perm);
virtual void close(Context &ctx) = 0; virtual void close(Context &ctx) = 0;
virtual ~OutputFile() = default; virtual ~OutputFile() = default;
@ -743,7 +743,7 @@ public:
u8 *buf = nullptr; u8 *buf = nullptr;
std::vector<u8> buf2; std::vector<u8> buf2;
std::string path; std::string path;
i64 fd = -1; int fd = -1;
i64 filesize = 0; i64 filesize = 0;
bool is_mmapped = false; bool is_mmapped = false;
bool is_unmapped = false; bool is_unmapped = false;
@ -756,7 +756,7 @@ protected:
template <typename Context> template <typename Context>
class MallocOutputFile : public OutputFile<Context> { class MallocOutputFile : public OutputFile<Context> {
public: public:
MallocOutputFile(Context &ctx, std::string path, i64 filesize, i64 perm) MallocOutputFile(Context &ctx, std::string path, i64 filesize, int perm)
: OutputFile<Context>(path, filesize, false), ptr(new u8[filesize]), : OutputFile<Context>(path, filesize, false), ptr(new u8[filesize]),
perm(perm) { perm(perm) {
this->buf = ptr.get(); this->buf = ptr.get();
@ -792,7 +792,7 @@ public:
private: private:
std::unique_ptr<u8[]> ptr; std::unique_ptr<u8[]> ptr;
i64 perm; int perm;
}; };
// //
@ -905,7 +905,7 @@ void release_global_lock();
// //
u32 compute_crc32(u32 crc, u8 *buf, i64 len); u32 compute_crc32(u32 crc, u8 *buf, i64 len);
std::vector<u8> crc32_solve(i64 datalen, u32 current, u32 want); std::vector<u8> crc32_solve(u32 current, u32 desired);
// //
// compress.cc // compress.cc

View File

@ -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 "common.h"
#include <tbb/parallel_for_each.h> #include <tbb/parallel_for_each.h>
@ -44,101 +5,29 @@
namespace mold { namespace mold {
static constexpr i64 deg = 32; // This function "forges" a CRC. That is, given the current and a desired
static constexpr u32 poly = 0xedb88320; // 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<u8> crc32_solve(u32 current, u32 desired) {
constexpr u32 poly = 0xedb88320;
u32 x = ~desired;
using Mat = std::array<u32, deg>; // Each iteration computes x = (x * x^-1) mod poly.
for (i64 i = 0; i < 32; i++) {
static constexpr u32 gf2_matrix_times(const Mat &mat, u32 vec) { x = std::rotl(x, 1);
u32 n = 0; x ^= (x & 1) * (poly << 1);
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<Mat, 64> get_crc_zero_powers() {
std::array<Mat, 64> 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<Mat, 64> 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<bool> gf2_matrix_solve(std::vector<u32> M, u32 c) {
i64 cols = M.size();
i64 rows = deg;
// create adjoining identity matrix
std::vector<std::vector<bool>> inv(cols);
for (i64 i = 0; i < cols; i++) {
inv[i].resize(cols);
inv[i][i] = 1;
} }
for (i64 j = 0; j < rows; j++) { x ^= ~current;
u32 pos = 1 << j;
if ((M[j] & pos) == 0) { std::vector<u8> out(4);
i64 k; out[0] = x;
for (k = j + 1; k < cols; k++) out[1] = x >> 8;
if (M[k] & pos) out[2] = x >> 16;
break; out[3] = x >> 24;
return out;
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<bool> 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;
} }
// Compute a CRC for given data in parallel // Compute a CRC for given data in parallel
@ -168,30 +57,4 @@ u32 compute_crc32(u32 crc, u8 *buf, i64 len) {
return crc; 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<u8> 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<u32> 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<bool> sol = gf2_matrix_solve(mat, desired ^ current);
std::vector<u8> 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 } // namespace mold

View File

@ -15,50 +15,47 @@ inline u32 get_umask() {
} }
template <typename Context> template <typename Context>
static std::pair<i64, char *> static int
open_or_create_file(Context &ctx, std::string path, i64 filesize, i64 perm) { open_or_create_file(Context &ctx, std::string path, std::string tmpfile,
std::string tmpl = filepath(path).parent_path() / ".mold-XXXXXX"; int perm) {
char *path2 = (char *)save_string(ctx, tmpl).data();
i64 fd = mkstemp(path2);
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
// Reuse an existing file if exists and writable because on Linux, // Reuse an existing file if exists and writable because on Linux,
// writing to an existing file is much faster than creating a fresh // writing to an existing file is much faster than creating a fresh
// file and writing to it. // file and writing to it.
if (ctx.overwrite_output_file && rename(path.c_str(), path2) == 0) { if (ctx.overwrite_output_file && rename(path.c_str(), tmpfile.c_str()) == 0) {
::close(fd); i64 fd = ::open(tmpfile.c_str(), O_RDWR | O_CREAT, perm);
fd = ::open(path2, O_RDWR | O_CREAT, perm); if (fd != -1)
if (fd != -1 && !ftruncate(fd, filesize) && !fchmod(fd, perm & ~get_umask())) return fd;
return {fd, path2}; unlink(tmpfile.c_str());
unlink(path2);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
} }
if (fchmod(fd, (perm & ~get_umask())) == -1) i64 fd = ::open(tmpfile.c_str(), O_RDWR | O_CREAT, perm);
Fatal(ctx) << "fchmod failed: " << errno_string(); if (fd == -1)
Fatal(ctx) << "cannot open " << tmpfile << ": " << errno_string();
#ifdef __linux__ return fd;
if (fallocate(fd, 0, 0, filesize) == 0)
return {fd, path2};
#endif
if (ftruncate(fd, filesize) == -1)
Fatal(ctx) << "ftruncate failed: " << errno_string();
return {fd, path2};
} }
template <typename Context> template <typename Context>
class MemoryMappedOutputFile : public OutputFile<Context> { class MemoryMappedOutputFile : public OutputFile<Context> {
public: public:
MemoryMappedOutputFile(Context &ctx, std::string path, i64 filesize, i64 perm) MemoryMappedOutputFile(Context &ctx, std::string path, i64 filesize, int perm)
: OutputFile<Context>(path, filesize, true) { : OutputFile<Context>(path, filesize, true) {
std::tie(this->fd, output_tmpfile) = std::filesystem::path dir = filepath(path).parent_path();
open_or_create_file(ctx, path, filesize, perm); std::string filename = filepath(path).filename().string();
std::string tmpfile = dir / ("." + filename + "." + std::to_string(getpid()));
this->fd = open_or_create_file(ctx, path, tmpfile, perm);
if (fchmod(this->fd, perm & ~get_umask()) == -1)
Fatal(ctx) << "fchmod failed: " << errno_string();
if (ftruncate(this->fd, filesize) == -1)
Fatal(ctx) << "ftruncate failed: " << errno_string();
output_tmpfile = (char *)save_string(ctx, tmpfile).data();
#ifdef __linux__
fallocate(this->fd, 0, 0, filesize);
#endif
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE, this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED, this->fd, 0); MAP_SHARED, this->fd, 0);
@ -107,7 +104,7 @@ private:
template <typename Context> template <typename Context>
std::unique_ptr<OutputFile<Context>> std::unique_ptr<OutputFile<Context>>
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm) { OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, int perm) {
Timer t(ctx, "open_file"); Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty()) if (path.starts_with('/') && !ctx.arg.chroot.empty())

View File

@ -9,36 +9,35 @@ namespace mold {
template <typename Context> template <typename Context>
class MemoryMappedOutputFile : public OutputFile<Context> { class MemoryMappedOutputFile : public OutputFile<Context> {
public: public:
MemoryMappedOutputFile(Context &ctx, std::string path, i64 filesize, i64 perm) MemoryMappedOutputFile(Context &ctx, std::string path, i64 filesize, int perm)
: OutputFile<Context>(path, filesize, true) { : OutputFile<Context>(path, filesize, true) {
// TODO: use intermediate temporary file for output. // TODO: use intermediate temporary file for output.
DWORD file_attrs = DWORD attrs = (perm & 0200) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_READONLY;
(perm & 0200) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_READONLY;
file_handle = handle = CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE,
CreateFileA(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, attrs, nullptr);
nullptr, CREATE_ALWAYS, file_attrs, nullptr); if (handle == INVALID_HANDLE_VALUE)
if (file_handle == INVALID_HANDLE_VALUE)
Fatal(ctx) << "cannot open " << path << ": " << GetLastError(); Fatal(ctx) << "cannot open " << path << ": " << GetLastError();
HANDLE mapping_handle = CreateFileMapping( HANDLE map = CreateFileMapping(handle, nullptr, PAGE_READWRITE, 0,
file_handle, nullptr, PAGE_READWRITE, 0, filesize, nullptr); filesize, nullptr);
if (!mapping_handle) if (!map)
Fatal(ctx) << path << ": CreateFileMapping failed: " << GetLastError(); Fatal(ctx) << path << ": CreateFileMapping failed: " << GetLastError();
this->buf = this->buf = (u8 *)MapViewOfFile(map, FILE_MAP_WRITE, 0, 0, filesize);
(u8 *)MapViewOfFile(mapping_handle, FILE_MAP_WRITE, 0, 0, filesize);
CloseHandle(mapping_handle);
if (!this->buf) if (!this->buf)
Fatal(ctx) << path << ": MapViewOfFile failed: " << GetLastError(); Fatal(ctx) << path << ": MapViewOfFile failed: " << GetLastError();
CloseHandle(map);
mold::output_buffer_start = this->buf; mold::output_buffer_start = this->buf;
mold::output_buffer_end = this->buf + filesize; mold::output_buffer_end = this->buf + filesize;
} }
~MemoryMappedOutputFile() { ~MemoryMappedOutputFile() {
if (file_handle != INVALID_HANDLE_VALUE) if (handle != INVALID_HANDLE_VALUE)
CloseHandle(file_handle); CloseHandle(handle);
} }
void close(Context &ctx) override { void close(Context &ctx) override {
@ -47,28 +46,27 @@ public:
UnmapViewOfFile(this->buf); UnmapViewOfFile(this->buf);
if (!this->buf2.empty()) { if (!this->buf2.empty()) {
if (SetFilePointer(file_handle, 0, nullptr, FILE_END) == if (SetFilePointer(handle, 0, nullptr, FILE_END) == INVALID_SET_FILE_POINTER)
INVALID_SET_FILE_POINTER) Fatal(ctx) << this->path << ": SetFilePointer failed: "
Fatal(ctx) << this->path << GetLastError();
<< ": SetFilePointer failed: " << GetLastError();
DWORD written; DWORD written;
if (!WriteFile(file_handle, this->buf2.data(), this->buf2.size(), if (!WriteFile(handle, this->buf2.data(), this->buf2.size(), &written,
&written, nullptr)) nullptr))
Fatal(ctx) << this->path << ": WriteFile failed: " << GetLastError(); Fatal(ctx) << this->path << ": WriteFile failed: " << GetLastError();
} }
CloseHandle(file_handle); CloseHandle(handle);
file_handle = INVALID_HANDLE_VALUE; handle = INVALID_HANDLE_VALUE;
} }
private: private:
HANDLE file_handle; HANDLE handle;
}; };
template <typename Context> template <typename Context>
std::unique_ptr<OutputFile<Context>> std::unique_ptr<OutputFile<Context>>
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm) { OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, int perm) {
Timer t(ctx, "open_file"); Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty()) if (path.starts_with('/') && !ctx.arg.chroot.empty())
@ -78,14 +76,13 @@ OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm
if (path == "-") { if (path == "-") {
is_special = true; is_special = true;
} else { } else {
HANDLE file_handle = HANDLE h = CreateFileA(path.c_str(), GENERIC_READ,
CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (h != INVALID_HANDLE_VALUE) {
if (file_handle != INVALID_HANDLE_VALUE) { if (GetFileType(h) != FILE_TYPE_DISK)
if (GetFileType(file_handle) != FILE_TYPE_DISK)
is_special = true; is_special = true;
CloseHandle(file_handle); CloseHandle(h);
} }
} }

View File

@ -85,6 +85,8 @@ Options:
--defsym=SYMBOL=VALUE Define a symbol alias --defsym=SYMBOL=VALUE Define a symbol alias
--demangle Demangle C++ symbols in log messages (default) --demangle Demangle C++ symbols in log messages (default)
--no-demangle --no-demangle
--detach Create separate debug info file in the background (default)
--no-detach
--enable-new-dtags Emit DT_RUNPATH for --rpath (default) --enable-new-dtags Emit DT_RUNPATH for --rpath (default)
--disable-new-dtags Emit DT_RPATH for --rpath --disable-new-dtags Emit DT_RPATH for --rpath
--execute-only Make executable segments unreadable --execute-only Make executable segments unreadable
@ -759,6 +761,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.demangle = true; ctx.arg.demangle = true;
} else if (read_flag("no-demangle")) { } else if (read_flag("no-demangle")) {
ctx.arg.demangle = false; ctx.arg.demangle = false;
} else if (read_flag("detach")) {
ctx.arg.detach = true;
} else if (read_flag("no-detach")) {
ctx.arg.detach = false;
} else if (read_flag("default-symver")) { } else if (read_flag("default-symver")) {
ctx.arg.default_symver = true; ctx.arg.default_symver = true;
} else if (read_flag("noinhibit-exec")) { } else if (read_flag("noinhibit-exec")) {
@ -1413,6 +1419,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (ctx.arg.shared && warn_shared_textrel) if (ctx.arg.shared && warn_shared_textrel)
ctx.arg.warn_textrel = true; ctx.arg.warn_textrel = true;
// We don't want the background process to write to stdout
if (ctx.arg.stats || ctx.arg.perf)
ctx.arg.detach = false;
ctx.arg.undefined.push_back(ctx.arg.entry); ctx.arg.undefined.push_back(ctx.arg.entry);
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) { for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {

View File

@ -1740,6 +1740,7 @@ struct Context {
bool color_diagnostics = false; bool color_diagnostics = false;
bool default_symver = false; bool default_symver = false;
bool demangle = true; bool demangle = true;
bool detach = true;
bool discard_all = false; bool discard_all = false;
bool discard_locals = false; bool discard_locals = false;
bool eh_frame_hdr = true; bool eh_frame_hdr = true;

View File

@ -3058,7 +3058,7 @@ void write_separate_debug_file(Context<E> &ctx) {
// We want to write to the debug info file in background so that the // We want to write to the debug info file in background so that the
// user doesn't have to wait for it to complete. // user doesn't have to wait for it to complete.
if (!ctx.arg.stats && !ctx.arg.perf) if (ctx.arg.detach)
notify_parent(); notify_parent();
// A debug info file contains all sections as the original file, though // A debug info file contains all sections as the original file, though
@ -3102,13 +3102,13 @@ void write_separate_debug_file(Context<E> &ctx) {
// Reverse-compute a CRC32 value so that the CRC32 checksum embedded to // Reverse-compute a CRC32 value so that the CRC32 checksum embedded to
// the .gnu_debuglink section in the main executable matches with the // the .gnu_debuglink section in the main executable matches with the
// debug info file's CRC32 checksum. // debug info file's CRC32 checksum.
std::vector<u8> &buf2 = ctx.output_file->buf2;
i64 datalen = filesize + buf2.size();
u32 crc = compute_crc32(0, ctx.buf, filesize); u32 crc = compute_crc32(0, ctx.buf, filesize);
crc = compute_crc32(crc, buf2.data(), buf2.size());
std::vector<u8> trailer = crc32_solve(datalen, crc, ctx.gnu_debuglink->crc32); std::vector<u8> &buf2 = ctx.output_file->buf2;
if (!buf2.empty())
crc = compute_crc32(crc, buf2.data(), buf2.size());
std::vector<u8> trailer = crc32_solve(crc, ctx.gnu_debuglink->crc32);
append(ctx.output_file->buf2, trailer); append(ctx.output_file->buf2, trailer);
ctx.output_file->close(ctx); ctx.output_file->close(ctx);
} }