blob: 88d37eac1b669406bc5b2d2a77045907cce983c2 [file] [edit]
/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*-
*
* This file is part of PRoot.
*
* Copyright (C) 2014 STMicroelectronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
#include <stdbool.h> /* bool, true, false, */
#define NO_LIBC_HEADER
#include "loader/script.h"
#include "compat.h"
#include "arch.h"
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#if GCC_VERSION < 40500
#define __builtin_unreachable()
#endif
#if defined(ARCH_X86_64)
# include "loader/assembly-x86_64.h"
#elif defined(ARCH_ARM_EABI)
# include "loader/assembly-arm.h"
#elif defined(ARCH_ARM64)
# include "loader/assembly-arm64.h"
#elif defined(ARCH_X86)
# include "loader/assembly-x86.h"
#else
# error "Unsupported architecture"
#endif
#if !defined(MMAP_OFFSET_SHIFT)
# define MMAP_OFFSET_SHIFT 0
#endif
#define FATAL() do { \
SYSCALL(EXIT, 1, 182); \
__builtin_unreachable(); \
} while (0)
#define unlikely(expr) __builtin_expect(!!(expr), 0)
/**
* Clear the memory from @start (inclusive) to @end (exclusive).
*/
static inline void clear(word_t start, word_t end)
{
byte_t *start_misaligned;
byte_t *end_misaligned;
word_t *start_aligned;
word_t *end_aligned;
/* Compute the number of mis-aligned bytes. */
word_t start_bytes = start % sizeof(word_t);
word_t end_bytes = end % sizeof(word_t);
/* Compute aligned addresses. */
start_aligned = (word_t *) (start_bytes ? start + sizeof(word_t) - start_bytes : start);
end_aligned = (word_t *) (end - end_bytes);
/* Clear leading mis-aligned bytes. */
start_misaligned = (byte_t *) start;
while (start_misaligned < (byte_t *) start_aligned)
*start_misaligned++ = 0;
/* Clear aligned bytes. */
while (start_aligned < end_aligned)
*start_aligned++ = 0;
/* Clear trailing mis-aligned bytes. */
end_misaligned = (byte_t *) end_aligned;
while (end_misaligned < (byte_t *) end)
*end_misaligned++ = 0;
}
/**
* Return the address of the last path component of @string_. Note
* that @string_ is not modified.
*/
static inline word_t basename(word_t string_)
{
byte_t *string = (byte_t *) string_;
byte_t *cursor;
for (cursor = string; *cursor != 0; cursor++)
;
for (; *cursor != (byte_t) '/' && cursor > string; cursor--)
;
if (cursor != string)
cursor++;
return (word_t) cursor;
}
/**
* Interpret the load script pointed to by @cursor.
*/
void _start(void *cursor)
{
bool traced = false;
bool reset_at_base = true;
word_t at_base = 0;
word_t fd = -1;
word_t status;
while(1) {
LoadStatement *stmt = cursor;
switch (stmt->action) {
case LOAD_ACTION_OPEN_NEXT:
status = SYSCALL(CLOSE, 1, fd);
if (unlikely((int) status < 0))
FATAL();
/* Fall through. */
case LOAD_ACTION_OPEN:
#ifdef OPENAT
fd = SYSCALL(OPENAT, 4, AT_FDCWD, stmt->open.string_address, O_RDONLY, 0);
#else
fd = SYSCALL(OPEN, 3, stmt->open.string_address, O_RDONLY, 0);
#endif
if (unlikely((int) fd < 0))
FATAL();
reset_at_base = true;
cursor += LOAD_STATEMENT_SIZE(*stmt, open);
break;
case LOAD_ACTION_MMAP_FILE:
status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length,
stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED, fd,
stmt->mmap.offset >> MMAP_OFFSET_SHIFT);
if (unlikely(status != stmt->mmap.addr))
FATAL();
if (stmt->mmap.clear_length != 0)
clear(stmt->mmap.addr + stmt->mmap.length - stmt->mmap.clear_length,
stmt->mmap.addr + stmt->mmap.length);
if (reset_at_base) {
at_base = stmt->mmap.addr;
reset_at_base = false;
}
cursor += LOAD_STATEMENT_SIZE(*stmt, mmap);
break;
case LOAD_ACTION_MMAP_ANON:
status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length,
stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
if (unlikely(status != stmt->mmap.addr))
FATAL();
cursor += LOAD_STATEMENT_SIZE(*stmt, mmap);
break;
case LOAD_ACTION_START_TRACED:
traced = true;
/* Fall through. */
case LOAD_ACTION_START: {
word_t *cursor2 = (word_t *) stmt->start.stack_pointer;
const word_t argc = cursor2[0];
const word_t at_execfn = cursor2[1];
word_t name;
status = SYSCALL(CLOSE, 1, fd);
if (unlikely((int) status < 0))
FATAL();
/* Right after execve, the stack content is as follow:
*
* +------+--------+--------+--------+
* | argc | argv[] | envp[] | auxv[] |
* +------+--------+--------+--------+
*/
/* Skip argv[]. */
cursor2 += argc + 1;
/* Skip envp[]. */
do cursor2++; while (cursor2[0] != 0);
cursor2++;
/* Adjust auxv[]. */
do {
switch (cursor2[0]) {
case AT_PHDR:
cursor2[1] = stmt->start.at_phdr;
break;
case AT_PHENT:
cursor2[1] = stmt->start.at_phent;
break;
case AT_PHNUM:
cursor2[1] = stmt->start.at_phnum;
break;
case AT_ENTRY:
cursor2[1] = stmt->start.at_entry;
break;
case AT_BASE:
cursor2[1] = at_base;
break;
case AT_EXECFN:
/* stmt->start.at_execfn can't be used for now since it is
* currently stored in a location that will be scratched
* by the process (below the final stack pointer). */
cursor2[1] = at_execfn;
break;
default:
break;
}
cursor2 += 2;
} while (cursor2[0] != AT_NULL);
/* Note that only 2 arguments are actually necessary... */
name = basename(stmt->start.at_execfn);
SYSCALL(PRCTL, 3, PR_SET_NAME, name, 0);
if (unlikely(traced))
SYSCALL(EXECVE, 6, 1,
stmt->start.stack_pointer,
stmt->start.entry_point, 2, 3, 4);
else
BRANCH(stmt->start.stack_pointer, stmt->start.entry_point);
FATAL();
}
default:
FATAL();
}
}
FATAL();
}