ladybird/Userland/Libraries/LibELF/ELFBuild.cpp
Jesús (gsus) Lapastora 48a9d0ede8 LibELF: Add builders to help with creating ELF images
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.
2023-12-07 15:34:38 -07:00

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