2nd try: Introduce a small api for loading code (codecs,plugins) from disk/memory.

It's a used by codec/plugin loading and vastly reduces code duplication. It's also a step forward in getting rid of libuisimulator in the application ports.

Apparently sh needs linker symbols prefixed with _ even if they're referenced without from C code.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27902 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Thomas Martitz 2010-08-27 00:29:50 +00:00
parent 97d2a6ec5c
commit 194174a371
8 changed files with 316 additions and 239 deletions

View file

@ -28,6 +28,7 @@
#include <ctype.h>
#include <stdarg.h>
#include "string-extra.h"
#include "load_code.h"
#include "debug.h"
#include "button.h"
#include "dir.h"
@ -74,26 +75,13 @@ size_t codec_size;
extern void* plugin_get_audio_buffer(size_t *buffer_size);
#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(HAVE_RECORDING)
#undef open
static int open(const char* pathname, int flags, ...)
{
#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
int fd;
if (flags & O_CREAT)
{
va_list ap;
va_start(ap, flags);
fd = sim_open(pathname, flags, va_arg(ap, unsigned int));
va_end(ap);
}
else
fd = sim_open(pathname, flags);
return fd;
#else
return file_open(pathname, flags);
#endif
}
#endif
struct codec_api ci = {
0, /* filesize */
@ -197,62 +185,46 @@ void codec_get_full_path(char *path, const char *codec_root_fn)
CODECS_DIR, codec_root_fn);
}
static int codec_load_ram(int size, struct codec_api *api)
static int codec_load_ram(void *handle, struct codec_api *api)
{
struct codec_header *hdr;
struct codec_header *hdr = lc_get_header(handle);
int status;
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
hdr = (struct codec_header *)codecbuf;
if (size <= (signed)sizeof(struct codec_header)
|| (hdr->magic != CODEC_MAGIC
#ifdef HAVE_RECORDING
&& hdr->magic != CODEC_ENC_MAGIC
#endif
)
|| hdr->target_id != TARGET_ID
|| hdr->load_addr != codecbuf
|| hdr->end_addr > codecbuf + CODEC_SIZE)
{
logf("codec header error");
return CODEC_ERROR;
}
codec_size = hdr->end_addr - codecbuf;
#elif (CONFIG_PLATFORM & PLATFORM_HOSTED)
void *pd;
hdr = sim_codec_load_ram(codecbuf, size, &pd);
if (pd == NULL)
return CODEC_ERROR;
if (hdr == NULL
|| (hdr->magic != CODEC_MAGIC
#ifdef HAVE_RECORDING
&& hdr->magic != CODEC_ENC_MAGIC
#endif
)
|| hdr->target_id != TARGET_ID) {
sim_codec_close(pd);
)
|| hdr->target_id != TARGET_ID
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|| hdr->load_addr != codecbuf
|| hdr->end_addr > codecbuf + CODEC_SIZE
#endif
)
{
logf("codec header error");
lc_close(handle);
return CODEC_ERROR;
}
codec_size = codecbuf - codecbuf;
#endif /* CONFIG_PLATFORM */
if (hdr->api_version > CODEC_API_VERSION
|| hdr->api_version < CODEC_MIN_API_VERSION) {
sim_codec_close(pd);
logf("codec api version error");
lc_close(handle);
return CODEC_ERROR;
}
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
codec_size = hdr->end_addr - codecbuf;
#else
codec_size = 0;
#endif
*(hdr->api) = api;
cpucache_invalidate();
status = hdr->entry_point();
sim_codec_close(pd);
lc_close(handle);
return status;
}
@ -260,36 +232,37 @@ static int codec_load_ram(int size, struct codec_api *api)
int codec_load_buf(unsigned int hid, struct codec_api *api)
{
int rc;
void *handle;
rc = bufread(hid, CODEC_SIZE, codecbuf);
if (rc < 0) {
logf("error loading codec");
return CODEC_ERROR;
}
handle = lc_open_from_mem(codecbuf, rc);
if (handle == NULL)
{
logf("error loading codec");
return CODEC_ERROR;
}
api->discard_codec();
return codec_load_ram(rc, api);
return codec_load_ram(handle, api);
}
int codec_load_file(const char *plugin, struct codec_api *api)
{
char path[MAX_PATH];
int fd;
int rc;
void *handle;
codec_get_full_path(path, plugin);
fd = open(path, O_RDONLY);
if (fd < 0) {
logf("Codec load error:%d", fd);
handle = lc_open(path, codecbuf, CODEC_SIZE);
if (handle == NULL) {
logf("Codec load error");
splashf(HZ*2, "Couldn't load codec: %s", path);
return fd;
}
rc = read(fd, &codecbuf[0], CODEC_SIZE);
close(fd);
if (rc <= 0) {
logf("Codec read error");
return CODEC_ERROR;
}
return codec_load_ram((size_t)rc, api);
return codec_load_ram(handle, api);
}

View file

@ -42,6 +42,7 @@
#include "errno.h"
#include "diacritic.h"
#include "filefuncs.h"
#include "load_code.h"
#if CONFIG_CHARGING
#include "power.h"
@ -75,21 +76,19 @@ static unsigned int open_files;
#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
static unsigned char pluginbuf[PLUGIN_BUFFER_SIZE];
void *sim_plugin_load(char *plugin, void **pd);
void sim_plugin_close(void *pd);
void sim_lcd_ex_init(unsigned long (*getpixel)(int, int));
void sim_lcd_ex_update_rect(int x, int y, int width, int height);
#else
#define sim_plugin_close(x)
extern unsigned char pluginbuf[];
#include "bitswap.h"
#endif
/* for actual plugins only, not for codecs */
static bool plugin_loaded = false;
static int plugin_size = 0;
static bool (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */
static char current_plugin[MAX_PATH];
/* NULL if no plugin is loaded, otherwise the handle that lc_open() returned */
static void *current_plugin_handle;
char *plugin_get_current_filename(void);
@ -728,98 +727,60 @@ int plugin_load(const char* plugin, const void* parameter)
{
int rc, i;
struct plugin_header *hdr;
#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
void *pd;
#else /* PLATFOR_NATIVE */
int fd;
ssize_t readsize;
#if NUM_CORES > 1
unsigned my_core;
#endif
#endif /* CONFIG_PLATFORM */
#if LCD_DEPTH > 1
fb_data* old_backdrop;
#endif
if (pfn_tsr_exit != NULL) /* if we have a resident old plugin: */
{
if (current_plugin_handle && pfn_tsr_exit)
{ /* if we have a resident old plugin and a callback */
if (pfn_tsr_exit(!strcmp(current_plugin, plugin)) == false )
{
/* not allowing another plugin to load */
return PLUGIN_OK;
}
pfn_tsr_exit = NULL;
plugin_loaded = false;
lc_close(current_plugin_handle);
current_plugin_handle = pfn_tsr_exit = NULL;
}
splash(0, ID2P(LANG_WAIT));
strcpy(current_plugin, plugin);
#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
hdr = sim_plugin_load((char *)plugin, &pd);
if (pd == NULL) {
current_plugin_handle = lc_open(plugin, pluginbuf, PLUGIN_BUFFER_SIZE);
if (current_plugin_handle == NULL) {
splashf(HZ*2, str(LANG_PLUGIN_CANT_OPEN), plugin);
return -1;
}
hdr = lc_get_header(current_plugin_handle);
if (hdr == NULL
|| hdr->magic != PLUGIN_MAGIC
|| hdr->target_id != TARGET_ID) {
sim_plugin_close(pd);
splash(HZ*2, str(LANG_PLUGIN_WRONG_MODEL));
return -1;
}
if (hdr->api_version > PLUGIN_API_VERSION
|| hdr->api_version < PLUGIN_MIN_API_VERSION) {
sim_plugin_close(pd);
splash(HZ*2, str(LANG_PLUGIN_WRONG_VERSION));
return -1;
}
#else
fd = open(plugin, O_RDONLY);
if (fd < 0) {
splashf(HZ*2, str(LANG_PLUGIN_CANT_OPEN), plugin);
return fd;
}
#if NUM_CORES > 1
/* Make sure COP cache is flushed and invalidated before loading */
my_core = switch_core(CURRENT_CORE ^ 1);
cpucache_invalidate();
switch_core(my_core);
#endif
readsize = read(fd, pluginbuf, PLUGIN_BUFFER_SIZE);
close(fd);
if (readsize < 0) {
splashf(HZ*2, str(LANG_READ_FAILED), plugin);
return -1;
}
hdr = (struct plugin_header *)pluginbuf;
if ((unsigned)readsize <= sizeof(struct plugin_header)
|| hdr->magic != PLUGIN_MAGIC
|| hdr->target_id != TARGET_ID
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
|| hdr->load_addr != pluginbuf
|| hdr->end_addr > pluginbuf + PLUGIN_BUFFER_SIZE) {
|| hdr->end_addr > pluginbuf + PLUGIN_BUFFER_SIZE
#endif
)
{
lc_close(current_plugin_handle);
splash(HZ*2, str(LANG_PLUGIN_WRONG_MODEL));
return -1;
}
if (hdr->api_version > PLUGIN_API_VERSION
|| hdr->api_version < PLUGIN_MIN_API_VERSION) {
|| hdr->api_version < PLUGIN_MIN_API_VERSION)
{
lc_close(current_plugin_handle);
splash(HZ*2, str(LANG_PLUGIN_WRONG_VERSION));
return -1;
}
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
plugin_size = hdr->end_addr - pluginbuf;
/* zero out bss area only, above guards end of pluginbuf */
if (plugin_size > readsize)
memset(pluginbuf + readsize, 0, plugin_size - readsize);
#else
plugin_size = 0;
#endif
*(hdr->api) = &rockbox_api;
plugin_loaded = true;
#if defined HAVE_LCD_BITMAP && LCD_DEPTH > 1
old_backdrop = lcd_get_backdrop();
@ -834,8 +795,6 @@ int plugin_load(const char* plugin, const void* parameter)
FOR_NB_SCREENS(i)
viewportmanager_theme_enable(i, false, NULL);
cpucache_invalidate();
#ifdef HAVE_TOUCHSCREEN
touchscreen_set_mode(TOUCHSCREEN_BUTTON);
@ -847,6 +806,12 @@ int plugin_load(const char* plugin, const void* parameter)
rc = hdr->entry_point(parameter);
if (!pfn_tsr_exit)
{ /* close handle if plugin is no tsr one */
lc_close(current_plugin_handle);
current_plugin_handle = NULL;
}
/* Go back to the global setting in case the plugin changed it */
#ifdef HAVE_TOUCHSCREEN
touchscreen_set_mode(global_settings.touch_mode);
@ -887,11 +852,8 @@ int plugin_load(const char* plugin, const void* parameter)
FOR_NB_SCREENS(i)
viewportmanager_theme_undo(i, false);
if (pfn_tsr_exit == NULL)
plugin_loaded = false;
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
if(open_files != 0 && !plugin_loaded)
if(open_files != 0 && !current_plugin_handle)
{
int fd;
logf("Plugin '%s' leaks file handles", plugin);
@ -909,8 +871,6 @@ int plugin_load(const char* plugin, const void* parameter)
}
#endif
sim_plugin_close(pd);
if (rc == PLUGIN_ERROR)
splash(HZ*2, str(LANG_PLUGIN_ERROR));
@ -923,7 +883,7 @@ void* plugin_get_buffer(size_t *buffer_size)
{
int buffer_pos;
if (plugin_loaded)
if (current_plugin_handle)
{
if (plugin_size >= PLUGIN_BUFFER_SIZE)
return NULL;

View file

@ -278,6 +278,7 @@ SECTIONS
.bss (NOLOAD) :
{
plugin_bss_start = .;
_plugin_bss_start = .;
*(.bss*)
#if defined(IRAMSIZE) && IRAMSIZE == 0
*(.ibss)

View file

@ -32,6 +32,8 @@ PLUGIN_HEADER
#define EXIT_MAGIC 0x0CDEBABE
extern enum plugin_status plugin_start(const void*);
extern unsigned char plugin_bss_start[];
extern unsigned char plugin_end_addr[];
static jmp_buf __exit_env;
/* only 1 atexit handler for now, chain in the exit handler if you need more */
@ -61,6 +63,10 @@ enum plugin_status plugin__start(const void *param)
int exit_ret;
enum plugin_status ret;
/* zero out the bss section */
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
rb->memset(plugin_bss_start, 0, plugin_end_addr - plugin_bss_start);
#endif
/* we come back here if exit() was called or the plugin returned normally */
exit_ret = setjmp(__exit_env);
if (exit_ret == 0)

View file

@ -3,6 +3,7 @@ events.c
backlight.c
buffer.c
general.c
load_code.c
powermgmt.c
system.c
usb.c

View file

@ -0,0 +1,55 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 by Thomas Martitz
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
#include "system.h"
extern void *lc_open(const char *filename, char *buf, size_t buf_size);
/* header is always at the beginning of the blob, and handle actually points
* to the start of the blob */
static inline char *lc_open_from_mem(void* addr, size_t blob_size)
{
(void)blob_size;
cpucache_invalidate();
return addr;
}
static inline void *lc_get_header(void *handle) { return handle; }
/* no need to do anything */
static inline void lc_close(void *handle) { (void)handle; }
#elif (CONFIG_PLATFORM & PLATFORM_HOSTED)
/* don't call these directly for loading code
* they're to be wrapped by platform specific functions */
extern void *_lc_open(const char *filename, char *buf, size_t buf_size);
extern void *_lc_get_header(void *handle);
extern void _lc_close(void *handle);
extern void *lc_open(const char *filename, char *buf, size_t buf_size);
/* not possiible on hosted platforms */
extern void *lc_open_from_mem(void *addr, size_t blob_size);
extern void *lc_get_header(void *handle);
extern void lc_close(void *handle);
extern const char* lc_last_error(void);
#endif

165
firmware/load_code.c Normal file
View file

@ -0,0 +1,165 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 by Thomas Martitz
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "system.h"
#include "file.h"
#include "debug.h"
#include "load_code.h"
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
/* load binary blob from disk to memory, returning a handle */
void * lc_open(const char *filename, char *buf, size_t buf_size)
{
int fd = open(filename, O_RDONLY);
ssize_t read_size;
if (fd < 0)
{
DEBUGF("Could not open file");
return NULL;
}
#if NUM_CORES > 1
/* Make sure COP cache is flushed and invalidated before loading */
{
int my_core = switch_core(CURRENT_CORE ^ 1);
cpucache_invalidate();
switch_core(my_core);
}
#endif
read_size = read(fd, buf, buf_size);
close(fd);
cpucache_invalidate();
if (read_size < 0)
{
DEBUGF("Could not read from file");
return NULL;
}
return buf;
}
#elif (CONFIG_PLATFORM & PLATFORM_HOSTED)
/* libdl wrappers */
#ifdef WIN32
/* win32 */
#include <windows.h>
#define dlopen(_x_, _y_) LoadLibraryW(_x_)
#define dlsym(_x_, _y_) (void *)GetProcAddress(_x_, _y_)
#define dlclose(_x_) FreeLibrary(_x_)
static inline char *_dlerror(void)
{
static char err_buf[64];
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
err_buf, sizeof(err_buf), NULL);
return err_buf;
}
#define dlerror _dlerror
#else
/* unix */
#include <dlfcn.h>
#define O_BINARY 0
#endif
#include <stdio.h>
#include "rbpaths.h"
#include "general.h"
void * _lc_open(const char *filename, char *buf, size_t buf_size)
{
(void)buf;
(void)buf_size;
void* dl_handle = dlopen(filename, RTLD_NOW);
return dl_handle;
}
void *lc_open_from_mem(void *addr, size_t blob_size)
{
int fd, i;
char temp_filename[MAX_PATH];
/* We have to create the dynamic link library file from ram so we
can simulate the codec loading. With voice and crossfade,
multiple codecs may be loaded at the same time, so we need
to find an unused filename */
for (i = 0; i < 10; i++)
{
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
/* we need that path fixed, since get_user_file_path()
* gives us the folder on the sdcard where we cannot load libraries
* from (no exec permissions)
*/
snprintf(temp_filename, sizeof(temp_filename),
"/data/data/org.rockbox/app_rockbox/libtemp_binary_%d.so", i);
#else
char name[MAX_PATH];
const char *_name = get_user_file_path(ROCKBOX_DIR, NEED_WRITE, name, sizeof(name));
snprintf(temp_filename, sizeof(temp_filename),
"%slibtemp_binary_%d.dll", _name, i);
#endif
fd = open(temp_filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0766);
if (fd >= 0)
break; /* Created a file ok */
}
DEBUGF("Creating %s\n", temp_filename);
if (fd < 0)
{
DEBUGF("open failed\n");
return NULL;
}
if (write(fd, addr, blob_size) < (ssize_t)blob_size)
{
DEBUGF("Write failed\n");
close(fd);
remove(temp_filename);
return NULL;
}
close(fd);
return lc_open(temp_filename, NULL, 0);
}
void *_lc_get_header(void *handle)
{
char *ret = dlsym(handle, "__header");
if (ret == NULL)
ret = dlsym(handle, "___header");
return ret;
}
void _lc_close(void *handle)
{
if (handle)
dlclose(handle);
}
const char *lc_last_error(void)
{
return dlerror();
}
#endif

View file

@ -58,6 +58,7 @@
#include "debug.h"
#include "ata.h" /* for IF_MV2 et al. */
#include "rbpaths.h"
#include "load_code.h"
/* keep this in sync with file.h! */
#undef MAX_PATH /* this avoids problems when building simulator */
@ -520,6 +521,8 @@ int sim_fsync(int fd)
#endif
}
#ifndef __PCTOOL__
#ifdef WIN32
/* sim-win32 */
#define dlopen(_x_, _y_) LoadLibraryW(UTF8_TO_OS(_x_))
@ -530,118 +533,31 @@ int sim_fsync(int fd)
#include <dlfcn.h>
#endif
void *sim_codec_load_ram(char* codecptr, int size, void **pd)
void *lc_open(const char *filename, char *buf, size_t buf_size)
{
void *hdr;
char path[MAX_PATH];
int fd;
int codec_count;
#ifdef WIN32
char buf[MAX_PATH];
#endif
const char *sim_path = get_sim_pathname(filename);
void *handle = _lc_open((const char*)UTF8_TO_OS(sim_path), buf, buf_size);
*pd = NULL;
/* We have to create the dynamic link library file from ram so we
can simulate the codec loading. With voice and crossfade,
multiple codecs may be loaded at the same time, so we need
to find an unused filename */
for (codec_count = 0; codec_count < 10; codec_count++)
if (handle == NULL)
{
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
/* we need that path fixed, since get_user_file_path()
* gives us the folder on the sdcard where we cannot load libraries
* from (no exec permissions)
*/
snprintf(path, sizeof(path),
"/data/data/org.rockbox/app_rockbox/libtemp_codec_%d.so",
codec_count);
#else
char name[MAX_PATH];
const char *_name = get_user_file_path(ROCKBOX_DIR, 0, name, sizeof(name));
snprintf(path, sizeof(path), "%s/_temp_codec%d.dll", get_sim_pathname(_name), codec_count);
#endif
fd = OPEN(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRWXU);
if (fd >= 0)
break; /* Created a file ok */
DEBUGF("failed to load %s\n", filename);
DEBUGF("lc_open(%s): %s\n", filename, lc_last_error());
}
if (fd < 0)
{
DEBUGF("failed to open for write: %s\n", path);
return NULL;
}
if (write(fd, codecptr, size) != size)
{
DEBUGF("write failed");
return NULL;
}
close(fd);
/* Now load the library. */
*pd = dlopen(path, RTLD_NOW);
if (*pd == NULL)
{
DEBUGF("failed to load %s\n", path);
#ifdef WIN32
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
buf, sizeof buf, NULL);
DEBUGF("dlopen(%s): %s\n", path, buf);
#else
DEBUGF("dlopen(%s): %s\n", path, dlerror());
#endif
return NULL;
}
hdr = dlsym(*pd, "__header");
if (!hdr)
hdr = dlsym(*pd, "___header");
return hdr; /* maybe NULL if symbol not present */
return handle;
}
void sim_codec_close(void *pd)
void *lc_get_header(void *handle)
{
dlclose(pd);
return _lc_get_header(handle);
}
void *sim_plugin_load(char *plugin, void **pd)
void lc_close(void *handle)
{
void *hdr;
char path[MAX_PATH];
#ifdef WIN32
char buf[MAX_PATH];
#endif
snprintf(path, sizeof(path), "%s", get_sim_pathname(plugin));
*pd = NULL;
*pd = dlopen(path, RTLD_NOW);
if (*pd == NULL) {
DEBUGF("failed to load %s\n", plugin);
#ifdef WIN32
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
buf, sizeof(buf), NULL);
DEBUGF("dlopen(%s): %s\n", path, buf);
#else
DEBUGF("dlopen(%s): %s\n", path, dlerror());
#endif
return NULL;
}
hdr = dlsym(*pd, "__header");
if (!hdr)
hdr = dlsym(*pd, "___header");
return hdr; /* maybe NULL if symbol not present */
}
void sim_plugin_close(void *pd)
{
dlclose(pd);
_lc_close(handle);
}
#endif /* __PCTOOL__ */
#ifdef WIN32
static unsigned old_cp;