1
1
mirror of https://github.com/rui314/mold.git synced 2024-10-04 16:48:04 +03:00

[ELF] Fix section file offsets

Previously, if a section has a very large alignment requirement,
that section and the following sections may get wrong file offsets.

Fixes https://github.com/rui314/mold/issues/405
This commit is contained in:
Rui Ueyama 2022-04-15 07:23:50 +08:00
parent 0daf6233fb
commit 100922b650
4 changed files with 108 additions and 41 deletions

View File

@ -336,9 +336,6 @@ void report_undef(Context<E> &ctx, InputFile<E> &file, Symbol<E> &sym);
template <typename E>
bool is_relro(Context<E> &ctx, Chunk<E> *chunk);
template <typename E>
bool separate_page(Context<E> &ctx, Chunk<E> *a, Chunk<E> *b);
// Chunk represents a contiguous region in an output file.
template <typename E>
class Chunk {
@ -353,8 +350,9 @@ public:
virtual void update_shdr(Context<E> &ctx) {}
std::string_view name;
i64 shndx = 0;
ElfShdr<E> shdr = {};
i64 shndx = 0;
i64 extra_addralign = 1;
protected:
Chunk() { shdr.sh_addralign = 1; }
@ -968,9 +966,6 @@ private:
bool is_c_identifier(std::string_view name);
template <typename E>
std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx);
//
// input-files.cc
//

View File

@ -166,7 +166,7 @@ bool is_relro(Context<E> &ctx, Chunk<E> *chunk) {
}
template <typename E>
bool separate_page(Context<E> &ctx, Chunk<E> *x, Chunk<E> *y) {
static bool separate_page(Context<E> &ctx, Chunk<E> *x, Chunk<E> *y) {
if (ctx.arg.z_relro && is_relro(ctx, x) != is_relro(ctx, y))
return true;
@ -182,7 +182,7 @@ bool separate_page(Context<E> &ctx, Chunk<E> *x, Chunk<E> *y) {
}
template <typename E>
std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
std::vector<ElfPhdr<E>> vec;
auto define = [&](u64 type, u64 flags, i64 min_align, auto &chunk) {
@ -222,6 +222,10 @@ std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
return (shdr.sh_type == SHT_NOTE) && (shdr.sh_flags & SHF_ALLOC);
};
// Clear previous results so that this function is idempotent.
for (Chunk<E> *chunk : ctx.chunks)
chunk->extra_addralign = 1;
// Create a PT_PHDR for the program header itself.
if (ctx.phdr)
define(PT_PHDR, PF_R, E::word_size, ctx.phdr);
@ -262,14 +266,14 @@ std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
if (!is_bss(first))
while (i < end && !is_bss(chunks[i]) &&
to_phdr_flags(ctx, chunks[i]) == flags &&
chunks[i]->shdr.sh_addr - first->shdr.sh_addr ==
chunks[i]->shdr.sh_offset - first->shdr.sh_offset)
to_phdr_flags(ctx, chunks[i]) == flags)
append(chunks[i++]);
while (i < end && is_bss(chunks[i]) &&
to_phdr_flags(ctx, chunks[i]) == flags)
append(chunks[i++]);
first->extra_addralign = vec.back().p_align;
}
}
@ -312,15 +316,17 @@ std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
continue;
define(PT_GNU_RELRO, PF_R, 1, ctx.chunks[i]);
ctx.chunks[i]->extra_addralign = ctx.page_size;
i++;
while (i < ctx.chunks.size() && is_relro(ctx, ctx.chunks[i]))
append(ctx.chunks[i++]);
// RELRO works on page granularity, so align it up to the next
// page boundary.
assert(i == ctx.chunks.size() ||
ctx.chunks[i]->shdr.sh_addr % ctx.page_size == 0);
// RELRO works on page granularity, so align both ends to
// the page size.
vec.back().p_memsz = align_to(vec.back().p_memsz, ctx.page_size);
if (i < ctx.chunks.size())
ctx.chunks[i]->extra_addralign = ctx.page_size;
}
}
@ -2771,9 +2777,7 @@ void RelocSection<E>::copy_buf(Context<E> &ctx) {
template class GnuCompressedSection<E>; \
template class RelocSection<E>; \
template i64 BuildId::size(Context<E> &) const; \
template bool is_relro(Context<E> &, Chunk<E> *); \
template bool separate_page(Context<E> &, Chunk<E> *, Chunk<E> *); \
template std::vector<ElfPhdr<E>> create_phdr(Context<E> &)
template bool is_relro(Context<E> &, Chunk<E> *);
INSTANTIATE(X86_64);
INSTANTIATE(I386);

View File

@ -1267,15 +1267,6 @@ i64 get_section_rank(Context<E> &ctx, Chunk<E> *chunk) {
(!relro << 16) | (is_bss << 15);
}
// Returns the smallest number n such that
// val <= n and n % align == skew % align.
inline u64 align_with_skew(u64 val, u64 align, u64 skew) {
skew = skew % align;
u64 n = align_to(val + align - skew, align) - align + skew;
assert(val <= n && n < val + align && n % align == skew % align);
return n;
}
template <typename E>
static bool is_tbss(Chunk<E> *chunk) {
return (chunk->shdr.sh_type == SHT_NOBITS) && (chunk->shdr.sh_flags & SHF_TLS);
@ -1286,6 +1277,10 @@ template <typename E>
i64 do_set_osec_offsets(Context<E> &ctx) {
std::vector<Chunk<E> *> &chunks = ctx.chunks;
auto alignment = [](Chunk<E> *chunk) {
return std::max<i64>(chunk->extra_addralign, chunk->shdr.sh_addralign);
};
// Assign virtual addresses
u64 addr = ctx.arg.image_base;
for (i64 i = 0; i < chunks.size(); i++) {
@ -1296,15 +1291,12 @@ i64 do_set_osec_offsets(Context<E> &ctx) {
it != ctx.arg.section_start.end())
addr = it->second;
if (i > 0 && separate_page(ctx, chunks[i - 1], chunks[i]))
addr = align_to(addr, ctx.page_size);
if (is_tbss(chunks[i])) {
chunks[i]->shdr.sh_addr = addr;
continue;
}
addr = align_to(addr, chunks[i]->shdr.sh_addralign);
addr = align_to(addr, alignment(chunks[i]));
chunks[i]->shdr.sh_addr = addr;
addr += chunks[i]->shdr.sh_size;
}
@ -1321,7 +1313,7 @@ i64 do_set_osec_offsets(Context<E> &ctx) {
if (is_tbss(chunks[i])) {
u64 addr = chunks[i]->shdr.sh_addr;
for (; i < chunks.size() && is_tbss(chunks[i]); i++) {
addr = align_to(addr, chunks[i]->shdr.sh_addralign);
addr = align_to(addr, alignment(chunks[i]));
chunks[i]->shdr.sh_addr = addr;
addr += chunks[i]->shdr.sh_size;
}
@ -1330,16 +1322,42 @@ i64 do_set_osec_offsets(Context<E> &ctx) {
}
}
// Assign file offsets
// Assign file offsets to memory-allocated sections.
u64 fileoff = 0;
for (Chunk<E> *chunk : chunks) {
if (chunk->shdr.sh_type == SHT_NOBITS) {
chunk->shdr.sh_offset = fileoff;
} else {
fileoff = align_with_skew(fileoff, ctx.page_size, chunk->shdr.sh_addr);
chunk->shdr.sh_offset = fileoff;
fileoff += chunk->shdr.sh_size;
i64 i = 0;
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC)) {
Chunk<E> &first = *chunks[i];
assert(first.shdr.sh_type != SHT_NOBITS);
fileoff = align_to(fileoff, alignment(&first));
u64 end = fileoff;
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
chunks[i]->shdr.sh_type != SHT_NOBITS) {
// The addresses may not increase monotonically if a user uses
// --start-sections.
if (chunks[i]->shdr.sh_addr < first.shdr.sh_addr)
break;
chunks[i]->shdr.sh_offset =
fileoff + chunks[i]->shdr.sh_addr - first.shdr.sh_addr;
end = chunks[i]->shdr.sh_offset + chunks[i]->shdr.sh_size;
i++;
}
fileoff = end;
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
chunks[i]->shdr.sh_type == SHT_NOBITS)
i++;
}
// Assign file offsets to non-memory-allocated sections.
for (; i < chunks.size(); i++) {
fileoff = align_to(fileoff, chunks[i]->shdr.sh_addralign);
chunks[i]->shdr.sh_offset = fileoff;
fileoff += chunks[i]->shdr.sh_size;
}
return fileoff;
}

50
test/elf/large-alignment-dso.sh Executable file
View File

@ -0,0 +1,50 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
[ $MACHINE = i386 -o $MACHINE = arm ] && { echo skipped; exit; }
cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections -fPIC
#include <stdio.h>
#include <stdint.h>
void hello() __attribute__((aligned(32768), section(".hello")));
void world() __attribute__((aligned(32768), section(".world")));
void hello() {
printf("Hello");
}
void world() {
printf(" world");
}
void greet() {
hello();
world();
}
EOF
$CC -B. -o $t/b.so $t/a.o -shared
cat <<EOF | $CC -o $t/c.o -c -xc -
void greet();
int main() { greet(); }
EOF
$CC -B. -o $t/exe $t/c.o $t/b.so
$QEMU $t/exe | grep -q 'Hello world'
echo OK