1
1
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:
Rui Ueyama 2021-01-23 15:01:49 +09:00
parent 404d5b1b88
commit 46f3402543
4 changed files with 173 additions and 24 deletions

1
elf.h
View File

@ -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
View File

@ -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
View File

@ -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';

View File

@ -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;
}
}