Skip to content

Commit

Permalink
tools: simplify lone segment creator tool
Browse files Browse the repository at this point in the history
There's a better and simpler way to implement this feature:
take advantage of the fact that the PT_PHDR program header is optional.
Instead of moving the whole program header table to the end of the file
and then wrangling pointers, I can just overwrite the PT_PHDR entry.

There seems to be no downsides to this. Since the kernel passes programs
pointers to their program header tables, as well as the entry count and
even the size of each entry, the PT_PHDR segment does not appear to be
necessary at all for accessing the program header table.

This enables even simpler integration with mold and its new features:
look for spare PT_NULL segments to use for the lone segment before using
the PT_PHDR segment. This allows keeping the PT_PHDR segment and avoids
disturbing the linker's output.

The ELF documentation says, emphasis mine:

> PT_PHDR
>
>     The array element, _if present_ ...
>     _If it is present_ ...

So it might not be present and is not required to be present.

>     Moreover, it _may_ occur only if the program header table
>     is part of the memory image of the program.

A lack of the PT_PHDR segment does not imply the program header table
is not part of the program's memory image. It could still be covered by
a PT_LOAD segment and be in memory anyway. All linkers I've tried
generate PT_LOAD segments that cover the ELF header and the program
headers and so accessing the table at runtime should work.

GitHub-Issue: rui314/mold#1148
  • Loading branch information
matheusmoreira committed Nov 17, 2023
1 parent 547b7b8 commit 1724fec
Showing 1 changed file with 76 additions and 167 deletions.
243 changes: 76 additions & 167 deletions source/tools/create-lone-segment.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,191 +97,105 @@ static struct lone_elf_program_header_table find_program_header_table(struct lon
}
}

static size_t move_program_header_table(struct lone_bytes elf, size_t elf_size, struct lone_elf_program_header_table *phdrs)
static void set_lone_entry(struct lone_bytes elf, unsigned char elf_class, struct lone_elf_program_header_table phdrs)
{
size_t table_size = phdrs->entry_size * phdrs->entry_count, new_size = elf_size + table_size;
void *new_address;

if (elf.count < new_size) { /* Not enough space in buffer */ linux_exit(7); }

new_address = elf.pointer + elf_size;
lone_memory_move(phdrs->address, new_address, table_size);
phdrs->address = new_address;

return new_size;
}

static size_t append_program_header_table_entry(struct lone_bytes elf, size_t elf_size, unsigned char elf_class, struct lone_elf_program_header_table *phdrs)
{
size_t new_size = elf_size + phdrs->entry_size;
unsigned char *entry = phdrs->address;
Elf32_Phdr *phdr32;
Elf64_Phdr *phdr64;

if (elf.count < new_size) { /* Not enough space in buffer */ linux_exit(8); }

entry += phdrs->entry_size * phdrs->entry_count++;
unsigned char *table = phdrs.address;
size_t base, address, size, alignment, flags, i;
Elf32_Phdr *pt_phdr32 = NULL, *phdr32;
Elf64_Phdr *pt_phdr64 = NULL, *phdr64;

switch (elf_class) {
case ELFCLASS64:
phdr64 = (Elf64_Phdr *) entry;
phdr64->p_type = PT_NULL;
phdr64->p_flags = PF_R;
phdr64->p_offset = 0;
phdr64->p_vaddr = 0;
phdr64->p_paddr = 0;
phdr64->p_filesz = 0;
phdr64->p_memsz = 0;
phdr64->p_align = 1;
break;
case ELFCLASS32:
phdr32 = (Elf32_Phdr *) entry;
phdr32->p_type = PT_NULL;
phdr32->p_flags = PF_R;
phdr32->p_offset = 0;
phdr32->p_vaddr = 0;
phdr32->p_paddr = 0;
phdr32->p_filesz = 0;
phdr32->p_memsz = 0;
phdr32->p_align = 1;
break;
default:
/* Invalid ELF class but somehow made it here? */ linux_exit(6);
}
for (i = 0; i < phdrs.entry_count; ++i) {
phdr64 = ((Elf64_Phdr *) table) + i;

return new_size;
}
switch (phdr64->p_type) {
case PT_NULL:
// linker allocated spare program header
set_lone_entry_64:
phdr64->p_type = PT_LONE;

static size_t adjust_elf_header(struct lone_bytes elf, unsigned char elf_class, struct lone_elf_program_header_table phdrs)
{
unsigned char *address = phdrs.address;
Elf32_Ehdr *elf32;
Elf64_Ehdr *elf64;
phdr64->p_filesz = phdr64->p_memsz = 0;
phdr64->p_vaddr = phdr64->p_paddr = phdr64->p_vaddr - phdr64->p_offset;
phdr64->p_offset = 0;
phdr64->p_align = 1;
phdr64->p_flags = PF_R;

switch (elf_class) {
case ELFCLASS64:
elf64 = (Elf64_Ehdr *) elf.pointer;
elf64->e_phoff = address - elf.pointer;
elf64->e_phentsize = phdrs.entry_size;
elf64->e_phnum = phdrs.entry_count;
return elf64->e_phoff;
case ELFCLASS32:
elf32 = (Elf32_Ehdr *) elf.pointer;
elf32->e_phoff = address - elf.pointer;
elf32->e_phentsize = phdrs.entry_size;
elf32->e_phnum = phdrs.entry_count;
return elf32->e_phoff;
default:
/* Invalid ELF class but somehow made it here? */ linux_exit(6);
}
}
goto lone_entry_set;

static size_t adjust_and_cover_phdr_entry(struct lone_bytes elf, unsigned char elf_class, struct lone_elf_program_header_table phdrs)
{
unsigned char *table = phdrs.address;
size_t table_offset = table - elf.pointer;
size_t size = phdrs.entry_size * phdrs.entry_count;
Elf32_Phdr *phdr32;
Elf64_Phdr *phdr64;
size_t base, address, alignment, flags, i;
case PT_PHDR:
/* The PT_PHDR entry is optional
and will become a PT_LONE entry
if no linker spares are provided */

pt_phdr64 = phdr64;

switch (elf_class) {
case ELFCLASS64:
for (i = 0; i < phdrs.entry_count; ++i) {
phdr64 = ((Elf64_Phdr *) table) + i;
if (phdr64->p_type == PT_PHDR) {
base = phdr64->p_vaddr - phdr64->p_offset;
address = base + table_offset;
alignment = phdr64->p_align;
flags = phdr64->p_flags;

phdr64->p_offset = table_offset;
phdr64->p_vaddr = phdr64->p_paddr = address;
phdr64->p_filesz = phdr64->p_memsz = size;
break;

default:
continue;
}
}
for (/* i */; i < phdrs.entry_count; ++i) {
phdr64 = ((Elf64_Phdr *) table) + i;
if (phdr64->p_type == PT_NULL) {
phdr64->p_type = PT_LOAD;

phdr64->p_offset = table_offset;
phdr64->p_vaddr = phdr64->p_paddr = address;
phdr64->p_filesz = phdr64->p_memsz = size;
phdr64->p_align = alignment;
phdr64->p_flags = flags;
break;
}

// no spare segments were provided by the linker...
if (pt_phdr64) {
// ... but there's a PT_PHDR segment
phdr64 = pt_phdr64;
goto set_lone_entry_64;
} else {
// ... and there's no PT_PHDR segment
linux_exit(7);
}

break;
case ELFCLASS32:
for (i = 0; i < phdrs.entry_count; ++i) {
phdr32 = ((Elf32_Phdr *) table) + i;
if (phdr32->p_type == PT_PHDR) {
base = phdr32->p_vaddr - phdr32->p_offset;
address = base + table_offset;
alignment = phdr32->p_align;
flags = phdr32->p_flags;

phdr32->p_offset = table_offset;
phdr32->p_vaddr = phdr32->p_paddr = address;
phdr32->p_filesz = phdr32->p_memsz = size;
break;
}
}
for (/* i */; i < phdrs.entry_count; ++i) {
phdr64 = ((Elf32_Phdr *) table) + i;
if (phdr32->p_type == PT_NULL) {
phdr32->p_type = PT_LOAD;

phdr32->p_offset = table_offset;
phdr32->p_vaddr = phdr32->p_paddr = address;
phdr32->p_filesz = phdr32->p_memsz = size;
phdr32->p_align = alignment;
phdr32->p_flags = flags;
break;
}
}
break;
default:
/* Invalid ELF class but somehow made it here? */ linux_exit(6);
}

return base;
}
switch (phdr32->p_type) {
case PT_NULL:
// linker allocated spare program header
set_lone_entry_32:
phdr32->p_type = PT_LONE;

static void set_lone_entry(struct lone_bytes elf, unsigned char elf_class, struct lone_elf_program_header_table phdrs, size_t base)
{
unsigned char *table = phdrs.address;
Elf32_Phdr *phdr32;
Elf64_Phdr *phdr64;
size_t i;
phdr32->p_filesz = phdr32->p_memsz = 0;
phdr32->p_vaddr = phdr32->p_paddr = phdr32->p_vaddr - phdr32->p_offset;
phdr32->p_offset = 0;
phdr32->p_align = 1;
phdr32->p_flags = PF_R;

goto lone_entry_set;

case PT_PHDR:
/* The PT_PHDR entry is optional
and will become a PT_LONE entry
if no linker spares are provided */

pt_phdr32 = phdr32;

switch (elf_class) {
case ELFCLASS64:
for (i = 0; i < phdrs.entry_count; ++i) {
phdr64 = ((Elf64_Phdr *) table) + i;
if (phdr64->p_type == PT_NULL) {
phdr64->p_type = PT_LONE;
phdr64->p_vaddr = phdr64->p_paddr = base;
break;

default:
continue;
}
}
break;
case ELFCLASS32:
for (i = 0; i < phdrs.entry_count; ++i) {
phdr32 = ((Elf32_Phdr *) table) + i;
if (phdr32->p_type == PT_NULL) {
phdr32->p_type = PT_LONE;
phdr32->p_vaddr = phdr32->p_paddr = base;
break;
}

// no spare segments were provided by the linker...
if (pt_phdr32) {
// ... but there's a PT_PHDR segment
phdr32 = pt_phdr32;
goto set_lone_entry_32;
} else {
// ... and there's no PT_PHDR segment
linux_exit(7);
}
break;
default:
/* Invalid ELF class but somehow made it here? */ linux_exit(6);
}

lone_entry_set:
return;
}

static size_t write_elf(char *path, struct lone_bytes buffer, size_t elf_size)
Expand All @@ -290,13 +204,13 @@ static size_t write_elf(char *path, struct lone_bytes buffer, size_t elf_size)
int fd;

fd = linux_openat(AT_FDCWD, path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC);
if (fd < 0) { /* error opening output file */ linux_exit(9); }
if (fd < 0) { /* error opening output file */ linux_exit(8); }

bytes_written = linux_write(fd, buffer.pointer, elf_size);
if (bytes_written < 0) { /* error writing output file */ linux_exit(10); }
if (bytes_written < 0) { /* error writing output file */ linux_exit(9); }

fd = linux_close(fd);
if (fd < 0) { /* error closing output file */ linux_exit(11); }
if (fd < 0) { /* error closing output file */ linux_exit(10); }

return bytes_written;
}
Expand All @@ -307,18 +221,13 @@ long lone(int argc, char **argv, char **envp, struct auxiliary_vector *auxvec)
struct lone_bytes elf = { sizeof(buffer), buffer };
struct lone_elf_program_header_table phdrs;
unsigned char elf_class;
size_t elf_size, new_offset, base;
size_t elf_size;

check_arguments(argc, argv);
elf_size = read_elf(argv[1], elf);
elf_class = validate_elf_header(elf);
phdrs = find_program_header_table(elf, elf_class);
elf_size = move_program_header_table(elf, elf_size, &phdrs);
elf_size = append_program_header_table_entry(elf, elf_size, elf_class, &phdrs);
elf_size = append_program_header_table_entry(elf, elf_size, elf_class, &phdrs);
new_offset = adjust_elf_header(elf, elf_class, phdrs);
base = adjust_and_cover_phdr_entry(elf, elf_class, phdrs);
set_lone_entry(elf, elf_class, phdrs, base);
set_lone_entry(elf, elf_class, phdrs);
write_elf(argv[2], elf, elf_size);

return 0;
Expand Down

0 comments on commit 1724fec

Please sign in to comment.