mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-23 02:34:08 +00:00
MEM_SIZE needs to get increased for the 64-bit 'linux_x86' platform when building with -O0 on the current master branch. Fixes #970.
376 lines
8.2 KiB
C++
376 lines
8.2 KiB
C++
/*
|
|
* \brief libc file handling function emulation (open/read/write/mmap)
|
|
* \author Sebastian Sumpf
|
|
* \date 2009-10-26
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2009-2013 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
#include <base/allocator_avl.h>
|
|
#include <base/printf.h>
|
|
#include <ldso/arch.h>
|
|
#include <rom_session/connection.h>
|
|
#include <rm_session/connection.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/param.h>
|
|
#include <util/list.h>
|
|
|
|
#include "file.h"
|
|
|
|
extern int debug;
|
|
|
|
namespace Genode {
|
|
|
|
/**
|
|
* Managed dataspace for ELF files (singelton)
|
|
*/
|
|
class Rm_area : public Rm_connection
|
|
{
|
|
private:
|
|
|
|
/* size of dataspace */
|
|
enum { RESERVATION = 160 * 1024 * 1024 };
|
|
|
|
addr_t _base; /* base address of dataspace */
|
|
Allocator_avl _range; /* VM range allocator */
|
|
|
|
Rm_area(addr_t base)
|
|
: Rm_connection(0, RESERVATION), _range(env()->heap())
|
|
{
|
|
on_destruction(KEEP_OPEN);
|
|
|
|
_base = (addr_t) env()->rm_session()->attach_at(dataspace(), base);
|
|
_range.add_range(base, RESERVATION);
|
|
}
|
|
|
|
public:
|
|
|
|
static Rm_area *r(addr_t base = 0)
|
|
{
|
|
static Rm_area _area(base);
|
|
return &_area;
|
|
}
|
|
|
|
/**
|
|
* Reserve VM region of 'size' at 'vaddr'. Allocate any free region if
|
|
* 'vaddr' is zero
|
|
*/
|
|
addr_t alloc_region(size_t size, addr_t vaddr = 0)
|
|
{
|
|
addr_t addr = vaddr;
|
|
|
|
if (addr && (_range.alloc_addr(size, addr).is_error()))
|
|
throw Region_conflict();
|
|
else if (!addr && _range.alloc_aligned(size, (void **)&addr, 12).is_error())
|
|
throw Region_conflict();
|
|
|
|
return addr;
|
|
}
|
|
|
|
void free_region(addr_t vaddr) { _range.free((void *)vaddr); }
|
|
|
|
/**
|
|
* Overwritten from 'Rm_connection'
|
|
*/
|
|
Local_addr attach_at(Dataspace_capability ds, addr_t local_addr,
|
|
size_t size = 0, off_t offset = 0) {
|
|
return Rm_connection::attach_at(ds, local_addr - _base, size, offset); }
|
|
|
|
/**
|
|
* Overwritten from 'Rm_connection'
|
|
*/
|
|
Local_addr attach_executable(Dataspace_capability ds, addr_t local_addr,
|
|
size_t size = 0, off_t offset = 0) {
|
|
return Rm_connection::attach_executable(ds, local_addr - _base, size, offset); }
|
|
|
|
void detach(Local_addr local_addr) {
|
|
Rm_connection::detach((addr_t)local_addr - _base); }
|
|
};
|
|
|
|
|
|
class Fd_handle : public List<Fd_handle>::Element
|
|
{
|
|
private:
|
|
|
|
addr_t _vaddr; /* image start */
|
|
addr_t _daddr; /* data start */
|
|
Rom_dataspace_capability _ds_rom; /* image ds */
|
|
Ram_dataspace_capability _ds_ram; /* data ds */
|
|
int _fd; /* file handle */
|
|
|
|
public:
|
|
|
|
enum {
|
|
ENOT_FOUND = 1
|
|
};
|
|
|
|
Fd_handle(int fd, Rom_dataspace_capability ds_rom)
|
|
: _vaddr(~0UL), _ds_rom(ds_rom), _fd(fd)
|
|
{}
|
|
|
|
addr_t vaddr() { return _vaddr; }
|
|
Rom_dataspace_capability dataspace() { return _ds_rom; }
|
|
|
|
void setup_data(addr_t vaddr, addr_t vlimit, addr_t flimit, off_t offset)
|
|
{
|
|
/* allocate data segment */
|
|
_ds_ram = env()->ram_session()->alloc(vlimit - vaddr);
|
|
Rm_area::r()->attach_at(_ds_ram, vaddr);
|
|
|
|
/* map rom data segment */
|
|
void *rom_data = env()->rm_session()->attach(_ds_rom, 0, offset);
|
|
|
|
/* copy data */
|
|
memcpy((void *)vaddr, rom_data, flimit - vaddr);
|
|
env()->rm_session()->detach(rom_data);
|
|
|
|
/* set parent cap (arch.lib.a) */
|
|
set_parent_cap_arch((void *)vaddr);
|
|
|
|
_daddr = vaddr;
|
|
}
|
|
|
|
void setup_text(addr_t vaddr, size_t size, off_t offset)
|
|
{
|
|
_vaddr = vaddr;
|
|
Rm_area::r()->attach_executable(_ds_rom, vaddr, size, offset);
|
|
}
|
|
|
|
addr_t alloc_region(addr_t vaddr, addr_t vlimit)
|
|
{
|
|
Rm_area *a = Rm_area::r(vaddr);
|
|
return a->alloc_region(vlimit - vaddr, vaddr);
|
|
}
|
|
|
|
static List<Fd_handle> *file_list()
|
|
{
|
|
static List<Fd_handle> _file_list;
|
|
return &_file_list;
|
|
}
|
|
|
|
static Fd_handle *find_handle(int fd)
|
|
{
|
|
Fd_handle *h = file_list()->first();
|
|
|
|
while (h) {
|
|
if (h->_fd == fd)
|
|
return h;
|
|
h = h->next();
|
|
}
|
|
throw ENOT_FOUND;
|
|
}
|
|
|
|
static void free(void *addr)
|
|
{
|
|
addr_t vaddr = (addr_t) addr;
|
|
|
|
Fd_handle *h = file_list()->first();
|
|
|
|
while (h) {
|
|
|
|
if (h->_vaddr != vaddr) {
|
|
h = h->next();
|
|
continue;
|
|
}
|
|
|
|
destroy(env()->heap(), h);
|
|
return;
|
|
}
|
|
}
|
|
|
|
~Fd_handle()
|
|
{
|
|
file_list()->remove(this);
|
|
|
|
if (_vaddr != ~0UL) {
|
|
Rm_area::r()->detach(_vaddr);
|
|
Rm_area::r()->detach(_daddr);
|
|
Rm_area::r()->free_region(_vaddr);
|
|
env()->ram_session()->free(_ds_ram);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
extern "C" int open(const char *pathname, int flags)
|
|
{
|
|
using namespace Genode;
|
|
static int fd = -1;
|
|
|
|
/* skip directory part from pathname, leaving only the plain filename */
|
|
const char *filename = pathname;
|
|
for (; *pathname; pathname++)
|
|
if (*pathname == '/')
|
|
filename = pathname + 1;
|
|
|
|
try {
|
|
/* open the file dataspace and attach it */
|
|
Rom_connection rom(filename);
|
|
rom.on_destruction(Rom_connection::KEEP_OPEN);
|
|
|
|
Fd_handle::file_list()->insert(new(env()->heap())
|
|
Fd_handle(++fd, rom.dataspace()));
|
|
return fd;
|
|
}
|
|
catch (...) {
|
|
PERR("Could not open %s\n", filename);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
extern "C" int find_binary_name(int fd, char *buf, size_t buf_size)
|
|
{
|
|
using namespace Genode;
|
|
|
|
Fd_handle *h;
|
|
try {
|
|
h = Fd_handle::find_handle(fd);
|
|
}
|
|
catch (...) {
|
|
PERR("handle not found\n");
|
|
return -1;
|
|
}
|
|
|
|
return binary_name(h->dataspace(), buf, buf_size);
|
|
}
|
|
|
|
|
|
extern "C" ssize_t read(int fd, void *buf, size_t count)
|
|
{
|
|
using namespace Genode;
|
|
|
|
Fd_handle *h;
|
|
try {
|
|
h = Fd_handle::find_handle(fd);
|
|
}
|
|
catch (...) {
|
|
PERR("handle not found\n");
|
|
return -1;
|
|
}
|
|
|
|
try {
|
|
void *base = env()->rm_session()->attach(h->dataspace(), count);
|
|
memcpy(buf, base, count);
|
|
env()->rm_session()->detach(base);
|
|
}
|
|
catch (...) {
|
|
return -1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
extern "C" ssize_t write(int fd, const void *buf, size_t count)
|
|
{
|
|
Genode::printf("%p", buf);
|
|
return count;
|
|
}
|
|
|
|
|
|
extern "C" int munmap(void *addr, size_t /* length */)
|
|
{
|
|
using namespace Genode;
|
|
Fd_handle::free(addr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
|
{
|
|
using namespace Genode;
|
|
|
|
if(!(flags & MAP_ANON)) {
|
|
PERR("No MAP_ANON");
|
|
return MAP_FAILED;
|
|
}
|
|
|
|
/* called during ldso relocation */
|
|
if (flags & MAP_LDSO) {
|
|
enum { MEM_SIZE = 48 * 1024 };
|
|
static char _mem[MEM_SIZE];
|
|
|
|
/* generate fault on allocation */
|
|
if (length > MEM_SIZE) {
|
|
int *fault = (int *)0xa110ce88;
|
|
*fault = 1;
|
|
}
|
|
return _mem;
|
|
}
|
|
/* memory allocation */
|
|
else {
|
|
void *base;
|
|
|
|
try {
|
|
Ram_dataspace_capability ds_cap = env()->ram_session()->alloc(round_page(length));
|
|
base = env()->rm_session()->attach(ds_cap, length, 0,
|
|
(addr) ? true : false, (addr_t)addr);
|
|
}
|
|
catch(...) {
|
|
PERR("Anonmymous mmap failed\n");
|
|
return MAP_FAILED;
|
|
}
|
|
|
|
if (debug)
|
|
PDBG("base %p", base);
|
|
|
|
return base;
|
|
}
|
|
|
|
return MAP_FAILED;
|
|
}
|
|
|
|
|
|
extern "C" void *genode_map(int fd, Elf_Phdr **segs)
|
|
{
|
|
using namespace Genode;
|
|
|
|
if (1 > 1) {
|
|
PERR("More than two segments in ELF");
|
|
return MAP_FAILED;
|
|
}
|
|
|
|
Fd_handle *h;
|
|
try { h = Fd_handle::find_handle(fd); }
|
|
catch (...) {
|
|
PERR("handle not found\n");
|
|
return MAP_FAILED;
|
|
}
|
|
|
|
addr_t base_vaddr = trunc_page(segs[0]->p_vaddr);
|
|
addr_t base_offset = trunc_page(segs[0]->p_offset);
|
|
addr_t base_msize = round_page(segs[1]->p_vaddr - base_vaddr);
|
|
addr_t base_vlimit = round_page(segs[1]->p_vaddr + segs[1]->p_memsz);
|
|
/* is this a fixed address */
|
|
bool fixed = base_vaddr ? true : false;
|
|
|
|
try {
|
|
base_vaddr = h->alloc_region(base_vaddr, base_vlimit);
|
|
} catch (...) {
|
|
PERR("Region allocation failed: %lx-%lx", base_vaddr, base_vlimit);
|
|
return MAP_FAILED;
|
|
}
|
|
|
|
/* map text segment */
|
|
h->setup_text(base_vaddr, base_msize, base_offset);
|
|
|
|
addr_t offset = fixed ? 0 : base_vaddr;
|
|
base_vlimit += offset;
|
|
addr_t base_flimit = offset + segs[1]->p_vaddr + segs[1]->p_filesz;
|
|
base_vaddr = offset + trunc_page(segs[1]->p_vaddr);
|
|
base_offset = trunc_page(segs[1]->p_offset);
|
|
|
|
/* copy data segment */
|
|
h->setup_data(base_vaddr, base_vlimit, base_flimit, base_offset);
|
|
|
|
return (void *)h->vaddr();
|
|
}
|
|
|