corda/sdk/trts/linux/elf_parser.c
Angie Chinchilla 9441de4c38 Initial release of Intel SGX for Linux.
This release is used in conjunction with the linux-sgx-driver Intial release:
https://github.com/01org/linux-sgx-driver
commit-id: 0e865ce5e6b297a787bcdc12d98bada8174be6d7

Intel-id: 33399

Signed-off-by: Angie Chinchilla <angie.v.chinchilla@intel.com>
2016-06-23 18:51:53 -04:00

420 lines
13 KiB
C

/*
* Copyright (C) 2011-2016 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* This file is part of trusted loader for tRTS.
*/
#include "elf_parser.h"
#include "rts.h"
#include "util.h"
#include "elf_util.h"
#include "global_data.h"
static int elf_tls_aligned_virtual_size(const void *enclave_base,
size_t *aligned_virtual_size);
static ElfW(Phdr)* get_phdr(const ElfW(Ehdr)* ehdr)
{
if (ehdr == NULL)
return NULL; /* Invalid image. */
/* Check the ElfW Magic number. */
if ((ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
(ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
(ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
(ehdr->e_ident[EI_MAG3] != ELFMAG3))
return NULL;
/* Enclave image should be a shared object file. */
if (ehdr->e_type != ET_DYN)
return NULL;
return GET_PTR(ElfW(Phdr), ehdr, ehdr->e_phoff);
}
static ElfW(Sym)* get_sym(ElfW(Sym)* symtab, size_t idx)
{
if(STB_WEAK == ELFW(ST_BIND)(symtab[idx].st_info)
&& 0 == symtab[idx].st_value)
{
return NULL;
}
return &symtab[idx];
}
#ifdef __x86_64__
/* Relocation for x64 (with addend) */
static int do_relocs(const ElfW(Addr) enclave_base,
ElfW(Addr) rela_offset,
ElfW(Addr) sym_offset,
size_t nr_relocs)
{
ElfW(Rela)* rela = GET_PTR(ElfW(Rela), enclave_base, rela_offset);
ElfW(Sym)* symtab = GET_PTR(ElfW(Sym), enclave_base, sym_offset);
ElfW(Sym)* sym;
size_t i;
size_t aligned_virtual_size = 0;
for (i = 0; i < nr_relocs; ++i, ++rela)
{
ElfW(Addr)* reloc_addr = GET_PTR(ElfW(Addr), enclave_base, rela->r_offset);
switch (ELF64_R_TYPE(rela->r_info))
{
case R_X86_64_RELATIVE:
*reloc_addr = enclave_base + (uintptr_t)rela->r_addend;
break;
case R_X86_64_GLOB_DAT:
case R_X86_64_JUMP_SLOT:
case R_X86_64_64:
sym = get_sym(symtab, ELF64_R_SYM(rela->r_info));
if(!sym)
break;
*reloc_addr = enclave_base + sym->st_value + (uintptr_t)rela->r_addend;
break;
case R_X86_64_DTPMOD64:
*reloc_addr = 1;
break;
case R_X86_64_DTPOFF64:
sym = get_sym(symtab, ELF64_R_SYM(rela->r_info));
if(!sym)
break;
*reloc_addr = sym->st_value + (uintptr_t)rela->r_addend;
break;
case R_X86_64_TPOFF64:
sym = get_sym(symtab, ELF64_R_SYM(rela->r_info));
if(!sym)
break;
if ((0 == elf_tls_aligned_virtual_size((void *)enclave_base, &aligned_virtual_size)) && (aligned_virtual_size))
{
*reloc_addr = sym->st_value + (uintptr_t)rela->r_addend - aligned_virtual_size;
break;
}
else
return -1;
case R_X86_64_NONE:
break;
default: /* unsupported relocs */
return -1;
}
}
return 0;
}
#elif defined(__i386__)
/* Relocation for x86 (without addend) */
static int do_relocs(const ElfW(Addr) enclave_base,
ElfW(Addr) rel_offset,
ElfW(Addr) sym_offset,
size_t nr_relocs)
{
ElfW(Rel)* rel = GET_PTR(ElfW(Rel), enclave_base, rel_offset);
ElfW(Sym)* symtab = GET_PTR(ElfW(Sym), enclave_base, sym_offset);
ElfW(Sym)* sym = NULL;
size_t i;
size_t aligned_virtual_size = 0;
for (i = 0; i < nr_relocs; ++i, ++rel)
{
ElfW(Addr)* reloc_addr = GET_PTR(ElfW(Addr), enclave_base, rel->r_offset);
if(R_386_RELATIVE == ELF32_R_TYPE(rel->r_info))
{
*reloc_addr += enclave_base; /* B+A */
continue;
}
sym = get_sym(symtab, ELF32_R_SYM(rel->r_info));
if(!sym) /* when the weak symbol is not implemented, sym is NULL */
continue;
switch (ELF32_R_TYPE(rel->r_info))
{
case R_386_GLOB_DAT:
case R_386_JMP_SLOT: /* S */
*reloc_addr = enclave_base + sym->st_value;
break;
case R_386_32: /* S+A */
*reloc_addr += enclave_base + sym->st_value;
break;
case R_386_PC32: /* S+A-P */
*reloc_addr += (enclave_base + sym->st_value - (ElfW(Addr))reloc_addr);
break;
case R_386_NONE:
break;
case R_386_TLS_DTPMOD32:
*reloc_addr = 1;
break;
case R_386_TLS_DTPOFF32:
*reloc_addr = sym->st_value;
break;
case R_386_TLS_TPOFF:
if ((0 == elf_tls_aligned_virtual_size((void *)enclave_base, &aligned_virtual_size)) && (aligned_virtual_size))
{
*reloc_addr += sym->st_value - aligned_virtual_size;
break;
}
else
return -1;
case R_386_TLS_TPOFF32:
if ((0 == elf_tls_aligned_virtual_size((void *)enclave_base, &aligned_virtual_size)) && (aligned_virtual_size))
{
*reloc_addr += aligned_virtual_size - sym->st_value;
break;
}
else
return -1;
default: /* unsupported relocs */
return -1;
}
}
return 0;
}
#endif
#define DO_REL(base_addr, rel_offset, sym_offset, total_sz, rel_entry_sz) \
do { \
if (rel_offset) \
{ \
size_t n; \
if (rel_entry_sz == 0) \
return -1; \
n = total_sz/rel_entry_sz; \
if (do_relocs((ElfW(Addr))enclave_base, rel_offset, sym_offset, n)) \
return -1; \
} \
} while (0)
/* By default all symbol is linked as global symbol by link editor. When call global symbol,
* we first call .plt entry. It should have problems if the call goloal symbol when relocation
* is not done.
* Declare relocate_enclave as .hidden is to make it local symbol.
* Since this function is called before relocation is done, we must make
* it local symbol, so the code is like "fce3: e8 98 12 00 00 call 10f80 <relocate_enclave>"
* 0x9812=0x10f80-0xfce8
*/
__attribute__ ((visibility ("hidden")))
int relocate_enclave(void* enclave_base)
{
ElfW(Half) phnum = 0;
ElfW(Ehdr) *ehdr = (ElfW(Ehdr)*)enclave_base;
ElfW(Phdr) *phdr = get_phdr(ehdr);
if (phdr == NULL)
return -1; /* Invalid image. */
for (; phnum < ehdr->e_phnum; phnum++, phdr++)
{
/* Search for dynamic segment */
if (phdr->p_type == PT_DYNAMIC)
{
size_t count;
size_t n_dyn = phdr->p_filesz/sizeof(ElfW(Dyn));
ElfW(Dyn) *dyn = GET_PTR(ElfW(Dyn), ehdr, phdr->p_paddr);
ElfW(Addr) sym_offset = 0;
ElfW(Addr) rel_offset = 0;
ElfW(Addr) plt_offset = 0;
size_t rel_total_sz = 0;
size_t rel_entry_sz = 0;
size_t plt_total_sz = 0;
for (count = 0; count < n_dyn; count++, dyn++)
{
if (dyn->d_tag == DT_NULL) /* End */
break;
switch (dyn->d_tag)
{
case DT_SYMTAB: /* symbol table */
sym_offset = dyn->d_un.d_ptr;
break;
case RTS_DT_REL:/* Rel (x86) or Rela (x64) relocs */
rel_offset = dyn->d_un.d_ptr;
break;
case RTS_DT_RELSZ:
rel_total_sz = dyn->d_un.d_val;
break;
case RTS_DT_RELENT:
rel_entry_sz = dyn->d_un.d_val;
break;
case DT_JMPREL: /* PLT relocs */
plt_offset = dyn->d_un.d_ptr;
break;
case DT_PLTRELSZ:
plt_total_sz = dyn->d_un.d_val;
break;
}
}
DO_REL(enclave_base, rel_offset, sym_offset, rel_total_sz, rel_entry_sz);
DO_REL(enclave_base, plt_offset, sym_offset, plt_total_sz, rel_entry_sz);
}
}
return 0;
}
int elf_tls_info(const void* enclave_base,
uintptr_t *tls_addr, size_t *tdata_size)
{
ElfW(Half) phnum = 0;
const ElfW(Ehdr) *ehdr = (const ElfW(Ehdr)*)enclave_base;
ElfW(Phdr) *phdr = get_phdr(ehdr);
if (!tls_addr || !tdata_size)
return -1;
if (phdr == NULL)
return -1; /* Invalid image. */
/* Search for TLS segment */
*tls_addr = 0;
*tdata_size = 0;
for (; phnum < ehdr->e_phnum; phnum++, phdr++)
{
if (phdr->p_type == PT_TLS)
{
/* tls_addr here is got from the program header, the address
* need to be added by the enclave base.
*/
*tls_addr = (size_t)enclave_base + phdr->p_vaddr;
*tdata_size = phdr->p_filesz;
break;
}
}
return 0;
}
static int elf_tls_aligned_virtual_size(const void *enclave_base,
size_t *aligned_virtual_size)
{
ElfW(Half) phnum = 0;
const ElfW(Ehdr) *ehdr = (const ElfW(Ehdr)*)enclave_base;
ElfW(Phdr) *phdr = get_phdr(ehdr);
size_t virtual_size =0, align = 0;
if (phdr == NULL)
return -1;
if (!aligned_virtual_size)
return -1;
*aligned_virtual_size = 0;
for (; phnum < ehdr->e_phnum; phnum++, phdr++)
{
if (phdr->p_type == PT_TLS)
{
virtual_size = phdr->p_memsz;
align = phdr->p_align;
/* p_align == 0 or p_align == 1 means no alignment is required */
if (align == 0 || align == 1)
*aligned_virtual_size = virtual_size;
else
*aligned_virtual_size = (virtual_size + align - 1) & (~(align - 1));
break;
}
}
return 0;
}
int elf_get_init_array(const void* enclave_base,
uintptr_t *init_array_addr, size_t *init_array_size)
{
ElfW(Half) phnum = 0;
const ElfW(Ehdr) *ehdr = (const ElfW(Ehdr)*)enclave_base;
ElfW(Phdr) *phdr = get_phdr(ehdr);
if (!init_array_addr || !init_array_size)
return -1;
if (phdr == NULL)
return -1; /* Invalid image. */
*init_array_addr = 0;
*init_array_size = 0;
/* Search for Dynamic segment */
for (; phnum < ehdr->e_phnum; phnum++, phdr++)
{
if (phdr->p_type == PT_DYNAMIC)
{
size_t count;
size_t n_dyn = phdr->p_filesz/sizeof(ElfW(Dyn));
ElfW(Dyn) *dyn = GET_PTR(ElfW(Dyn), ehdr, phdr->p_paddr);
for (count = 0; count < n_dyn; count++, dyn++)
{
switch (dyn->d_tag)
{
case DT_INIT_ARRAY:
*init_array_addr = dyn->d_un.d_ptr;
break;
case DT_INIT_ARRAYSZ:
*init_array_size = dyn->d_un.d_val;
break;
}
}
}
}
return 0;
}
/* vim: set ts=4 sw=4 et cin: */