mirror of
https://github.com/rui314/mold.git
synced 2024-10-26 04:59:48 +03:00
Compare commits
3 Commits
b145377c39
...
c6b54532e9
Author | SHA1 | Date | |
---|---|---|---|
|
c6b54532e9 | ||
|
3936134823 | ||
|
55ca05bab6 |
23
elf/main.cc
23
elf/main.cc
@ -375,11 +375,9 @@ int elf_main(int argc, char **argv) {
|
|||||||
<< ": " << errno_string();
|
<< ": " << errno_string();
|
||||||
|
|
||||||
// Fork a subprocess unless --no-fork is given.
|
// Fork a subprocess unless --no-fork is given.
|
||||||
std::function<void()> on_complete;
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||||
if (ctx.arg.fork)
|
if (ctx.arg.fork)
|
||||||
on_complete = fork_child();
|
fork_child();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
acquire_global_lock();
|
acquire_global_lock();
|
||||||
@ -664,8 +662,13 @@ int elf_main(int argc, char **argv) {
|
|||||||
// so we sort them.
|
// so we sort them.
|
||||||
ctx.reldyn->sort(ctx);
|
ctx.reldyn->sort(ctx);
|
||||||
|
|
||||||
// Zero-clear paddings between sections
|
// .note.gnu.build-id section contains a cryptographic hash of the
|
||||||
clear_padding(ctx);
|
// entire output file. Now that we wrote everything except build-id,
|
||||||
|
// we can compute it.
|
||||||
|
if (ctx.buildid) {
|
||||||
|
compute_build_id(ctx);
|
||||||
|
ctx.buildid->copy_buf(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// .gdb_index's contents cannot be constructed before applying
|
// .gdb_index's contents cannot be constructed before applying
|
||||||
// relocations to other debug sections. We have relocated debug
|
// relocations to other debug sections. We have relocated debug
|
||||||
@ -673,12 +676,6 @@ int elf_main(int argc, char **argv) {
|
|||||||
if (ctx.gdb_index)
|
if (ctx.gdb_index)
|
||||||
write_gdb_index(ctx);
|
write_gdb_index(ctx);
|
||||||
|
|
||||||
// .note.gnu.build-id section contains a cryptographic hash of the
|
|
||||||
// entire output file. Now that we wrote everything except build-id,
|
|
||||||
// we can compute it.
|
|
||||||
if (ctx.buildid)
|
|
||||||
ctx.buildid->write_buildid(ctx);
|
|
||||||
|
|
||||||
t_copy.stop();
|
t_copy.stop();
|
||||||
ctx.checkpoint();
|
ctx.checkpoint();
|
||||||
|
|
||||||
@ -707,8 +704,8 @@ int elf_main(int argc, char **argv) {
|
|||||||
std::cout << std::flush;
|
std::cout << std::flush;
|
||||||
std::cerr << std::flush;
|
std::cerr << std::flush;
|
||||||
|
|
||||||
if (on_complete)
|
if (ctx.arg.fork)
|
||||||
on_complete();
|
notify_parent();
|
||||||
|
|
||||||
release_global_lock();
|
release_global_lock();
|
||||||
|
|
||||||
|
21
elf/mold.h
21
elf/mold.h
@ -949,9 +949,8 @@ public:
|
|||||||
|
|
||||||
void update_shdr(Context<E> &ctx) override;
|
void update_shdr(Context<E> &ctx) override;
|
||||||
void copy_buf(Context<E> &ctx) override;
|
void copy_buf(Context<E> &ctx) override;
|
||||||
void write_buildid(Context<E> &ctx);
|
|
||||||
|
|
||||||
static constexpr i64 HEADER_SIZE = 16;
|
std::vector<u8> contents;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
@ -1368,7 +1367,8 @@ void print_map(Context<E> &ctx);
|
|||||||
// subprocess.cc
|
// subprocess.cc
|
||||||
//
|
//
|
||||||
|
|
||||||
std::function<void()> fork_child();
|
void fork_child();
|
||||||
|
void notify_parent();
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
@ -1426,11 +1426,11 @@ template <typename E> void apply_version_script(Context<E> &);
|
|||||||
template <typename E> void parse_symbol_version(Context<E> &);
|
template <typename E> void parse_symbol_version(Context<E> &);
|
||||||
template <typename E> void compute_import_export(Context<E> &);
|
template <typename E> void compute_import_export(Context<E> &);
|
||||||
template <typename E> void compute_address_significance(Context<E> &);
|
template <typename E> void compute_address_significance(Context<E> &);
|
||||||
template <typename E> void clear_padding(Context<E> &);
|
|
||||||
template <typename E> void compute_section_headers(Context<E> &);
|
template <typename E> void compute_section_headers(Context<E> &);
|
||||||
template <typename E> i64 set_osec_offsets(Context<E> &);
|
template <typename E> i64 set_osec_offsets(Context<E> &);
|
||||||
template <typename E> void fix_synthetic_symbols(Context<E> &);
|
template <typename E> void fix_synthetic_symbols(Context<E> &);
|
||||||
template <typename E> i64 compress_debug_sections(Context<E> &);
|
template <typename E> i64 compress_debug_sections(Context<E> &);
|
||||||
|
template <typename E> void compute_build_id(Context<E> &);
|
||||||
template <typename E> void write_dependency_file(Context<E> &);
|
template <typename E> void write_dependency_file(Context<E> &);
|
||||||
template <typename E> void show_stats(Context<E> &);
|
template <typename E> void show_stats(Context<E> &);
|
||||||
|
|
||||||
@ -1565,7 +1565,18 @@ private:
|
|||||||
//
|
//
|
||||||
|
|
||||||
struct BuildId {
|
struct BuildId {
|
||||||
i64 size() const;
|
i64 size() const {
|
||||||
|
switch (kind) {
|
||||||
|
case HEX:
|
||||||
|
return value.size();
|
||||||
|
case HASH:
|
||||||
|
return hash_size;
|
||||||
|
case UUID:
|
||||||
|
return 16;
|
||||||
|
default:
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum { NONE, HEX, HASH, UUID } kind = NONE;
|
enum { NONE, HEX, HASH, UUID } kind = NONE;
|
||||||
std::vector<u8> value;
|
std::vector<u8> value;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#include "mold.h"
|
#include "mold.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "blake3.h"
|
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -2526,89 +2524,21 @@ void VerdefSection<E>::copy_buf(Context<E> &ctx) {
|
|||||||
write_vector(ctx.buf + this->shdr.sh_offset, contents);
|
write_vector(ctx.buf + this->shdr.sh_offset, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline i64 BuildId::size() const {
|
|
||||||
switch (kind) {
|
|
||||||
case HEX:
|
|
||||||
return value.size();
|
|
||||||
case HASH:
|
|
||||||
return hash_size;
|
|
||||||
case UUID:
|
|
||||||
return 16;
|
|
||||||
default:
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
void BuildIdSection<E>::update_shdr(Context<E> &ctx) {
|
void BuildIdSection<E>::update_shdr(Context<E> &ctx) {
|
||||||
this->shdr.sh_size = HEADER_SIZE + ctx.arg.build_id.size();
|
this->shdr.sh_size = ctx.arg.build_id.size() + 16; // +16 for the header
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
void BuildIdSection<E>::copy_buf(Context<E> &ctx) {
|
void BuildIdSection<E>::copy_buf(Context<E> &ctx) {
|
||||||
U32<E> *base = (U32<E> *)(ctx.buf + this->shdr.sh_offset);
|
U32<E> *base = (U32<E> *)(ctx.buf + this->shdr.sh_offset);
|
||||||
memset(base, 0, this->shdr.sh_size);
|
memset(base, 0, this->shdr.sh_size);
|
||||||
base[0] = 4; // Name size
|
|
||||||
base[1] = ctx.arg.build_id.size(); // Hash size
|
|
||||||
base[2] = NT_GNU_BUILD_ID; // Type
|
|
||||||
memcpy(base + 3, "GNU", 4); // Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// BLAKE3 is a cryptographic hash function just like SHA256.
|
base[0] = 4; // Name size
|
||||||
// We use it instead of SHA256 because it's faster.
|
base[1] = ctx.arg.build_id.size(); // Hash size
|
||||||
static void blake3_hash(u8 *buf, i64 size, u8 *out) {
|
base[2] = NT_GNU_BUILD_ID; // Type
|
||||||
blake3_hasher hasher;
|
memcpy(base + 3, "GNU", 4); // Name string
|
||||||
blake3_hasher_init(&hasher);
|
write_vector(base + 4, contents); // Build ID
|
||||||
blake3_hasher_update(&hasher, buf, size);
|
|
||||||
blake3_hasher_finalize(&hasher, out, BLAKE3_OUT_LEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename E>
|
|
||||||
void BuildIdSection<E>::write_buildid(Context<E> &ctx) {
|
|
||||||
Timer t(ctx, "build_id");
|
|
||||||
u8 *buf = ctx.buf + this->shdr.sh_offset + HEADER_SIZE;
|
|
||||||
|
|
||||||
switch (ctx.arg.build_id.kind) {
|
|
||||||
case BuildId::HEX:
|
|
||||||
write_vector(buf, ctx.arg.build_id.value);
|
|
||||||
return;
|
|
||||||
case BuildId::HASH: {
|
|
||||||
i64 shard_size = 4 * 1024 * 1024;
|
|
||||||
i64 filesize = ctx.output_file->filesize;
|
|
||||||
i64 num_shards = align_to(filesize, shard_size) / shard_size;
|
|
||||||
std::vector<u8> shards(num_shards * BLAKE3_OUT_LEN);
|
|
||||||
|
|
||||||
tbb::parallel_for((i64)0, num_shards, [&](i64 i) {
|
|
||||||
u8 *begin = ctx.buf + shard_size * i;
|
|
||||||
u8 *end = (i == num_shards - 1) ? ctx.buf + filesize : begin + shard_size;
|
|
||||||
blake3_hash(begin, end - begin, shards.data() + i * BLAKE3_OUT_LEN);
|
|
||||||
|
|
||||||
#ifdef HAVE_MADVISE
|
|
||||||
// Make the kernel page out the file contents we've just written
|
|
||||||
// so that subsequent close(2) call will become quicker.
|
|
||||||
if (i > 0 && ctx.output_file->is_mmapped)
|
|
||||||
madvise(begin, end - begin, MADV_DONTNEED);
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
|
|
||||||
u8 digest[BLAKE3_OUT_LEN];
|
|
||||||
blake3_hash(shards.data(), shards.size(), digest);
|
|
||||||
|
|
||||||
assert(ctx.arg.build_id.size() <= BLAKE3_OUT_LEN);
|
|
||||||
memcpy(buf, digest, ctx.arg.build_id.size());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case BuildId::UUID: {
|
|
||||||
get_random_bytes(buf, 16);
|
|
||||||
|
|
||||||
// Indicate that this is UUIDv4 as defined by RFC4122
|
|
||||||
buf[6] = (buf[6] & 0b0000'1111) | 0b0100'0000;
|
|
||||||
buf[8] = (buf[8] & 0b0011'1111) | 0b1000'0000;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "mold.h"
|
#include "mold.h"
|
||||||
|
#include "blake3.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -1720,6 +1721,22 @@ void copy_chunks(Context<E> &ctx) {
|
|||||||
|
|
||||||
if constexpr (is_arm32<E>)
|
if constexpr (is_arm32<E>)
|
||||||
fixup_arm_exidx_section(ctx);
|
fixup_arm_exidx_section(ctx);
|
||||||
|
|
||||||
|
// Zero-clear paddings between chunks
|
||||||
|
auto zero = [&](Chunk<E> *chunk, i64 next_start) {
|
||||||
|
i64 pos = chunk->shdr.sh_offset + chunk->shdr.sh_size;
|
||||||
|
memset(ctx.buf + pos, 0, next_start - pos);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Chunk<E> *> chunks = ctx.chunks;
|
||||||
|
|
||||||
|
std::erase_if(chunks, [](Chunk<E> *chunk) {
|
||||||
|
return chunk->shdr.sh_type == SHT_NOBITS;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (i64 i = 1; i < chunks.size(); i++)
|
||||||
|
zero(chunks[i - 1], chunks[i]->shdr.sh_offset);
|
||||||
|
zero(chunks.back(), ctx.output_file->filesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite the leading endbr64 instruction with a nop if a function
|
// Rewrite the leading endbr64 instruction with a nop if a function
|
||||||
@ -2168,26 +2185,6 @@ void compute_address_significance(Context<E> &ctx) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename E>
|
|
||||||
void clear_padding(Context<E> &ctx) {
|
|
||||||
Timer t(ctx, "clear_padding");
|
|
||||||
|
|
||||||
auto zero = [&](Chunk<E> *chunk, i64 next_start) {
|
|
||||||
i64 pos = chunk->shdr.sh_offset + chunk->shdr.sh_size;
|
|
||||||
memset(ctx.buf + pos, 0, next_start - pos);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Chunk<E> *> chunks = ctx.chunks;
|
|
||||||
|
|
||||||
std::erase_if(chunks, [](Chunk<E> *chunk) {
|
|
||||||
return chunk->shdr.sh_type == SHT_NOBITS;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (i64 i = 1; i < chunks.size(); i++)
|
|
||||||
zero(chunks[i - 1], chunks[i]->shdr.sh_offset);
|
|
||||||
zero(chunks.back(), ctx.output_file->filesize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We want to sort output chunks in the following order.
|
// We want to sort output chunks in the following order.
|
||||||
//
|
//
|
||||||
// <ELF header>
|
// <ELF header>
|
||||||
@ -2999,6 +2996,65 @@ i64 compress_debug_sections(Context<E> &ctx) {
|
|||||||
return set_osec_offsets(ctx);
|
return set_osec_offsets(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BLAKE3 is a cryptographic hash function just like SHA256.
|
||||||
|
// We use it instead of SHA256 because it's faster.
|
||||||
|
static void blake3_hash(u8 *buf, i64 size, u8 *out) {
|
||||||
|
blake3_hasher hasher;
|
||||||
|
blake3_hasher_init(&hasher);
|
||||||
|
blake3_hasher_update(&hasher, buf, size);
|
||||||
|
blake3_hasher_finalize(&hasher, out, BLAKE3_OUT_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
void compute_build_id(Context<E> &ctx) {
|
||||||
|
Timer t(ctx, "compute_build_id");
|
||||||
|
|
||||||
|
switch (ctx.arg.build_id.kind) {
|
||||||
|
case BuildId::HEX:
|
||||||
|
ctx.buildid->contents = ctx.arg.build_id.value;
|
||||||
|
break;
|
||||||
|
case BuildId::HASH: {
|
||||||
|
i64 shard_size = 4 * 1024 * 1024;
|
||||||
|
i64 filesize = ctx.output_file->filesize;
|
||||||
|
i64 num_shards = align_to(filesize, shard_size) / shard_size;
|
||||||
|
std::vector<u8> shards(num_shards * BLAKE3_OUT_LEN);
|
||||||
|
|
||||||
|
tbb::parallel_for((i64)0, num_shards, [&](i64 i) {
|
||||||
|
u8 *begin = ctx.buf + shard_size * i;
|
||||||
|
u8 *end = (i == num_shards - 1) ? ctx.buf + filesize : begin + shard_size;
|
||||||
|
blake3_hash(begin, end - begin, shards.data() + i * BLAKE3_OUT_LEN);
|
||||||
|
|
||||||
|
#ifdef HAVE_MADVISE
|
||||||
|
// Make the kernel page out the file contents we've just written
|
||||||
|
// so that subsequent close(2) call will become quicker.
|
||||||
|
if (i > 0 && ctx.output_file->is_mmapped)
|
||||||
|
madvise(begin, end - begin, MADV_DONTNEED);
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
u8 buf[BLAKE3_OUT_LEN];
|
||||||
|
blake3_hash(shards.data(), shards.size(), buf);
|
||||||
|
|
||||||
|
assert(ctx.arg.build_id.size() <= BLAKE3_OUT_LEN);
|
||||||
|
ctx.buildid->contents = {buf, buf + ctx.arg.build_id.size()};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BuildId::UUID: {
|
||||||
|
u8 buf[16];
|
||||||
|
get_random_bytes(buf, 16);
|
||||||
|
|
||||||
|
// Indicate that this is UUIDv4 as defined by RFC4122
|
||||||
|
buf[6] = (buf[6] & 0b0000'1111) | 0b0100'0000;
|
||||||
|
buf[8] = (buf[8] & 0b0011'1111) | 0b1000'0000;
|
||||||
|
ctx.buildid->contents = {buf, buf + 16};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Write Makefile-style dependency rules to a file specified by
|
// Write Makefile-style dependency rules to a file specified by
|
||||||
// --dependency-file. This is analogous to the compiler's -M flag.
|
// --dependency-file. This is analogous to the compiler's -M flag.
|
||||||
template <typename E>
|
template <typename E>
|
||||||
@ -3134,11 +3190,11 @@ template void apply_version_script(Context<E> &);
|
|||||||
template void parse_symbol_version(Context<E> &);
|
template void parse_symbol_version(Context<E> &);
|
||||||
template void compute_import_export(Context<E> &);
|
template void compute_import_export(Context<E> &);
|
||||||
template void compute_address_significance(Context<E> &);
|
template void compute_address_significance(Context<E> &);
|
||||||
template void clear_padding(Context<E> &);
|
|
||||||
template void compute_section_headers(Context<E> &);
|
template void compute_section_headers(Context<E> &);
|
||||||
template i64 set_osec_offsets(Context<E> &);
|
template i64 set_osec_offsets(Context<E> &);
|
||||||
template void fix_synthetic_symbols(Context<E> &);
|
template void fix_synthetic_symbols(Context<E> &);
|
||||||
template i64 compress_debug_sections(Context<E> &);
|
template i64 compress_debug_sections(Context<E> &);
|
||||||
|
template void compute_build_id(Context<E> &);
|
||||||
template void write_dependency_file(Context<E> &);
|
template void write_dependency_file(Context<E> &);
|
||||||
template void show_stats(Context<E> &);
|
template void show_stats(Context<E> &);
|
||||||
|
|
||||||
|
@ -176,7 +176,6 @@ void combine_objects(Context<E> &ctx) {
|
|||||||
ctx.buf = ctx.output_file->buf;
|
ctx.buf = ctx.output_file->buf;
|
||||||
|
|
||||||
copy_chunks(ctx);
|
copy_chunks(ctx);
|
||||||
clear_padding(ctx);
|
|
||||||
ctx.output_file->close(ctx);
|
ctx.output_file->close(ctx);
|
||||||
ctx.checkpoint();
|
ctx.checkpoint();
|
||||||
|
|
||||||
|
@ -14,10 +14,12 @@
|
|||||||
namespace mold::elf {
|
namespace mold::elf {
|
||||||
|
|
||||||
#ifdef MOLD_X86_64
|
#ifdef MOLD_X86_64
|
||||||
|
static int pipe_write_fd = -1;
|
||||||
|
|
||||||
// Exiting from a program with large memory usage is slow --
|
// Exiting from a program with large memory usage is slow --
|
||||||
// it may take a few hundred milliseconds. To hide the latency,
|
// it may take a few hundred milliseconds. To hide the latency,
|
||||||
// we fork a child and let it do the actual linking work.
|
// we fork a child and let it do the actual linking work.
|
||||||
std::function<void()> fork_child() {
|
void fork_child() {
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
if (pipe(pipefd) == -1) {
|
if (pipe(pipefd) == -1) {
|
||||||
perror("pipe");
|
perror("pipe");
|
||||||
@ -50,12 +52,16 @@ std::function<void()> fork_child() {
|
|||||||
|
|
||||||
// Child
|
// Child
|
||||||
close(pipefd[0]);
|
close(pipefd[0]);
|
||||||
|
pipe_write_fd = pipefd[1];
|
||||||
|
}
|
||||||
|
|
||||||
return [=] {
|
void notify_parent() {
|
||||||
char buf[] = {1};
|
if (pipe_write_fd == -1)
|
||||||
[[maybe_unused]] int n = write(pipefd[1], buf, 1);
|
return;
|
||||||
assert(n == 1);
|
|
||||||
};
|
char buf[] = {1};
|
||||||
|
[[maybe_unused]] int n = write(pipe_write_fd, buf, 1);
|
||||||
|
assert(n == 1);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user