executor/src/unix_like.c

468 lines
11 KiB
C

#include "rsys/common.h"
/*
* This file is a quick hack to hoist code from config/os/linux to where it
* can be shared with the Mac OS X port.
*
* Eventually everything should be rejiggered to use the GNU build system.
*/
#if defined (LINUX) || defined (MACOSX)
#include <sys/types.h>
#include <sys/mman.h>
#include "rsys/os.h"
#include "rsys/memsize.h"
#include "rsys/mman.h"
#include "rsys/system_error.h"
#include "rsys/memory_layout.h"
#include "rsys/lowglobals.h"
#include "Gestalt.h"
#include "SegmentLdr.h"
#include "rsys/gestalt.h"
#include "rsys/lockunlock.h"
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#if defined (MACOSX)
// The code in here should work for Mac OS X as well as Linux, although
// in Mac OS X they use MAP_ANON instead of MAP_ANONYMOUS
#warning "Fix this, the code isn't Linux specific"
#define MAP_ANONYMOUS MAP_ANON
#endif
static void
my_fault_proc( int sig )
{
// FIXME: Change this to an internal Executor dialog
fprintf(stderr, "Unexpected Application Failure\n");
// If we are already in the browser, does this exit the program?
C_ExitToShell ();
}
static int except_list[] = { SIGSEGV, SIGBUS, 0 };
void
install_exception_handler (void)
{
int i;
for ( i=0; except_list[i]; ++i )
{
signal(except_list[i], my_fault_proc);
}
}
void
uninstall_exception_handler (void)
{
int i;
for ( i=0; except_list[i]; ++i )
{
signal(except_list[i], SIG_DFL);
}
}
static unsigned long
physical_memory (void)
{
FILE *fp;
unsigned long mem;
mem = 0;
fp = fopen ("/proc/meminfo", "r");
if (fp)
{
char buf[256];
while (fgets (buf, sizeof buf - 1, fp))
if (!strncmp (buf, "Mem:", 4) && sscanf (buf + 4, "%lu", &mem))
break;
fclose (fp);
}
replace_physgestalt_selector (gestaltPhysicalRAMSize, mem);
return mem;
}
static void
guess_good_memory_settings (void)
{
unsigned long new_appl_size;
new_appl_size = physical_memory () / 4;
#if defined (powerpc) || defined (__ppc__)
/* This hack prevents Photoshop 5.5 demo from complaining that we don't
have enough memory when we run on a 64 MB Linux machine. Our division
by four is a bit naive above, so there's really no harm, other than
ugliness, to this hack. */
{
enum { PHOTOSHOP_55_PREFERRED_SIZE = 16584 * 1024 };
if (new_appl_size < PHOTOSHOP_55_PREFERRED_SIZE &&
new_appl_size >= PHOTOSHOP_55_PREFERRED_SIZE * 8 / 10)
new_appl_size = PHOTOSHOP_55_PREFERRED_SIZE;
}
#endif
if (new_appl_size > ROMlib_applzone_size)
ROMlib_applzone_size = MIN (MAX_APPLZONE_SIZE, new_appl_size);
}
boolean_t
os_init (void)
{
guess_good_memory_settings ();
#if defined (SDL)
install_exception_handler ();
#endif
return TRUE;
}
PUBLIC int
ROMlib_lockunlockrange (int fd, uint32 begin, uint32 count, lockunlock_t op)
{
int retval;
struct flock flock;
warning_trace_info ("fd = %d, begin = %d, count = %d, op = %d",
fd, begin, count, op);
retval = noErr;
switch (op)
{
case lock:
flock.l_type = F_WRLCK;
break;
case unlock:
flock.l_type = F_UNLCK;
break;
default:
warning_unexpected ("op = %d", op);
retval = paramErr;
break;
}
if (retval == noErr)
{
boolean_t success;
flock.l_whence = SEEK_SET;
flock.l_start = begin;
flock.l_len = count;
success = fcntl (fd, F_SETLK, &flock) != -1;
if (success)
retval = noErr;
else
{
switch (errno)
{
case EAGAIN:
case EACCES:
retval = fLckdErr;
break;
#if 0
case ERROR_NOT_LOCKED:
retval = afpRangeNotLocked;
break;
#endif
#if 0
case ERROR_LOCK_FAILED:
retval = afpRangeOverlap;
break;
#endif
default:
warning_unexpected ("errno = %d", errno);
retval = noErr;
break;
}
}
}
return retval;
}
PUBLIC int
ROMlib_launch_native_app (int n_filenames, char **filenames)
{
char **v;
v = alloca (sizeof *v * (n_filenames + 1));
memcpy (v, filenames, n_filenames * sizeof *v);
v[n_filenames] = 0;
if (fork () == 0)
execv (filenames[0], v);
return 0;
}
/*
* There is a very bad problem associated with the use of the db
* shared libraries under Linux. Specifically, the calling convention
* for functions which return structs that are larger than 32 bits
* somehow got changed when some of the Linux distributions switched
* from gcc to egcs. Both compilers put an extra pointer on the stack
* before calling the routine that returns the large struct, but gcc
* expects the caller to pop that extra pointer, where egcs expects
* the called to pop it. This means that if you compile the caller
* with gcc and call a shared library that was called with egcs, the
* stack pointer will be off by 4 bytes after the function returns and
* the stack is adjusted. That can be a catastrophe if further code
* expects the stack to be correct after adjustments. On the other
* hand, if we make the questionable call and then do nothing else,
* the "leave" instruction will restore the stack pointer by using the
* frame pointer and we'll never be bothered by the extra pop.
*
* So, as a workaround, we can wrap the routines, then check the
* assembly code that the compiler produces to make sure that it's
* tolerant of the error, then call the wrappers. That makes the
* wrapper routines look like voodoo code that was written by a
* superstitious programmer, but the code (or some other workaround)
* is absolutely necessary because we want to have one Executor
* executable that can run with both the shared libraries from Red Hat
* 5.2 as well as the new ones in SuSE 6.0 (and the new ones that will
* probably be in Red Hat 6.0).
*
* These wrappers absolutely have to be compiled with enough
* optimization so that the stack isn't adjusted before the leave
* instruction. If it is adjusted, then any interrupt that occurs
* between the adjustment and the transfering of the data from the
* temporary stack space to the address passed will cause corruption.
*
* If you do not understand the above, or if you disagree with it,
* please contact Cliff before changing the following code. Axing the
* code alone and then testing the result is not sufficient, unless
* you're sure that your test involves *both* db shared libaries.
*
*/
PUBLIC void
_dbm_fetch (datum *datump, DBM *db, datum datum)
{
*datump = dbm_fetch (db, datum);
}
PUBLIC void
_dbm_firstkey (datum *datump, DBM *db)
{
*datump = dbm_firstkey (db);
}
PUBLIC void
_dbm_nextkey (datum *datump, DBM *db)
{
*datump = dbm_nextkey (db);
}
PUBLIC boolean_t host_has_spfcommon (void)
{
return false;
}
PUBLIC boolean_t
host_spfcommon (host_spf_reply_block *replyp, const char *prompt,
const char *incoming_filename, void *fp, void *filef, int numt,
void *tl, getorput_t getorput, sf_flavor_t flavor,
void *activeList, void *activateproc, void *yourdatap)
{
return false;
}
void
mmap_lowglobals (void)
{
if (!force_big_offset)
{
caddr_t addr;
addr = mmap ((caddr_t) PAGE_ZERO_START,
PAGE_ZERO_SIZE,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, -1, 0);
gui_assert (addr == (caddr_t) PAGE_ZERO_START);
}
}
#if !defined (MACOSX)
#if !defined (powerpc) && !defined (__ppc__)
PRIVATE caddr_t
round_up_to_page_size (unsigned long addr)
{
caddr_t retval;
size_t page_size;
page_size = getpagesize ();
retval = (caddr_t) ((addr + page_size - 1) / page_size * page_size);
return retval;
}
#endif
static jmp_buf segv_return;
static void
segv_handler (int signum_ignored __attribute__((unused)))
{
siglongjmp (segv_return, 1);
}
static bool
mmap_conflict (void *start, size_t length)
{
bool retval;
long page_size;
retval = false;
page_size = sysconf(_SC_PAGESIZE);
if ((long) start % page_size != 0)
{
retval = true;
warning_unexpected ("start = %p, page_size = %ld, "
"start %% page_size = %ld",
start, page_size, (long) start % page_size);
}
else if (length % page_size != 0)
{
retval = true;
warning_unexpected ("length = %ld, page_size = %ld, "
"length %% page_size = %ld",
(long) length, page_size, (long) length % page_size);
}
else
{
sig_t old_segv_handler;
volatile int n_pages;
volatile int n_failures;
volatile char *volatile addr;
char *stop;
n_pages = 0;
n_failures = 0;
stop = (char *) start + length;
old_segv_handler = signal (SIGSEGV, segv_handler);
for (addr = start; addr < stop; addr += page_size)
{
++n_pages;
if (sigsetjmp (segv_return, 1) != 0)
++n_failures;
else
*addr;
}
signal (SIGSEGV, old_segv_handler);
retval = n_failures < n_pages;
if (retval)
warning_unexpected ("%d pages were already mapped",
n_pages - n_failures);
}
return retval;
}
#endif /* defined (MACOSX) */
/*
* This code used to try to get us memory that we could use directly (meaning
* it either included page 0 or was adjacent to page 0 and we could use some
* other trick to get page 0) without having to offset the emulated addresses.
*
* I doubt it works on many (any?) platforms anymore, and since speed of
* emulation isn't an issue, we really shouldn't care.
*/
void *
mmap_permanent_memory (unsigned long amount_wanted)
{
#if defined(MACOSX)
return NULL;
#else
caddr_t addr_got;
caddr_t badness_start;
/* Only do this if our text segment is up nice and high out of the way. */
if (((unsigned long) mmap_permanent_memory & 0xFF000000L) == 0)
return NULL;
{
extern void *_start;
badness_start = (caddr_t) ((unsigned long) &_start
/ (1024 * 1024) * (1024 * 1024));
}
#if !defined (powerpc) && !defined (__ppc__)
{
caddr_t addr_wanted;
addr_wanted = round_up_to_page_size (PAGE_ZERO_START + PAGE_ZERO_SIZE);
if (addr_wanted + amount_wanted > badness_start)
{
warning_unexpected ("addr_wanted = %p, amount_wanted = 0x%lx, "
"badness_start = %p", addr_wanted, amount_wanted,
badness_start);
return NULL;
}
if (mmap_conflict (addr_wanted, amount_wanted))
addr_got = NULL;
else
{
addr_got = mmap (addr_wanted, amount_wanted, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, -1, 0);
if (addr_got == (caddr_t) -1)
addr_got = NULL;
else if (addr_got != addr_wanted)
warning_unexpected ("addr_wanted = %p, addr_got = %p",
addr_wanted, addr_got);
}
}
#else
#warning THIS CODE IS PROBABLY WRONG
/*
* I haven't tested a powerpc build in a while, but I just noticed that
* we're trying to mmap from 0 and then we're returning addr_got. I think
* that when we return 0, the caller believes that we weren't able to
* mmap the low globals. As such, the code below PROBABLY DOESN'T DO
* ANYTHING DIFFERENT THAN SIMPLY RETURNING NULL.
*/
if (amount_wanted > badness_start)
{
warning_unexpected ("amount_wanted = 0x%x, badness_start = %p",
amount_wanted, badness_start);
addr_got = NULL;
}
else
addr_got = mmap (0, amount_wanted, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (addr_got == (caddr_t) -1)
addr_got = NULL;
#endif
return addr_got;
#endif
}
#endif /* defined (LINUX) || defined (MACOSX) */