mirror of
https://github.com/rui314/mold.git
synced 2024-10-26 13:10:46 +03:00
Compare commits
6 Commits
1bf7d4e528
...
8d524d430f
Author | SHA1 | Date | |
---|---|---|---|
|
8d524d430f | ||
|
e03f74a357 | ||
|
8c1b25b3aa | ||
|
315dfd3244 | ||
|
02ca830f42 | ||
|
24f818820b |
@ -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,15 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<u8[]> ptr;
|
std::unique_ptr<u8[]> ptr;
|
||||||
i64 perm;
|
int perm;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
class LockingOutputFile : public OutputFile<Context> {
|
||||||
|
public:
|
||||||
|
LockingOutputFile(Context &ctx, std::string path, int perm);
|
||||||
|
void resize(Context &ctx, i64 filesize);
|
||||||
|
void close(Context &ctx) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <sys/file.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -15,50 +16,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 +105,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())
|
||||||
@ -146,4 +144,52 @@ OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm
|
|||||||
return std::unique_ptr<OutputFile>(file);
|
return std::unique_ptr<OutputFile>(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LockingOutputFile is similar to MemoryMappedOutputFile, but it doesn't
|
||||||
|
// rename output files and instead acquires file lock using flock().
|
||||||
|
template <typename Context>
|
||||||
|
LockingOutputFile<Context>::LockingOutputFile(Context &ctx, std::string path,
|
||||||
|
int perm)
|
||||||
|
: OutputFile<Context>(path, 0, true) {
|
||||||
|
this->fd = ::open(path.c_str(), O_RDWR | O_CREAT, perm);
|
||||||
|
if (this->fd == -1)
|
||||||
|
Fatal(ctx) << "cannot open " << path << ": " << errno_string();
|
||||||
|
flock(this->fd, LOCK_EX);
|
||||||
|
|
||||||
|
// We may be overwriting to an existing debug info file. We want to
|
||||||
|
// make the file unusable so that gdb won't use it by accident until
|
||||||
|
// it's ready.
|
||||||
|
u8 buf[256] = {};
|
||||||
|
(void)!!write(this->fd, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
void LockingOutputFile<Context>::resize(Context &ctx, i64 filesize) {
|
||||||
|
if (ftruncate(this->fd, filesize) == -1)
|
||||||
|
Fatal(ctx) << "ftruncate failed: " << errno_string();
|
||||||
|
|
||||||
|
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED, this->fd, 0);
|
||||||
|
if (this->buf == MAP_FAILED)
|
||||||
|
Fatal(ctx) << this->path << ": mmap failed: " << errno_string();
|
||||||
|
|
||||||
|
this->filesize = filesize;
|
||||||
|
mold::output_buffer_start = this->buf;
|
||||||
|
mold::output_buffer_end = this->buf + filesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
void LockingOutputFile<Context>::close(Context &ctx) {
|
||||||
|
if (!this->is_unmapped)
|
||||||
|
munmap(this->buf, this->filesize);
|
||||||
|
|
||||||
|
if (!this->buf2.empty()) {
|
||||||
|
FILE *out = fdopen(this->fd, "w");
|
||||||
|
fseek(out, 0, SEEK_END);
|
||||||
|
fwrite(&this->buf2[0], this->buf2.size(), 1, out);
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
::close(this->fd);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mold
|
} // namespace mold
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,4 +97,17 @@ OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm
|
|||||||
return std::unique_ptr<OutputFile<Context>>(file);
|
return std::unique_ptr<OutputFile<Context>>(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
LockingOutputFile<Context>::LockingOutputFile(Context &ctx, std::string path,
|
||||||
|
int perm)
|
||||||
|
: OutputFile<Context>(path, 0, true) {
|
||||||
|
Fatal(ctx) << "LockingOutputFile is not supported on Windows";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
void LockingOutputFile<Context>::resize(Context &ctx, i64 filesize) {}
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
void LockingOutputFile<Context>::close(Context &ctx) {}
|
||||||
|
|
||||||
} // namespace mold
|
} // namespace mold
|
||||||
|
@ -163,7 +163,7 @@ tool.
|
|||||||
(i.e. `_start`) or a few other root sections. In mold, we are using
|
(i.e. `_start`) or a few other root sections. In mold, we are using
|
||||||
multiple threads to mark sections concurrently.
|
multiple threads to mark sections concurrently.
|
||||||
|
|
||||||
- Similarly, BFD, gold an lld support Identical Comdat Folding (ICF)
|
- Similarly, BFD, gold and lld support Identical Comdat Folding (ICF)
|
||||||
as yet another size optimization. ICF merges two or more read-only
|
as yet another size optimization. ICF merges two or more read-only
|
||||||
sections that happen to have the same contents and relocations.
|
sections that happen to have the same contents and relocations.
|
||||||
To do that, we have to find isomorphic subgraphs from larger graphs.
|
To do that, we have to find isomorphic subgraphs from larger graphs.
|
||||||
@ -381,7 +381,7 @@ not plan to implement and why I turned them down.
|
|||||||
fixing the final file layout.
|
fixing the final file layout.
|
||||||
|
|
||||||
The other reason to reject this idea is because there's good a
|
The other reason to reject this idea is because there's good a
|
||||||
chance for this idea to have a negative impact on linker's overall
|
chance for this idea to have a negative impact on the linker's overall
|
||||||
performance. If we copy file contents before fixing the layout, we
|
performance. If we copy file contents before fixing the layout, we
|
||||||
can't apply relocations to them while copying because symbol
|
can't apply relocations to them while copying because symbol
|
||||||
addresses are not available yet. If we fix the file layout first, we
|
addresses are not available yet. If we fix the file layout first, we
|
||||||
|
@ -1944,6 +1944,7 @@ struct Context {
|
|||||||
NotePropertySection<E> *note_property = nullptr;
|
NotePropertySection<E> *note_property = nullptr;
|
||||||
GdbIndexSection<E> *gdb_index = nullptr;
|
GdbIndexSection<E> *gdb_index = nullptr;
|
||||||
RelroPaddingSection<E> *relro_padding = nullptr;
|
RelroPaddingSection<E> *relro_padding = nullptr;
|
||||||
|
MergedSection<E> *comment = nullptr;
|
||||||
|
|
||||||
[[no_unique_address]] ContextExtras<E> extra;
|
[[no_unique_address]] ContextExtras<E> extra;
|
||||||
|
|
||||||
|
@ -2070,6 +2070,33 @@ MergedSection<E>::insert(Context<E> &ctx, std::string_view data, u64 hash,
|
|||||||
return frag;
|
return frag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
static std::string get_cmdline_args(Context<E> &ctx) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << ctx.cmdline_args[1];
|
||||||
|
for (i64 i = 2; i < ctx.cmdline_args.size(); i++)
|
||||||
|
ss << " " << ctx.cmdline_args[i];
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add strings to .comment
|
||||||
|
template <typename E>
|
||||||
|
static void add_comment_strings(Context<E> &ctx) {
|
||||||
|
auto add = [&](std::string str) {
|
||||||
|
std::string_view buf = save_string(ctx, str);
|
||||||
|
std::string_view data(buf.data(), buf.size() + 1);
|
||||||
|
ctx.comment->insert(ctx, data, hash_string(data), 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add an identification string to .comment.
|
||||||
|
add(get_mold_version());
|
||||||
|
|
||||||
|
// Embed command line arguments for debugging.
|
||||||
|
char *env = getenv("MOLD_DEBUG");
|
||||||
|
if (env && env[0])
|
||||||
|
add("mold command line: " + get_cmdline_args(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
void MergedSection<E>::resolve(Context<E> &ctx) {
|
void MergedSection<E>::resolve(Context<E> &ctx) {
|
||||||
tbb::parallel_for_each(members, [&](MergeableSection<E> *sec) {
|
tbb::parallel_for_each(members, [&](MergeableSection<E> *sec) {
|
||||||
@ -2083,6 +2110,8 @@ void MergedSection<E>::resolve(Context<E> &ctx) {
|
|||||||
sec->resolve_contents(ctx);
|
sec->resolve_contents(ctx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this == ctx.comment)
|
||||||
|
add_comment_strings(ctx);
|
||||||
resolved = true;
|
resolved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "mold.h"
|
#include "mold.h"
|
||||||
#include "blake3.h"
|
#include "blake3.h"
|
||||||
|
#include "../common/output-file.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -173,6 +174,13 @@ void create_synthetic_sections(Context<E> &ctx) {
|
|||||||
ctx.note_package = push(new NotePackageSection<E>);
|
ctx.note_package = push(new NotePackageSection<E>);
|
||||||
ctx.note_property = push(new NotePropertySection<E>);
|
ctx.note_property = push(new NotePropertySection<E>);
|
||||||
|
|
||||||
|
if (!ctx.arg.oformat_binary) {
|
||||||
|
ElfShdr<E> shdr = {};
|
||||||
|
shdr.sh_type = SHT_PROGBITS;
|
||||||
|
shdr.sh_flags = SHF_MERGE | SHF_STRINGS;
|
||||||
|
ctx.comment = MergedSection<E>::get_instance(ctx, ".comment", shdr);
|
||||||
|
}
|
||||||
|
|
||||||
if constexpr (is_riscv<E>)
|
if constexpr (is_riscv<E>)
|
||||||
ctx.extra.riscv_attributes = push(new RiscvAttributesSection<E>);
|
ctx.extra.riscv_attributes = push(new RiscvAttributesSection<E>);
|
||||||
|
|
||||||
@ -421,33 +429,6 @@ void create_merged_sections(Context<E> &ctx) {
|
|||||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||||
file->reattach_section_pieces(ctx);
|
file->reattach_section_pieces(ctx);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add strings to .comment
|
|
||||||
if (!ctx.arg.oformat_binary) {
|
|
||||||
ElfShdr<E> shdr = {};
|
|
||||||
shdr.sh_type = SHT_PROGBITS;
|
|
||||||
shdr.sh_flags = SHF_MERGE | SHF_STRINGS;
|
|
||||||
|
|
||||||
MergedSection<E> *sec = MergedSection<E>::get_instance(ctx, ".comment", shdr);
|
|
||||||
if (!sec->resolved) {
|
|
||||||
sec->map.resize(4096);
|
|
||||||
sec->resolved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto add = [&](std::string str) {
|
|
||||||
std::string_view buf = save_string(ctx, str);
|
|
||||||
std::string_view data(buf.data(), buf.size() + 1);
|
|
||||||
sec->insert(ctx, data, hash_string(data), 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add an identification string to .comment.
|
|
||||||
add(get_mold_version());
|
|
||||||
|
|
||||||
// Embed command line arguments for debugging.
|
|
||||||
char *env = getenv("MOLD_DEBUG");
|
|
||||||
if (env && env[0])
|
|
||||||
add("mold command line: " + get_cmdline_args(ctx));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
@ -459,15 +440,6 @@ void convert_common_symbols(Context<E> &ctx) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename E>
|
|
||||||
static std::string get_cmdline_args(Context<E> &ctx) {
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << ctx.cmdline_args[1];
|
|
||||||
for (i64 i = 2; i < ctx.cmdline_args.size(); i++)
|
|
||||||
ss << " " << ctx.cmdline_args[i];
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
static bool has_ctors_and_init_array(Context<E> &ctx) {
|
static bool has_ctors_and_init_array(Context<E> &ctx) {
|
||||||
bool x = false;
|
bool x = false;
|
||||||
@ -1340,7 +1312,7 @@ void compute_section_sizes(Context<E> &ctx) {
|
|||||||
Timer t(ctx, "compute_section_sizes");
|
Timer t(ctx, "compute_section_sizes");
|
||||||
|
|
||||||
if constexpr (needs_thunk<E>) {
|
if constexpr (needs_thunk<E>) {
|
||||||
// Chunk<E>::compute_section_size may obtain a global lock to create
|
// Chunk<E>::compute_section_size obtains a global lock to create
|
||||||
// range extension thunks. I don't know why, but using parallel_for
|
// range extension thunks. I don't know why, but using parallel_for
|
||||||
// loop both inside and outside of the lock may cause a deadlock. It
|
// loop both inside and outside of the lock may cause a deadlock. It
|
||||||
// might be a bug in TBB. For now, I'll avoid using parallel_for_each
|
// might be a bug in TBB. For now, I'll avoid using parallel_for_each
|
||||||
@ -3056,6 +3028,10 @@ template <typename E>
|
|||||||
void write_separate_debug_file(Context<E> &ctx) {
|
void write_separate_debug_file(Context<E> &ctx) {
|
||||||
Timer t(ctx, "write_separate_debug_file");
|
Timer t(ctx, "write_separate_debug_file");
|
||||||
|
|
||||||
|
// Open an output file early
|
||||||
|
LockingOutputFile<Context<E>> *file =
|
||||||
|
new LockingOutputFile<Context<E>>(ctx, ctx.arg.separate_debug_file, 0666);
|
||||||
|
|
||||||
// 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.detach)
|
if (ctx.arg.detach)
|
||||||
@ -3087,11 +3063,9 @@ void write_separate_debug_file(Context<E> &ctx) {
|
|||||||
|
|
||||||
// Write to the debug info file as if it were a regular output file.
|
// Write to the debug info file as if it were a regular output file.
|
||||||
compute_section_headers(ctx);
|
compute_section_headers(ctx);
|
||||||
i64 filesize = set_osec_offsets(ctx);
|
file->resize(ctx, set_osec_offsets(ctx));
|
||||||
|
|
||||||
ctx.output_file =
|
ctx.output_file.reset(file);
|
||||||
OutputFile<Context<E>>::open(ctx, ctx.arg.separate_debug_file,
|
|
||||||
filesize, 0666);
|
|
||||||
ctx.buf = ctx.output_file->buf;
|
ctx.buf = ctx.output_file->buf;
|
||||||
|
|
||||||
copy_chunks(ctx);
|
copy_chunks(ctx);
|
||||||
@ -3102,7 +3076,7 @@ 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.
|
||||||
u32 crc = compute_crc32(0, ctx.buf, filesize);
|
u32 crc = compute_crc32(0, ctx.buf, ctx.output_file->filesize);
|
||||||
|
|
||||||
std::vector<u8> &buf2 = ctx.output_file->buf2;
|
std::vector<u8> &buf2 = ctx.output_file->buf2;
|
||||||
if (!buf2.empty())
|
if (!buf2.empty())
|
||||||
|
@ -16,11 +16,12 @@ $CC -c -o $t/a.o $t/a.c -g
|
|||||||
$CC -B. -o $t/exe1 $t/a.o -Wl,--separate-debug-file
|
$CC -B. -o $t/exe1 $t/a.o -Wl,--separate-debug-file
|
||||||
readelf -SW $t/exe1 | grep -Fq .gnu_debuglink
|
readelf -SW $t/exe1 | grep -Fq .gnu_debuglink
|
||||||
|
|
||||||
|
flock $t/exe1 true
|
||||||
|
gdb $t/exe1 -ex 'list main' -ex 'quit' | grep -Fq printf
|
||||||
|
|
||||||
$CC -c -o $t/a.o $t/a.c -g
|
$CC -c -o $t/a.o $t/a.c -g
|
||||||
$CC -B. -o $t/exe2 $t/a.o -Wl,--separate-debug-file -Wl,--no-build-id
|
$CC -B. -o $t/exe2 $t/a.o -Wl,--separate-debug-file -Wl,--no-build-id
|
||||||
readelf -SW $t/exe2 | grep -Fq .gnu_debuglink
|
readelf -SW $t/exe2 | grep -Fq .gnu_debuglink
|
||||||
|
|
||||||
sleep 1
|
flock $t/exe2 true
|
||||||
|
|
||||||
gdb $t/exe1 -ex 'list main' -ex 'quit' | grep -Fq printf
|
|
||||||
gdb $t/exe2 -ex 'list main' -ex 'quit' | grep -Fq printf
|
gdb $t/exe2 -ex 'list main' -ex 'quit' | grep -Fq printf
|
||||||
|
Loading…
Reference in New Issue
Block a user