mirror of
https://github.com/rui314/mold.git
synced 2024-12-27 10:23:41 +03:00
Implement .gnu.hash
This commit is contained in:
parent
404d5b1b88
commit
46f3402543
1
elf.h
1
elf.h
@ -142,6 +142,7 @@ static constexpr u32 DT_INIT_ARRAYSZ = 27;
|
||||
static constexpr u32 DT_FINI_ARRAYSZ = 28;
|
||||
static constexpr u32 DT_RUNPATH = 29;
|
||||
static constexpr u32 DT_FLAGS = 30;
|
||||
static constexpr u32 DT_GNU_HASH = 0x6ffffef5;
|
||||
static constexpr u32 DT_VERSYM = 0x6ffffff0;
|
||||
static constexpr u32 DT_RELACOUNT = 0x6ffffff9;
|
||||
static constexpr u32 DT_RELCOUNT = 0x6ffffffa;
|
||||
|
25
main.cc
25
main.cc
@ -431,7 +431,8 @@ static void fill_symbol_versions() {
|
||||
Timer t("fill_symbol_versions");
|
||||
|
||||
// Create a list of versioned symbols and sort by file and version.
|
||||
std::vector<Symbol *> syms = out::dynsym->symbols;
|
||||
std::vector<Symbol *> syms(out::dynsym->symbols.begin() + 1,
|
||||
out::dynsym->symbols.end());
|
||||
erase(syms, [](Symbol *sym){ return sym->ver_idx < 2; });
|
||||
|
||||
if (syms.empty())
|
||||
@ -443,7 +444,7 @@ static void fill_symbol_versions() {
|
||||
});
|
||||
|
||||
// Compute sizes of .gnu.version and .gnu.version_r sections.
|
||||
out::versym->contents.resize(out::dynsym->symbols.size() + 1, 1);
|
||||
out::versym->contents.resize(out::dynsym->symbols.size(), 1);
|
||||
out::versym->contents[0] = 0;
|
||||
|
||||
int sz = sizeof(ElfVerneed) + sizeof(ElfVernaux);
|
||||
@ -890,6 +891,19 @@ static Config parse_nonpositional_args(std::span<std::string_view> args,
|
||||
conf.library_paths.push_back(arg);
|
||||
} else if (read_arg(args, arg, "sysroot")) {
|
||||
conf.sysroot = arg;
|
||||
} else if (read_arg(args, arg, "hash-style")) {
|
||||
if (arg == "sysv") {
|
||||
conf.hash_style_sysv = true;
|
||||
conf.hash_style_gnu = false;
|
||||
} else if (arg == "gnu") {
|
||||
conf.hash_style_sysv = false;
|
||||
conf.hash_style_gnu = true;
|
||||
} else if (arg == "both") {
|
||||
conf.hash_style_sysv = true;
|
||||
conf.hash_style_gnu = true;
|
||||
} else {
|
||||
Fatal() << "invalid --hashstyle argument: " << arg;
|
||||
}
|
||||
} else if (read_flag(args, "trace")) {
|
||||
conf.trace = true;
|
||||
} else if (read_flag(args, "eh-frame-hdr")) {
|
||||
@ -937,7 +951,6 @@ static Config parse_nonpositional_args(std::span<std::string_view> args,
|
||||
} else if (read_flag(args, "preload")) {
|
||||
conf.preload = true;
|
||||
} else if (read_arg(args, arg, "z")) {
|
||||
} else if (read_arg(args, arg, "hash-style")) {
|
||||
} else if (read_arg(args, arg, "m")) {
|
||||
} else if (read_flag(args, "eh-frame-hdr")) {
|
||||
} else if (read_flag(args, "start-group")) {
|
||||
@ -1085,12 +1098,15 @@ int main(int argc, char **argv) {
|
||||
out::buildid = new BuildIdSection;
|
||||
if (config.eh_frame_hdr)
|
||||
out::eh_frame_hdr = new EhFrameHdrSection;
|
||||
if (config.hash_style_sysv)
|
||||
out::hash = new HashSection;
|
||||
if (config.hash_style_gnu)
|
||||
out::gnu_hash = new GnuHashSection;
|
||||
|
||||
if (!config.is_static) {
|
||||
out::interp = new InterpSection;
|
||||
out::dynamic = new DynamicSection;
|
||||
out::reldyn = new RelDynSection;
|
||||
out::hash = new HashSection;
|
||||
out::versym = new VersymSection;
|
||||
out::verneed = new VerneedSection;
|
||||
}
|
||||
@ -1107,6 +1123,7 @@ int main(int argc, char **argv) {
|
||||
out::chunks.push_back(out::symtab);
|
||||
out::chunks.push_back(out::strtab);
|
||||
out::chunks.push_back(out::hash);
|
||||
out::chunks.push_back(out::gnu_hash);
|
||||
out::chunks.push_back(out::eh_frame_hdr);
|
||||
out::chunks.push_back(out::eh_frame);
|
||||
out::chunks.push_back(out::copyrel);
|
||||
|
53
mold.h
53
mold.h
@ -60,12 +60,14 @@ struct Config {
|
||||
bool eh_frame_hdr = true;
|
||||
bool export_dynamic = false;
|
||||
bool fork = true;
|
||||
bool quick_exit = true;
|
||||
bool hash_style_gnu = false;
|
||||
bool hash_style_sysv = true;
|
||||
bool is_static = false;
|
||||
bool perf = false;
|
||||
bool pie = false;
|
||||
bool preload = false;
|
||||
bool print_map = false;
|
||||
bool quick_exit = true;
|
||||
bool relax = true;
|
||||
bool stat = false;
|
||||
bool strip_all = false;
|
||||
@ -612,8 +614,8 @@ public:
|
||||
void update_shdr() override;
|
||||
void copy_buf() override;
|
||||
|
||||
std::vector<Symbol *> symbols;
|
||||
std::vector<u32> name_indices;
|
||||
std::vector<Symbol *> symbols = {nullptr};
|
||||
std::vector<u32> name_indices = {(u32)-1};
|
||||
};
|
||||
|
||||
class HashSection : public OutputChunk {
|
||||
@ -628,9 +630,28 @@ public:
|
||||
|
||||
void update_shdr() override;
|
||||
void copy_buf() override;
|
||||
};
|
||||
|
||||
private:
|
||||
static u32 hash(std::string_view name);
|
||||
class GnuHashSection : public OutputChunk {
|
||||
public:
|
||||
GnuHashSection() : OutputChunk(SYNTHETIC) {
|
||||
name = ".gnu.hash";
|
||||
shdr.sh_type = SHT_GNU_HASH;
|
||||
shdr.sh_flags = SHF_ALLOC;
|
||||
shdr.sh_addralign = 8;
|
||||
}
|
||||
|
||||
void update_shdr() override;
|
||||
void copy_buf() override;
|
||||
|
||||
static constexpr int LOAD_FACTOR = 8;
|
||||
static constexpr int HEADER_SIZE = 16;
|
||||
static constexpr int BLOOM_SHIFT = 26;
|
||||
static constexpr int ELFCLASS_BITS = 64;
|
||||
|
||||
u32 bucket_size = -1;
|
||||
u32 symoffset = -1;
|
||||
u32 bloom_size = 1;
|
||||
};
|
||||
|
||||
class MergedSection : public OutputChunk {
|
||||
@ -1082,6 +1103,7 @@ inline DynamicSection *dynamic;
|
||||
inline StrtabSection *strtab;
|
||||
inline DynstrSection *dynstr;
|
||||
inline HashSection *hash;
|
||||
inline GnuHashSection *gnu_hash;
|
||||
inline ShstrtabSection *shstrtab;
|
||||
inline PltSection *plt;
|
||||
inline SymtabSection *symtab;
|
||||
@ -1120,6 +1142,20 @@ inline u64 align_to(u64 val, u64 align) {
|
||||
return (val + align - 1) & ~(align - 1);
|
||||
}
|
||||
|
||||
inline u64 next_power_of_two(u64 val) {
|
||||
if (!val)
|
||||
return 1;
|
||||
|
||||
val--;
|
||||
val |= val >> 1;
|
||||
val |= val >> 2;
|
||||
val |= val >> 4;
|
||||
val |= val >> 8;
|
||||
val |= val >> 16;
|
||||
val |= val >> 32;
|
||||
return val + 1;
|
||||
}
|
||||
|
||||
inline bool Symbol::is_absolute() const {
|
||||
return input_section == nullptr && file != out::internal_file;
|
||||
}
|
||||
@ -1199,6 +1235,13 @@ inline u32 elf_hash(std::string_view name) {
|
||||
return h;
|
||||
}
|
||||
|
||||
inline u32 gnu_hash(std::string_view name) {
|
||||
u32 h = 5381;
|
||||
for (u8 c : name)
|
||||
h = (h << 5) + h + c;
|
||||
return h;
|
||||
}
|
||||
|
||||
inline void write_string(u8 *buf, std::string_view str) {
|
||||
memcpy(buf, str.data(), str.size());
|
||||
buf[str.size()] = '\0';
|
||||
|
118
output_chunks.cc
118
output_chunks.cc
@ -329,7 +329,6 @@ static std::vector<u64> create_dynamic_section() {
|
||||
define(DT_SYMENT, sizeof(ElfSym));
|
||||
define(DT_STRTAB, out::dynstr->shdr.sh_addr);
|
||||
define(DT_STRSZ, out::dynstr->shdr.sh_size);
|
||||
define(DT_HASH, out::hash->shdr.sh_addr);
|
||||
define(DT_INIT_ARRAY, out::__init_array_start->value);
|
||||
define(DT_INIT_ARRAYSZ, out::__init_array_end->value - out::__init_array_start->value);
|
||||
define(DT_FINI_ARRAY, out::__fini_array_start->value);
|
||||
@ -339,6 +338,11 @@ static std::vector<u64> create_dynamic_section() {
|
||||
define(DT_VERNEEDNUM, out::verneed->shdr.sh_info);
|
||||
define(DT_DEBUG, 0);
|
||||
|
||||
if (out::hash)
|
||||
define(DT_HASH, out::hash->shdr.sh_addr);
|
||||
if (out::gnu_hash)
|
||||
define(DT_GNU_HASH, out::gnu_hash->shdr.sh_addr);
|
||||
|
||||
auto find = [](std::string_view name) -> OutputChunk * {
|
||||
for (OutputChunk *chunk : out::chunks)
|
||||
if (chunk->name == name)
|
||||
@ -589,31 +593,52 @@ void DynsymSection::add_symbol(Symbol *sym) {
|
||||
return;
|
||||
sym->dynsym_idx = -2;
|
||||
symbols.push_back(sym);
|
||||
name_indices.push_back(out::dynstr->add_string(sym->name));
|
||||
}
|
||||
|
||||
void DynsymSection::sort_symbols() {
|
||||
// In any ELF file, local symbols should precede global symbols.
|
||||
auto first_global = std::stable_partition(
|
||||
symbols.begin(), symbols.end(),
|
||||
symbols.begin() + 1, symbols.end(),
|
||||
[](Symbol *sym) { return sym->esym->st_bind == STB_LOCAL; });
|
||||
|
||||
shdr.sh_info = first_global - symbols.begin() + 1;
|
||||
// In any ELF file, the index of the first global symbols can be
|
||||
// found in the symtab's sh_info field.
|
||||
shdr.sh_info = first_global - symbols.begin();
|
||||
|
||||
int i = 1;
|
||||
for (Symbol *sym : symbols)
|
||||
sym->dynsym_idx = i++;
|
||||
// If we have .gnu.hash section, it imposes more constraints
|
||||
// on the order of symbols.
|
||||
if (out::gnu_hash) {
|
||||
auto first_defined = std::stable_partition(
|
||||
first_global, symbols.end(),
|
||||
[](Symbol *sym) { return sym->is_imported || sym->esym->is_undef(); });
|
||||
|
||||
int num_defined = symbols.end() - first_defined;
|
||||
out::gnu_hash->bucket_size = num_defined / out::gnu_hash->LOAD_FACTOR + 1;
|
||||
out::gnu_hash->symoffset = first_defined - symbols.begin();
|
||||
|
||||
std::stable_sort(first_defined, symbols.end(), [&](Symbol *a, Symbol *b) {
|
||||
u32 x = gnu_hash(a->name) % out::gnu_hash->bucket_size;
|
||||
u32 y = gnu_hash(b->name) % out::gnu_hash->bucket_size;
|
||||
return x < y;
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 1; i < symbols.size(); i++) {
|
||||
name_indices.push_back(out::dynstr->add_string(symbols[i]->name));
|
||||
symbols[i]->dynsym_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
void DynsymSection::update_shdr() {
|
||||
shdr.sh_link = out::dynstr->shndx;
|
||||
shdr.sh_size = sizeof(ElfSym) * (symbols.size() + 1);
|
||||
shdr.sh_size = sizeof(ElfSym) * symbols.size();
|
||||
}
|
||||
|
||||
void DynsymSection::copy_buf() {
|
||||
u8 *base = out::buf + shdr.sh_offset;
|
||||
memset(base, 0, sizeof(ElfSym));
|
||||
|
||||
for (int i = 0; i < symbols.size(); i++) {
|
||||
for (int i = 1; i < symbols.size(); i++) {
|
||||
Symbol &sym = *symbols[i];
|
||||
|
||||
ElfSym &esym = *(ElfSym *)(base + sym.dynsym_idx * sizeof(ElfSym));
|
||||
@ -643,7 +668,7 @@ void DynsymSection::copy_buf() {
|
||||
|
||||
void HashSection::update_shdr() {
|
||||
int header_size = 8;
|
||||
int num_slots = out::dynsym->symbols.size() + 1;
|
||||
int num_slots = out::dynsym->symbols.size();
|
||||
shdr.sh_size = header_size + num_slots * 8;
|
||||
shdr.sh_link = out::dynsym->shndx;
|
||||
}
|
||||
@ -652,17 +677,80 @@ void HashSection::copy_buf() {
|
||||
u8 *base = out::buf + shdr.sh_offset;
|
||||
memset(base, 0, shdr.sh_size);
|
||||
|
||||
int num_slots = out::dynsym->symbols.size() + 1;
|
||||
int num_slots = out::dynsym->symbols.size();
|
||||
u32 *hdr = (u32 *)base;
|
||||
u32 *buckets = (u32 *)(base + 8);
|
||||
u32 *chains = buckets + num_slots;
|
||||
|
||||
hdr[0] = hdr[1] = num_slots;
|
||||
|
||||
for (Symbol *sym : out::dynsym->symbols) {
|
||||
u32 i = elf_hash(sym->name) % num_slots;
|
||||
chains[sym->dynsym_idx] = buckets[i];
|
||||
buckets[i] = sym->dynsym_idx;
|
||||
for (int i = 1; i < out::dynsym->symbols.size(); i++) {
|
||||
Symbol *sym = out::dynsym->symbols[i];
|
||||
u32 idx = elf_hash(sym->name) % num_slots;
|
||||
chains[sym->dynsym_idx] = buckets[idx];
|
||||
buckets[idx] = sym->dynsym_idx;
|
||||
}
|
||||
}
|
||||
|
||||
void GnuHashSection::update_shdr() {
|
||||
shdr.sh_link = out::dynsym->shndx;
|
||||
|
||||
if (int num_symbols = out::dynsym->symbols.size() - symoffset) {
|
||||
// We allocate 12 bits for each symbol in the bloom filter.
|
||||
int num_bits = num_symbols * 12;
|
||||
bloom_size = next_power_of_two(num_bits / ELFCLASS_BITS);
|
||||
}
|
||||
|
||||
int num_symbols = out::dynsym->symbols.size() - symoffset;
|
||||
|
||||
shdr.sh_size = HEADER_SIZE; // Header
|
||||
shdr.sh_size += bloom_size * ELFCLASS_BITS / 8; // Bloom filter
|
||||
shdr.sh_size += bucket_size * 4; // Hash buckets
|
||||
shdr.sh_size += num_symbols * 4; // Hash values
|
||||
}
|
||||
|
||||
void GnuHashSection::copy_buf() {
|
||||
u8 *base = out::buf + shdr.sh_offset;
|
||||
memset(base, 0, shdr.sh_size);
|
||||
|
||||
*(u32 *)base = bucket_size;
|
||||
*(u32 *)(base + 4) = symoffset;
|
||||
*(u32 *)(base + 8) = bloom_size;
|
||||
*(u32 *)(base + 12) = BLOOM_SHIFT;
|
||||
|
||||
std::span<Symbol *> symbols = std::span(out::dynsym->symbols).subspan(symoffset);
|
||||
std::vector<u32> hashes(symbols.size());
|
||||
for (int i = 0; i < symbols.size(); i++)
|
||||
hashes[i] = gnu_hash(symbols[i]->name);
|
||||
|
||||
// Write a bloom filter
|
||||
u64 *bloom = (u64 *)(base + HEADER_SIZE);
|
||||
for (u32 hash : hashes) {
|
||||
u32 idx = (hash / 64) % bloom_size;
|
||||
bloom[idx] |= (u64)1 << (hash % 64);
|
||||
bloom[idx] |= (u64)1 << ((hash >> BLOOM_SHIFT) % 64);
|
||||
}
|
||||
|
||||
// Write hash bucket indices
|
||||
u32 *buckets = (u32 *)(bloom + bloom_size);
|
||||
for (int i = 1; i < hashes.size(); i++) {
|
||||
u32 idx = hashes[i] % bucket_size;
|
||||
if (!buckets[idx])
|
||||
buckets[idx] = i + symoffset;
|
||||
}
|
||||
|
||||
// Write a hash table
|
||||
u32 *table = buckets + bucket_size;
|
||||
for (int i = 0; i < symbols.size(); i++) {
|
||||
bool is_last = false;;
|
||||
if (i == symbols.size() - 1 ||
|
||||
(hashes[i] % bucket_size) != (hashes[i + 1] % bucket_size))
|
||||
is_last = true;
|
||||
|
||||
if (is_last)
|
||||
table[i] = hashes[i] | 1;
|
||||
else
|
||||
table[i] = hashes[i] & ~(u32)1;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user