
Introduces new builders, mainly `SectionTable` and `StringTable`, and a final `build_elf_image` to merge everything into a single memory image. Each of the builders are fully detached from one another, although StringTable provides an extra API to remove steps when using it with a SectionTable. This automates the part of figuring out and properly writing offsets to headers, and making sure all required data is properly copied and referenced in the final image.
136 lines
4.7 KiB
C++
136 lines
4.7 KiB
C++
/*
|
|
* Copyright (c) 2023, Jesús Lapastora <cyber.gsuscode@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibELF/ELFBuild.h>
|
|
namespace ELF {
|
|
|
|
SectionTable::Index StringTable::emit_into_builder(u32 name_index, SectionTable& builder) const noexcept
|
|
{
|
|
return builder.append(emit_section(name_index));
|
|
}
|
|
|
|
Section StringTable::emit_section(u32 name_index) const noexcept
|
|
{
|
|
Elf64_Shdr header {};
|
|
header.sh_name = name_index;
|
|
header.sh_type = SHT_STRTAB;
|
|
header.sh_flags = 0;
|
|
header.sh_addr = 0;
|
|
header.sh_link = 0;
|
|
header.sh_info = 0;
|
|
header.sh_entsize = 0;
|
|
header.sh_addralign = 0;
|
|
return Section(m_data.span(), header);
|
|
}
|
|
|
|
u32 StringTable::insert(StringView str) noexcept
|
|
{
|
|
// The offsets for sh_name and st_name are 32-bit unsigned integers, so it
|
|
// won't make sense to address a string table bigger than what u32 can
|
|
// provide.
|
|
VERIFY(m_data.size() < NumericLimits<u32>::max());
|
|
auto const offset = static_cast<u32>(m_data.size());
|
|
|
|
auto const final_size = m_data.size() + str.length() + 1;
|
|
VERIFY(final_size < NumericLimits<u32>::max());
|
|
|
|
m_data.ensure_capacity(m_data.size() + str.length() + 1);
|
|
|
|
for (auto ch : str) {
|
|
VERIFY(ch != 0);
|
|
m_data.unchecked_append(ch);
|
|
}
|
|
m_data.append(0);
|
|
return offset;
|
|
}
|
|
|
|
FixedArray<u8> build_elf_image(u64 shstrndx, Elf64_Quarter image_type, ReadonlySpan<Section> sections)
|
|
{
|
|
Checked<u64> final_image_size = sizeof(Elf64_Ehdr);
|
|
Vector<u64> section_offsets;
|
|
section_offsets.ensure_capacity(sections.size());
|
|
|
|
auto const sections_begin = final_image_size.value_unchecked();
|
|
final_image_size += sizeof(Elf64_Shdr) * sections.size();
|
|
|
|
for (auto const& section : sections) {
|
|
auto const offset = final_image_size.value();
|
|
section_offsets.unchecked_append(offset);
|
|
if (section.data.has_value()) {
|
|
final_image_size += section.data.value().size();
|
|
}
|
|
}
|
|
|
|
auto image = MUST(FixedArray<u8>::create(final_image_size.value()));
|
|
|
|
{
|
|
auto section_headers = Span<Elf64_Shdr> {
|
|
reinterpret_cast<Elf64_Shdr*>(image.span().offset_pointer(sections_begin)),
|
|
sections.size(),
|
|
};
|
|
for (size_t i = 0; i < sections.size(); ++i) {
|
|
section_headers[i] = sections[i].header;
|
|
section_headers[i].sh_offset = section_offsets[i];
|
|
if (sections[i].data.has_value()) {
|
|
auto const data = sections[i].data.value();
|
|
auto const data_in_elf = image.span().slice(section_offsets[i], data.size());
|
|
data.copy_to(data_in_elf);
|
|
section_headers[i].sh_size = data.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
auto* const final_elf_hdr = reinterpret_cast<Elf64_Ehdr*>(image.data());
|
|
final_elf_hdr->e_ident[EI_MAG0] = 0x7f;
|
|
final_elf_hdr->e_ident[EI_MAG1] = 'E';
|
|
final_elf_hdr->e_ident[EI_MAG2] = 'L';
|
|
final_elf_hdr->e_ident[EI_MAG3] = 'F';
|
|
final_elf_hdr->e_ident[EI_CLASS] = ELFCLASS64;
|
|
// FIXME: This is platform-dependent. Any big-endian host will write the
|
|
// data in MSB format, so the EI_DATA field should be set to
|
|
// ELFDATA2MSB.
|
|
final_elf_hdr->e_ident[EI_DATA] = ELFDATA2LSB;
|
|
final_elf_hdr->e_ident[EI_VERSION] = EV_CURRENT;
|
|
// FIXME: This is platform-dependent. The host must set the OSABI to the
|
|
// one of the image target.
|
|
final_elf_hdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
|
final_elf_hdr->e_ident[EI_ABIVERSION] = 0;
|
|
auto padding = Bytes {
|
|
&final_elf_hdr->e_ident[EI_PAD],
|
|
EI_NIDENT - EI_PAD,
|
|
};
|
|
padding.fill(0);
|
|
|
|
final_elf_hdr->e_type = image_type;
|
|
// FIXME: This is platform-dependent. This must be set to the host
|
|
// architecture.
|
|
final_elf_hdr->e_machine = EM_AMD64;
|
|
final_elf_hdr->e_version = EV_CURRENT;
|
|
|
|
// Currently segments aren't supported, hence no program headers.
|
|
// FIXME: Update program header info on ELF header when adding segment
|
|
// information.
|
|
final_elf_hdr->e_phoff = 0;
|
|
final_elf_hdr->e_phnum = 0;
|
|
final_elf_hdr->e_phentsize = 0;
|
|
|
|
final_elf_hdr->e_shoff = sections_begin;
|
|
final_elf_hdr->e_shnum = sections.size();
|
|
final_elf_hdr->e_shentsize = sizeof(Section::header);
|
|
|
|
// FIXME: This is platform-dependent. The flags field should be in sync
|
|
// with the architecture flags assumed in the code sections, otherwise
|
|
// instructions may be misinterpreted.
|
|
final_elf_hdr->e_flags = 0;
|
|
|
|
final_elf_hdr->e_ehsize = sizeof(*final_elf_hdr);
|
|
final_elf_hdr->e_shstrndx = shstrndx;
|
|
}
|
|
|
|
return image;
|
|
}
|
|
};
|