Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/link_elf_obj.c
Show First 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
#include <contrib/zlib/zlib.h> | #include <contrib/zlib/zlib.h> | ||||
#endif | #endif | ||||
#include "linker_if.h" | #include "linker_if.h" | ||||
typedef struct { | typedef struct { | ||||
void *addr; | void *addr; | ||||
Elf_Off size; | Elf_Off size; | ||||
int flags; | int flags; /* Section flags. */ | ||||
int sec; /* Original section */ | int sec; /* Original section number. */ | ||||
char *name; | char *name; | ||||
} Elf_progent; | } Elf_progent; | ||||
typedef struct { | typedef struct { | ||||
Elf_Rel *rel; | Elf_Rel *rel; | ||||
int nrel; | int nrel; | ||||
int sec; | int sec; | ||||
} Elf_relent; | } Elf_relent; | ||||
▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | |||||
link_elf_init(void *arg) | link_elf_init(void *arg) | ||||
{ | { | ||||
linker_add_class(&link_elf_class); | linker_add_class(&link_elf_class); | ||||
} | } | ||||
SYSINIT(link_elf_obj, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, NULL); | SYSINIT(link_elf_obj, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, NULL); | ||||
static void | |||||
link_elf_protect_range(elf_file_t ef, vm_offset_t start, vm_offset_t end, | |||||
vm_prot_t prot) | |||||
{ | |||||
int error __unused; | |||||
KASSERT(start <= end && start >= (vm_offset_t)ef->address && | |||||
end <= round_page((vm_offset_t)ef->address + ef->lf.size), | |||||
("link_elf_protect_range: invalid range %#lx-%#lx", start, end)); | |||||
if (start == end) | |||||
return; | |||||
error = vm_map_protect(kernel_map, start, end, prot, FALSE); | |||||
KASSERT(error == KERN_SUCCESS, | |||||
("link_elf_protect_range: vm_map_protect() returned %d", error)); | |||||
kib: This relies on KERN_SUCCES == 0, I always want more explicitness in handling Mach<->errno… | |||||
Done Inline ActionsIn fact, it should be impossible for vm_map_protect() to fail here. markj: In fact, it should be impossible for vm_map_protect() to fail here. | |||||
} | |||||
/* | |||||
* Restrict permissions on linker file memory based on section flags. | |||||
* Sections need not be page-aligned, so overlap within a page is possible. | |||||
*/ | |||||
static void | |||||
link_elf_protect(elf_file_t ef) | |||||
{ | |||||
vm_offset_t end, segend, segstart, start; | |||||
vm_prot_t prot, segprot; | |||||
int i; | |||||
start = end = (vm_offset_t)ef->address; | |||||
prot = VM_PROT_READ; | |||||
for (i = 0; i < ef->nprogtab; i++) { | |||||
/* | |||||
* VNET and DPCPU sections have their memory allocated by their | |||||
* respective subsystems. | |||||
*/ | |||||
if (ef->progtab[i].name != NULL && ( | |||||
#ifdef VIMAGE | |||||
strcmp(ef->progtab[i].name, VNET_SETNAME) == 0 || | |||||
#endif | |||||
strcmp(ef->progtab[i].name, DPCPU_SETNAME) == 0)) | |||||
continue; | |||||
segstart = trunc_page((vm_offset_t)ef->progtab[i].addr); | |||||
segend = round_page((vm_offset_t)ef->progtab[i].addr + | |||||
ef->progtab[i].size); | |||||
segprot = VM_PROT_READ; | |||||
if ((ef->progtab[i].flags & SHF_WRITE) != 0) | |||||
segprot |= VM_PROT_WRITE; | |||||
if ((ef->progtab[i].flags & SHF_EXECINSTR) != 0) | |||||
segprot |= VM_PROT_EXECUTE; | |||||
if (end <= segstart) { | |||||
/* | |||||
* Case 1: there is no overlap between the previous | |||||
* segment and this one. Apply protections to the | |||||
* previous segment, and protect the gap between the | |||||
* previous and current segments, if any. | |||||
*/ | |||||
link_elf_protect_range(ef, start, end, prot); | |||||
link_elf_protect_range(ef, end, segstart, VM_PROT_READ); | |||||
start = segstart; | |||||
end = segend; | |||||
prot = segprot; | |||||
} else if (start < segstart && end == segend) { | |||||
/* | |||||
* Case 2: the current segment is a subrange of the | |||||
* previous segment. Apply protections to the | |||||
* non-overlapping portion of the previous segment. | |||||
*/ | |||||
link_elf_protect_range(ef, start, segstart, prot); | |||||
start = segstart; | |||||
prot |= segprot; | |||||
} else if (end < segend) { | |||||
/* | |||||
* Case 3: there is partial overlap between the previous | |||||
* and current segments. Apply protections to the | |||||
* non-overlapping portion of the previous segment, and | |||||
* then the overlap, which must use the union of the two | |||||
* segments' protections. | |||||
*/ | |||||
link_elf_protect_range(ef, start, segstart, prot); | |||||
link_elf_protect_range(ef, segstart, end, | |||||
prot | segprot); | |||||
start = end; | |||||
end = segend; | |||||
prot = segprot; | |||||
} else { | |||||
/* | |||||
* Case 4: the two segments reside in the same page. | |||||
*/ | |||||
prot |= segprot; | |||||
} | |||||
} | |||||
/* | |||||
* Fix up the last unprotected segment and trailing data. | |||||
*/ | |||||
link_elf_protect_range(ef, start, end, prot); | |||||
link_elf_protect_range(ef, end, | |||||
round_page((vm_offset_t)ef->address + ef->lf.size), VM_PROT_READ); | |||||
} | |||||
static int | static int | ||||
link_elf_link_preload(linker_class_t cls, const char *filename, | link_elf_link_preload(linker_class_t cls, const char *filename, | ||||
linker_file_t *result) | linker_file_t *result) | ||||
{ | { | ||||
Elf_Ehdr *hdr; | Elf_Ehdr *hdr; | ||||
Elf_Shdr *shdr; | Elf_Shdr *shdr; | ||||
Elf_Sym *es; | Elf_Sym *es; | ||||
void *modptr, *baseptr, *sizeptr; | void *modptr, *baseptr, *sizeptr; | ||||
▲ Show 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | #endif | ||||
ef->progtab[pb].name = "<<PROGBITS>>"; | ef->progtab[pb].name = "<<PROGBITS>>"; | ||||
#ifdef __amd64__ | #ifdef __amd64__ | ||||
else if (shdr[i].sh_type == SHT_X86_64_UNWIND) | else if (shdr[i].sh_type == SHT_X86_64_UNWIND) | ||||
ef->progtab[pb].name = "<<UNWIND>>"; | ef->progtab[pb].name = "<<UNWIND>>"; | ||||
#endif | #endif | ||||
else | else | ||||
ef->progtab[pb].name = "<<NOBITS>>"; | ef->progtab[pb].name = "<<NOBITS>>"; | ||||
ef->progtab[pb].size = shdr[i].sh_size; | ef->progtab[pb].size = shdr[i].sh_size; | ||||
ef->progtab[pb].flags = shdr[i].sh_flags; | |||||
ef->progtab[pb].sec = i; | ef->progtab[pb].sec = i; | ||||
if (ef->shstrtab && shdr[i].sh_name != 0) | if (ef->shstrtab && shdr[i].sh_name != 0) | ||||
ef->progtab[pb].name = | ef->progtab[pb].name = | ||||
ef->shstrtab + shdr[i].sh_name; | ef->shstrtab + shdr[i].sh_name; | ||||
if (ef->progtab[pb].name != NULL && | if (ef->progtab[pb].name != NULL && | ||||
!strcmp(ef->progtab[pb].name, DPCPU_SETNAME)) { | !strcmp(ef->progtab[pb].name, DPCPU_SETNAME)) { | ||||
void *dpcpu; | void *dpcpu; | ||||
▲ Show 20 Lines • Show All 409 Lines • ▼ Show 20 Lines | #endif | ||||
if (ef->object == NULL) { | if (ef->object == NULL) { | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto out; | goto out; | ||||
} | } | ||||
/* | /* | ||||
* In order to satisfy amd64's architectural requirements on the | * In order to satisfy amd64's architectural requirements on the | ||||
* location of code and data in the kernel's address space, request a | * location of code and data in the kernel's address space, request a | ||||
* mapping that is above the kernel. | * mapping that is above the kernel. | ||||
* | |||||
* Protections will be restricted once relocations are applied. | |||||
*/ | */ | ||||
#ifdef __amd64__ | #ifdef __amd64__ | ||||
mapbase = KERNBASE; | mapbase = KERNBASE; | ||||
#else | #else | ||||
mapbase = VM_MIN_KERNEL_ADDRESS; | mapbase = VM_MIN_KERNEL_ADDRESS; | ||||
#endif | #endif | ||||
error = vm_map_find(kernel_map, ef->object, 0, &mapbase, | error = vm_map_find(kernel_map, ef->object, 0, &mapbase, | ||||
round_page(mapsize), 0, VMFS_OPTIMAL_SPACE, VM_PROT_ALL, | round_page(mapsize), 0, VMFS_OPTIMAL_SPACE, VM_PROT_ALL, | ||||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Lines | #endif | ||||
else | else | ||||
ef->progtab[pb].addr = | ef->progtab[pb].addr = | ||||
(void *)(uintptr_t)mapbase; | (void *)(uintptr_t)mapbase; | ||||
if (ef->progtab[pb].addr == NULL) { | if (ef->progtab[pb].addr == NULL) { | ||||
error = ENOSPC; | error = ENOSPC; | ||||
goto out; | goto out; | ||||
} | } | ||||
ef->progtab[pb].size = shdr[i].sh_size; | ef->progtab[pb].size = shdr[i].sh_size; | ||||
ef->progtab[pb].flags = shdr[i].sh_flags; | |||||
ef->progtab[pb].sec = i; | ef->progtab[pb].sec = i; | ||||
if (shdr[i].sh_type == SHT_PROGBITS | if (shdr[i].sh_type == SHT_PROGBITS | ||||
#ifdef __amd64__ | #ifdef __amd64__ | ||||
|| shdr[i].sh_type == SHT_X86_64_UNWIND | || shdr[i].sh_type == SHT_X86_64_UNWIND | ||||
#endif | #endif | ||||
) { | ) { | ||||
error = vn_rdwr(UIO_READ, nd->ni_vp, | error = vn_rdwr(UIO_READ, nd->ni_vp, | ||||
ef->progtab[pb].addr, | ef->progtab[pb].addr, | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | |||||
#if defined(__i386__) || defined(__amd64__) | #if defined(__i386__) || defined(__amd64__) | ||||
/* Now ifuncs. */ | /* Now ifuncs. */ | ||||
error = link_elf_reloc_local(lf, true); | error = link_elf_reloc_local(lf, true); | ||||
if (error != 0) | if (error != 0) | ||||
goto out; | goto out; | ||||
#endif | #endif | ||||
/* Invoke .ctors */ | link_elf_protect(ef); | ||||
link_elf_invoke_ctors(lf->ctors_addr, lf->ctors_size); | link_elf_invoke_ctors(lf->ctors_addr, lf->ctors_size); | ||||
*result = lf; | *result = lf; | ||||
out: | out: | ||||
VOP_UNLOCK(nd->ni_vp, 0); | VOP_UNLOCK(nd->ni_vp, 0); | ||||
vn_close(nd->ni_vp, FREAD, td->td_ucred, td); | vn_close(nd->ni_vp, FREAD, td->td_ucred, td); | ||||
free(nd, M_TEMP); | free(nd, M_TEMP); | ||||
if (error && lf) | if (error && lf) | ||||
linker_file_unload(lf, LINKER_UNLOAD_FORCE); | linker_file_unload(lf, LINKER_UNLOAD_FORCE); | ||||
▲ Show 20 Lines • Show All 567 Lines • Show Last 20 Lines |
This relies on KERN_SUCCES == 0, I always want more explicitness in handling Mach<->errno translation.
Also, when could vm_map_protect() above fail ?