Skip to content

Commit

Permalink
[vmar][elf] Make the elf loader use VMARs
Browse files Browse the repository at this point in the history
Change-Id: I1930a886b920f31de90d40d36b88840e1510f43b
  • Loading branch information
Todd Eisenberger committed Dec 15, 2016
1 parent 9d0f76f commit 6e63896
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 80 deletions.
24 changes: 11 additions & 13 deletions system/core/userboot/start.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,26 @@ static noreturn void do_shutdown(mx_handle_t log, mx_handle_t rroot) {
__builtin_trap();
}

static void load_child_process(mx_handle_t log, mx_handle_t proc_self,
static void load_child_process(mx_handle_t log, mx_handle_t vmar_self,
const struct options* o,
mx_handle_t bootfs_vmo, mx_handle_t vdso_vmo,
mx_handle_t proc, mx_handle_t to_child,
mx_vaddr_t* entry, mx_vaddr_t* vdso_base,
size_t* stack_size) {
// TODO(teisenbe): elf_load_bootfs needs to take in vmar_self rather
// than proc_self. Update this once we update the elf loader.
mx_handle_t proc, mx_handle_t vmar,
mx_handle_t to_child, mx_vaddr_t* entry,
mx_vaddr_t* vdso_base, size_t* stack_size) {

// Examine the bootfs image and find the requested file in it.
struct bootfs bootfs;
bootfs_mount(proc_self, log, bootfs_vmo, &bootfs);
bootfs_mount(vmar_self, log, bootfs_vmo, &bootfs);

// This will handle a PT_INTERP by doing a second lookup in bootfs.
*entry = elf_load_bootfs(log, proc_self, &bootfs, proc,
*entry = elf_load_bootfs(log, vmar_self, &bootfs, proc, vmar,
o->value[OPTION_FILENAME], to_child, stack_size);

// All done with bootfs!
bootfs_unmount(proc_self, log, &bootfs);
bootfs_unmount(vmar_self, log, &bootfs);

// Now load the vDSO into the child, so it has access to system calls.
*vdso_base = elf_load_vmo(log, proc_self, proc, vdso_vmo);
*vdso_base = elf_load_vmo(log, vmar_self, vmar, vdso_vmo);
}

// This is the main logic:
Expand Down Expand Up @@ -146,7 +144,7 @@ static noreturn void bootstrap(mx_handle_t log, mx_handle_t bootstrap_pipe) {

// Hang on to our own process handle. If we closed it, our process
// would be killed. Exiting will clean it up.
const mx_handle_t proc_self = *proc_handle_loc;
__UNUSED const mx_handle_t proc_self = *proc_handle_loc;
const mx_handle_t vmar_self = *vmar_root_handle_loc;

// Hang on to the resource root handle.
Expand Down Expand Up @@ -181,8 +179,8 @@ static noreturn void bootstrap(mx_handle_t log, mx_handle_t bootstrap_pipe) {

mx_vaddr_t entry, vdso_base;
size_t stack_size = MAGENTA_DEFAULT_STACK_SIZE;
load_child_process(log, proc_self, &o, bootfs_vmo, vdso_vmo, proc, to_child,
&entry, &vdso_base, &stack_size);
load_child_process(log, vmar_self, &o, bootfs_vmo, vdso_vmo, proc, vmar,
to_child, &entry, &vdso_base, &stack_size);

// Allocate the stack for the child.
stack_size = (stack_size + PAGE_SIZE - 1) & -PAGE_SIZE;
Expand Down
33 changes: 19 additions & 14 deletions system/core/userboot/userboot-elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@

#define INTERP_PREFIX "lib/"

// TODO(mcgrathr): Remove proc_self when elf_load_map_segments no longer uses it.
static mx_vaddr_t load(mx_handle_t log, mx_handle_t proc_self,
mx_handle_t proc, mx_handle_t vmo,
// TODO(mcgrathr): Remove vmar_self when elf_load_map_segments no longer uses it.
static mx_vaddr_t load(mx_handle_t log, mx_handle_t vmar_self,
mx_handle_t vmar, mx_handle_t vmo,
uintptr_t* interp_off, size_t* interp_len,
size_t* stack_size, bool close_vmo, bool return_entry) {
elf_load_header_t header;
Expand All @@ -46,7 +46,7 @@ static mx_vaddr_t load(mx_handle_t log, mx_handle_t proc_self,
}

mx_vaddr_t addr;
status = elf_load_map_segments(proc_self, proc, &header, phdrs, vmo,
status = elf_load_map_segments(vmar_self, vmar, &header, phdrs, vmo,
return_entry ? NULL : &addr,
return_entry ? &addr : NULL);
check(log, status, "elf_load_map_segments failed\n");
Expand All @@ -57,15 +57,16 @@ static mx_vaddr_t load(mx_handle_t log, mx_handle_t proc_self,
return addr;
}

mx_vaddr_t elf_load_vmo(mx_handle_t log, mx_handle_t proc_self,
mx_handle_t proc, mx_handle_t vmo) {
return load(log, proc_self, proc, vmo, NULL, NULL, NULL, false, false);
mx_vaddr_t elf_load_vmo(mx_handle_t log, mx_handle_t vmar_self,
mx_handle_t vmar, mx_handle_t vmo) {
return load(log, vmar_self, vmar, vmo, NULL, NULL, NULL, false, false);
}

enum loader_bootstrap_handle_index {
BOOTSTRAP_EXEC_VMO,
BOOTSTRAP_LOGGER,
BOOTSTRAP_PROC,
BOOTSTRAP_ROOT_VMAR,
BOOTSTRAP_HANDLES
};

Expand All @@ -79,7 +80,8 @@ struct loader_bootstrap_message {
};

static void stuff_loader_bootstrap(mx_handle_t log, mx_handle_t proc,
mx_handle_t to_child, mx_handle_t vmo) {
mx_handle_t vmar, mx_handle_t to_child,
mx_handle_t vmo) {
struct loader_bootstrap_message msg = {
.header = {
.protocol = MX_PROCARGS_PROTOCOL,
Expand All @@ -93,16 +95,19 @@ static void stuff_loader_bootstrap(mx_handle_t log, mx_handle_t proc,
[BOOTSTRAP_EXEC_VMO] = MX_HND_INFO(MX_HND_TYPE_EXEC_VMO, 0),
[BOOTSTRAP_LOGGER] = MX_HND_INFO(MX_HND_TYPE_MXIO_LOGGER, 0),
[BOOTSTRAP_PROC] = MX_HND_INFO(MX_HND_TYPE_PROC_SELF, 0),
[BOOTSTRAP_ROOT_VMAR] = MX_HND_INFO(MX_HND_TYPE_VMAR_ROOT, 0),
},
.env = LOADER_BOOTSTRAP_ENVIRON,
};
mx_handle_t handles[] = {
[BOOTSTRAP_EXEC_VMO] = vmo,
[BOOTSTRAP_LOGGER] = MX_HANDLE_INVALID,
[BOOTSTRAP_PROC] = MX_HANDLE_INVALID,
[BOOTSTRAP_ROOT_VMAR] = MX_HANDLE_INVALID,
};
mx_handle_duplicate(log, MX_RIGHT_SAME_RIGHTS, &handles[BOOTSTRAP_LOGGER]);
mx_handle_duplicate(proc, MX_RIGHT_SAME_RIGHTS, &handles[BOOTSTRAP_PROC]);
mx_handle_duplicate(vmar, MX_RIGHT_SAME_RIGHTS, &handles[BOOTSTRAP_ROOT_VMAR]);

mx_status_t status = mx_channel_write(
to_child, 0, &msg, sizeof(msg),
Expand All @@ -111,15 +116,15 @@ static void stuff_loader_bootstrap(mx_handle_t log, mx_handle_t proc,
"mx_channel_write of loader bootstrap message failed\n");
}

mx_vaddr_t elf_load_bootfs(mx_handle_t log, mx_handle_t proc_self,
mx_vaddr_t elf_load_bootfs(mx_handle_t log, mx_handle_t vmar_self,
struct bootfs *fs, mx_handle_t proc,
const char* filename, mx_handle_t to_child,
size_t* stack_size) {
mx_handle_t vmar, const char* filename,
mx_handle_t to_child, size_t* stack_size) {
mx_handle_t vmo = bootfs_open(log, fs, filename);

uintptr_t interp_off = 0;
size_t interp_len = 0;
mx_vaddr_t entry = load(log, proc_self, proc, vmo, &interp_off, &interp_len,
mx_vaddr_t entry = load(log, vmar_self, vmar, vmo, &interp_off, &interp_len,
stack_size, true, true);
if (interp_len > 0) {
char interp[sizeof(INTERP_PREFIX) + interp_len];
Expand All @@ -135,10 +140,10 @@ mx_vaddr_t elf_load_bootfs(mx_handle_t log, mx_handle_t proc_self,

print(log, filename, " has PT_INTERP \"", interp, "\"\n", NULL);

stuff_loader_bootstrap(log, proc, to_child, vmo);
stuff_loader_bootstrap(log, proc, vmar, to_child, vmo);

mx_handle_t interp_vmo = bootfs_open(log, fs, interp);
entry = load(log, proc_self, proc,
entry = load(log, vmar_self, vmar,
interp_vmo, NULL, NULL, NULL, true, true);
}
return entry;
Expand Down
10 changes: 5 additions & 5 deletions system/core/userboot/userboot-elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
struct bootfs;

// Returns the base address (p_vaddr bias).
mx_vaddr_t elf_load_vmo(mx_handle_t log, mx_handle_t proc_self,
mx_handle_t proc, mx_handle_t vmo);
mx_vaddr_t elf_load_vmo(mx_handle_t log, mx_handle_t vmar_self,
mx_handle_t vmar, mx_handle_t vmo);

// Returns the entry point address in the child, either to the named
// executable or to the PT_INTERP file loaded instead. If the main
Expand All @@ -23,9 +23,9 @@ mx_vaddr_t elf_load_vmo(mx_handle_t log, mx_handle_t proc_self,
// sent down the to_child pipe to prime the interpreter (presumably
// the dynamic linker) with the given log handle and a VMO for the
// main executable.
mx_vaddr_t elf_load_bootfs(mx_handle_t log, mx_handle_t proc_self,
mx_vaddr_t elf_load_bootfs(mx_handle_t log, mx_handle_t vmar_self,
struct bootfs *fs, mx_handle_t proc,
const char* filename, mx_handle_t to_child,
size_t* stack_size);
mx_handle_t vmar, const char* filename,
mx_handle_t to_child, size_t* stack_size);

#pragma GCC visibility pop
73 changes: 47 additions & 26 deletions system/ulib/elfload/elf-load.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <endian.h>
#include <limits.h>
#include <magenta/syscalls.h>
// TODO(teisenbe): This should go away when we clean up the VMAR
// stuff below to not need to call mx_object_get_info.
#include <magenta/syscalls/object.h>

#if BYTE_ORDER == LITTLE_ENDIAN
# define MY_ELFDATA ELFDATA2LSB
Expand Down Expand Up @@ -82,7 +85,7 @@ mx_status_t elf_load_read_phdrs(mx_handle_t vmo, elf_phdr_t phdrs[],
// this file and actual runtime addresses. (Usually the lowest p_vaddr in
// an ET_DYN file will be 0 and so the load bias is also the load base
// address, but ELF does not require that the lowest p_vaddr be 0.)
static mx_status_t choose_load_bias(mx_handle_t proc,
static mx_status_t choose_load_bias(mx_handle_t vmar,
const elf_load_header_t* header,
const elf_phdr_t phdrs[],
uintptr_t *bias) {
Expand Down Expand Up @@ -122,9 +125,9 @@ static mx_status_t choose_load_bias(mx_handle_t proc,
// in POSIX terms). But the kernel currently doesn't allow that, so do
// a read-only mapping.
uintptr_t base = 0;
mx_status_t status = mx_process_map_vm(proc, vmo, 0,
span, &base,
MX_VM_FLAG_PERM_READ);
mx_status_t status = mx_vmar_map(vmar, 0, vmo, 0,
span, MX_VM_FLAG_PERM_READ,
&base);
mx_handle_close(vmo);
if (status < 0)
return ERR_NO_MEMORY;
Expand All @@ -136,7 +139,7 @@ static mx_status_t choose_load_bias(mx_handle_t proc,
// starting on the actual PT_LOAD mappings. Since there is no chance
// of racing with another thread doing mappings in this process,
// there's no danger of "losing the reservation".
status = mx_process_unmap_vm(proc, base, 0);
status = mx_vmar_unmap(vmar, base, span);
if (status < 0)
return ERR_NO_MEMORY;

Expand All @@ -146,7 +149,7 @@ static mx_status_t choose_load_bias(mx_handle_t proc,

// TODO(mcgrathr): Temporary hack to avoid modifying the file VMO.
// This will go away when we have copy-on-write.
static mx_handle_t get_writable_vmo(mx_handle_t proc_self,
static mx_handle_t get_writable_vmo(mx_handle_t vmar_self,
mx_handle_t vmo, size_t data_size,
uintptr_t* file_start,
uintptr_t* file_end) {
Expand All @@ -155,16 +158,16 @@ static mx_handle_t get_writable_vmo(mx_handle_t proc_self,
if (status < 0)
return status;
uintptr_t window = 0;
status = mx_process_map_vm(proc_self, vmo,
*file_start, data_size, &window,
MX_VM_FLAG_PERM_READ);
status = mx_vmar_map(vmar_self, 0, vmo,
*file_start, data_size, MX_VM_FLAG_PERM_READ,
&window);
if (status < 0) {
mx_handle_close(copy_vmo);
return status;
}
size_t n;
status = mx_vmo_write(copy_vmo, (void*)window, 0, data_size, &n);
mx_process_unmap_vm(proc_self, window, 0);
mx_vmar_unmap(vmar_self, window, data_size);
if (status < 0) {
mx_handle_close(copy_vmo);
return status;
Expand All @@ -179,34 +182,52 @@ static mx_handle_t get_writable_vmo(mx_handle_t proc_self,
}

static mx_status_t finish_load_segment(
mx_handle_t proc, mx_handle_t vmo, const elf_phdr_t* ph,
mx_handle_t vmar, mx_handle_t vmo, const elf_phdr_t* ph,
uintptr_t start, size_t size,
uintptr_t file_start, uintptr_t file_end, size_t partial_page) {
const uint32_t flags = MX_VM_FLAG_FIXED |
const uint32_t flags = MX_VM_FLAG_SPECIFIC |
((ph->p_flags & PF_R) ? MX_VM_FLAG_PERM_READ : 0) |
((ph->p_flags & PF_W) ? MX_VM_FLAG_PERM_WRITE : 0) |
((ph->p_flags & PF_X) ? MX_VM_FLAG_PERM_EXECUTE : 0);

// Compute the VMAR base so we can calculate offsets to give to map.
// TODO(teisenbe): This will become unnecessary once this code switches
// to using sub-regions.
mx_info_vmar_t vmar_info;
mx_status_t status = mx_object_get_info(vmar, MX_INFO_VMAR,
&vmar_info, sizeof(vmar_info),
NULL, NULL);
if (status != NO_ERROR)
return status;

if (start < vmar_info.base)
return ERR_ELF_BAD_FORMAT;

size_t start_offset = start - vmar_info.base;

if (ph->p_filesz == ph->p_memsz)
// Straightforward segment, map all the whole pages from the file.
return mx_process_map_vm(proc, vmo, file_start, size, &start, flags);
return mx_vmar_map(vmar, start_offset, vmo, file_start, size, flags,
&start);

const size_t file_size = file_end - file_start;

// This segment has some bss, so things are more complicated.
// Only the leading portion is directly mapped in from the file.
if (file_size > 0) {
mx_status_t status = mx_process_map_vm(proc, vmo, file_start,
file_size, &start, flags);
mx_status_t status = mx_vmar_map(vmar, start_offset, vmo, file_start,
file_size, flags, &start);
if (status != NO_ERROR)
return status;
start += file_size;

start_offset = start - vmar_info.base;
start_offset += file_size;
size -= file_size;
}

// The rest of the segment will be backed by anonymous memory.
mx_handle_t bss_vmo;
mx_status_t status = mx_vmo_create(size, 0, &bss_vmo);
status = mx_vmo_create(size, 0, &bss_vmo);
if (status < 0)
return status;

Expand Down Expand Up @@ -236,14 +257,14 @@ static mx_status_t finish_load_segment(
}
}

status = mx_process_map_vm(proc, bss_vmo, 0, size, &start, flags);
status = mx_vmar_map(vmar, start_offset, bss_vmo, 0, size, flags, &start);
mx_handle_close(bss_vmo);

return status;
}

static mx_status_t load_segment(mx_handle_t proc_self,
mx_handle_t proc, mx_handle_t vmo,
static mx_status_t load_segment(mx_handle_t vmar_self,
mx_handle_t vmar, mx_handle_t vmo,
uintptr_t bias, const elf_phdr_t* ph) {
// The p_vaddr can start in the middle of a page, but the
// semantics are that all the whole pages containing the
Expand All @@ -270,32 +291,32 @@ static mx_status_t load_segment(mx_handle_t proc_self,

// With no writable data, it's the simple case.
if (!(ph->p_flags & PF_W) || data_size == 0)
return finish_load_segment(proc, vmo, ph, start, size,
return finish_load_segment(vmar, vmo, ph, start, size,
file_start, file_end, partial_page);

// For a writable segment, we need a writable VMO.
mx_handle_t writable_vmo = get_writable_vmo(proc_self, vmo, data_size,
mx_handle_t writable_vmo = get_writable_vmo(vmar_self, vmo, data_size,
&file_start, &file_end);
if (writable_vmo < 0)
return writable_vmo;
mx_status_t status = finish_load_segment(proc, writable_vmo, ph,
mx_status_t status = finish_load_segment(vmar, writable_vmo, ph,
start, size, file_start,
file_end, partial_page);
mx_handle_close(writable_vmo);
return status;
}

mx_status_t elf_load_map_segments(mx_handle_t proc_self, mx_handle_t proc,
mx_status_t elf_load_map_segments(mx_handle_t vmar_self, mx_handle_t vmar,
const elf_load_header_t* header,
const elf_phdr_t phdrs[],
mx_handle_t vmo,
mx_vaddr_t* base, mx_vaddr_t* entry) {
uintptr_t bias = 0;
mx_status_t status = choose_load_bias(proc, header, phdrs, &bias);
mx_status_t status = choose_load_bias(vmar, header, phdrs, &bias);

for (uint_fast16_t i = 0; status == NO_ERROR && i < header->e_phnum; ++i) {
if (phdrs[i].p_type == PT_LOAD)
status = load_segment(proc_self, proc, vmo, bias, &phdrs[i]);
status = load_segment(vmar_self, vmar, vmo, bias, &phdrs[i]);
}

if (status == NO_ERROR) {
Expand Down
6 changes: 3 additions & 3 deletions system/ulib/elfload/include/elfload/elfload.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ mx_status_t elf_load_read_phdrs(mx_handle_t vmo, elf_phdr_t* phdrs,
uintptr_t phoff, size_t phnum);

// Load the image into the process.
// TODO(mcgrathr): Remove the proc_self argument when we no longer use it.
mx_status_t elf_load_map_segments(mx_handle_t proc_self,
mx_handle_t proc,
// TODO(mcgrathr): Remove the vmar_self argument when we no longer use it.
mx_status_t elf_load_map_segments(mx_handle_t vmar_self,
mx_handle_t vmar,
const elf_load_header_t* header,
const elf_phdr_t* phdrs,
mx_handle_t vmo,
Expand Down
4 changes: 2 additions & 2 deletions system/ulib/launchpad/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ mx_status_t elf_load_get_interp(elf_load_info_t* info, mx_handle_t vmo,
return NO_ERROR;
}

mx_status_t elf_load_finish(mx_handle_t proc, elf_load_info_t* info,
mx_status_t elf_load_finish(mx_handle_t vmar, elf_load_info_t* info,
mx_handle_t vmo,
mx_vaddr_t* base, mx_vaddr_t* entry) {
return elf_load_map_segments(mx_process_self(), proc,
return elf_load_map_segments(mx_process_self(), vmar,
&info->header, info->phdrs, vmo, base, entry);
}

Expand Down
Loading

0 comments on commit 6e63896

Please sign in to comment.