Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Forgot to add lib2ine.c to revision control. :/
- Loading branch information
Showing
1 changed file
with
387 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,387 @@ | ||
/** | ||
* 2ine; an OS/2 emulator for Linux. | ||
* | ||
* Please see the file LICENSE.txt in the source's root directory. | ||
* | ||
* This file written by Ryan C. Gordon. | ||
*/ | ||
|
||
#define _DARWIN_C_SOURCE 1 | ||
#define _POSIX_C_SOURCE 199309 | ||
#define _DEFAULT_SOURCE | ||
#define _GNU_SOURCE | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <errno.h> | ||
#include <sys/types.h> | ||
#include <sys/stat.h> | ||
#include <fcntl.h> | ||
#include <pthread.h> | ||
#include <sys/time.h> | ||
#include <sys/resource.h> | ||
|
||
#include "lib2ine.h" | ||
|
||
extern char **environ; | ||
|
||
LxLoaderState GLoaderState; | ||
|
||
static pthread_key_t tlskey; | ||
|
||
static void *readfile(const char *fname, size_t *_len) | ||
{ | ||
const size_t chunklen = 128; | ||
const int fd = open(fname, O_RDONLY); | ||
if (fd == -1) { | ||
fprintf(stderr, "Can't open '%s': %s\n", fname, strerror(errno)); | ||
return NULL; | ||
} | ||
|
||
char *buf = NULL; | ||
size_t bw = 0; | ||
|
||
while (1) { | ||
buf = (char *) realloc(buf, bw + chunklen); | ||
if (!buf) { | ||
fprintf(stderr, "Out of memory\n"); | ||
free(buf); | ||
close(fd); | ||
return NULL; | ||
} | ||
|
||
const ssize_t br = read(fd, buf + bw, chunklen); | ||
if (br < 0) { | ||
fprintf(stderr, "Read fail on '%s': %s\n", fname, strerror(errno)); | ||
free(buf); | ||
close(fd); | ||
return NULL; | ||
} | ||
|
||
bw += br; | ||
if (br < chunklen) { | ||
break; | ||
} | ||
} | ||
|
||
close(fd); | ||
*_len = bw; | ||
return buf; | ||
} | ||
|
||
static void initPib(void) | ||
{ | ||
LxPIB *pib = &GLoaderState.pib; | ||
memset(pib, '\0', sizeof (*pib)); | ||
|
||
size_t cmdlinebuflen = 0; | ||
char *cmdlinebuf = readfile("/proc/self/cmdline", &cmdlinebuflen); | ||
if (!cmdlinebuf) { | ||
abort(); | ||
} | ||
|
||
int argc = 0; | ||
for (size_t i = 0; i < cmdlinebuflen; i++) { | ||
if (cmdlinebuf[i] == '\0') { | ||
argc++; | ||
} | ||
} | ||
|
||
char **argv = (char **) calloc(argc + 1, sizeof (char *)); | ||
if (!argv) { | ||
fprintf(stderr, "Out of memory\n"); | ||
abort(); | ||
} | ||
|
||
argv[0] = cmdlinebuf; | ||
argc = 0; | ||
for (size_t i = 0; i < cmdlinebuflen; i++) { | ||
if (cmdlinebuf[i] == '\0') { | ||
argc++; | ||
argv[argc] = &cmdlinebuf[i + 1]; | ||
} | ||
} | ||
argv[argc] = NULL; | ||
|
||
|
||
// !!! FIXME: HUGE HACK | ||
int is_lx_loader = 0; | ||
{ | ||
const char *ptr = strrchr(argv[0], '/'); | ||
if (ptr) { ptr++; } else { ptr = argv[0]; } | ||
is_lx_loader = (strcmp(ptr, "lx_loader") == 0); | ||
} | ||
|
||
if (is_lx_loader) { | ||
argv++; | ||
argc--; | ||
} | ||
|
||
|
||
// !!! FIXME: this is incomplete. | ||
|
||
// Eventually, the environment table looks like this (double-null to terminate list): var1=a\0var2=b\0var3=c\0\0 | ||
// The command line looks like this: \0argv0\0argv1 argv2 argvN\0\0 | ||
// Between env and cmd is the exe name: argv0\0 | ||
|
||
size_t len = 1; | ||
for (int i = 0; i < argc; i++) { | ||
int needs_escape = 0; | ||
int num_backslashes = 0; | ||
for (const char *arg = argv[i]; *arg; arg++) { | ||
const char ch = *arg; | ||
if ((ch == ' ') || (ch == '\t')) | ||
needs_escape = 1; | ||
else if ((ch == '\\') || (ch == '\"')) | ||
num_backslashes++; | ||
len++; | ||
} // for | ||
|
||
if (needs_escape) { | ||
len += 2 + num_backslashes; | ||
} // if | ||
|
||
len++; // terminator | ||
} // for | ||
|
||
len += strlen(argv[0]) + 1; // for the exe name. | ||
|
||
char **envp = environ; | ||
|
||
const char *default_os2path = "PATH=C:\\home\\icculus\\Dropbox\\emx\\bin;C:\\WATCOM\\binp;C:\\home\\icculus"; // !!! FIXME: noooooope. | ||
for (int i = 0; envp[i]; i++) { | ||
const char *str = envp[i]; | ||
if (strncmp(str, "PATH=", 5) == 0) { | ||
if (!GLoaderState.subprocess) | ||
str = default_os2path; | ||
} else if (strncmp(str, "IS_2INE=", 8) == 0) { | ||
continue; | ||
} // if | ||
len += strlen(str) + 1; | ||
} // for | ||
|
||
len += 4; // null terminators. | ||
|
||
char *env = (char *) malloc(len); | ||
if (!env) { | ||
fprintf(stderr, "Out of memory\n"); | ||
abort(); | ||
} // if | ||
|
||
const char *libpath = NULL; | ||
char *ptr = env; | ||
for (int i = 0; envp[i]; i++) { | ||
const char *str = envp[i]; | ||
if (strncmp(str, "PATH=", 5) == 0) { | ||
if (!GLoaderState.subprocess) | ||
str = default_os2path; | ||
} else if (strncmp(str, "LIBPATH=", 8) == 0) { | ||
libpath = str + 8; | ||
} else if (strncmp(str, "IS_2INE=", 8) == 0) { | ||
continue; | ||
} // if | ||
|
||
strcpy(ptr, str); | ||
ptr += strlen(str) + 1; | ||
} | ||
*(ptr++) = '\0'; | ||
|
||
// we keep a copy of LIBPATH for undocumented API | ||
// DosQueryHandleInfo(..., QHINF_LIBPATH, ...). I guess in case the app | ||
// has changed the environment var after start? Java uses this. | ||
if (libpath == NULL) | ||
libpath = ""; | ||
|
||
GLoaderState.libpath = strdup(libpath); | ||
GLoaderState.libpathlen = strlen(libpath) + 1; | ||
|
||
// put the exe name between the environment and the command line. | ||
strcpy(ptr, argv[0]); | ||
ptr += strlen(argv[0]) + 1; | ||
|
||
char *cmd = ptr; | ||
strcpy(ptr, argv[0]); | ||
ptr += strlen(argv[0]); | ||
*(ptr++) = '\0'; | ||
for (int i = 1; i < argc; i++) { | ||
int needs_escape = 0; | ||
for (const char *arg = argv[i]; *arg; arg++) { | ||
const char ch = *arg; | ||
if ((ch == ' ') || (ch == '\t')) | ||
needs_escape = 1; | ||
} // for | ||
|
||
if (needs_escape) { | ||
*(ptr++) = '"'; | ||
for (const char *arg = argv[i]; *arg; arg++) { | ||
const char ch = *arg; | ||
if ((ch == '\\') || (ch == '\n')) | ||
*(ptr++) = '\\'; | ||
*(ptr++) = ch; | ||
} // for | ||
*(ptr++) = '"'; | ||
} else { | ||
for (const char *arg = argv[i]; *arg; arg++) | ||
*(ptr++) = *arg; | ||
} // if | ||
|
||
if (i < (argc-1)) | ||
*(ptr++) = ' '; | ||
} // for | ||
|
||
*(ptr++) = '\0'; | ||
|
||
pib->pib_ulpid = (uint32) getpid(); | ||
pib->pib_ulppid = (uint32) getppid(); | ||
pib->pib_pchcmd = cmd; | ||
pib->pib_pchenv = env; | ||
//pib->pib_hmte is filled in later during loadLxModule() | ||
// !!! FIXME: uint32 pib_flstatus; | ||
// !!! FIXME: uint32 pib_ultype; | ||
|
||
if (is_lx_loader) { | ||
argv--; | ||
} | ||
|
||
free(cmdlinebuf); | ||
free(argv); | ||
} // initPib | ||
|
||
static void dosExit_lib2ine(uint32 action, uint32 result) {} | ||
|
||
static void initOs2Tib_lib2ine(uint8 *tibspace, void *_topOfStack, const size_t stacklen, const uint32 tid) | ||
{ | ||
uint8 *topOfStack = (uint8 *) _topOfStack; | ||
|
||
LxTIB *tib = (LxTIB *) tibspace; | ||
LxTIB2 *tib2 = (LxTIB2 *) (tib + 1); | ||
|
||
FIXME("This is probably 50% wrong"); | ||
tib->tib_pexchain = NULL; | ||
tib->tib_pstack = topOfStack - stacklen; | ||
tib->tib_pstacklimit = (void *) topOfStack; | ||
tib->tib_ptib2 = tib2; | ||
tib->tib_version = 20; // !!! FIXME | ||
tib->tib_ordinal = 79; // !!! FIXME | ||
|
||
tib2->tib2_ultid = tid; | ||
tib2->tib2_ulpri = 512; | ||
tib2->tib2_version = 20; | ||
tib2->tib2_usMCCount = 0; | ||
tib2->tib2_fMCForceFlag = 0; | ||
} | ||
|
||
static uint16 setOs2Tib_lib2ine(uint8 *tibspace) | ||
{ | ||
pthread_setspecific(tlskey, (LxTIB *) tibspace); | ||
return 1; | ||
} | ||
|
||
static LxTIB *getOs2Tib_lib2ine(void) | ||
{ | ||
return (LxTIB *) pthread_getspecific(tlskey); | ||
} | ||
|
||
static void deinitOs2Tib_lib2ine(const uint16 selector) | ||
{ | ||
pthread_setspecific(tlskey, NULL); | ||
} | ||
|
||
static int findSelector_lib2ine(const uint32 addr, uint16 *outselector, uint16 *outoffset, int iscode) | ||
{ | ||
return 0; // always fail. This is for 16-bit support. | ||
} | ||
|
||
static void freeSelector_lib2ine(const uint16 selector) | ||
{ | ||
// no-op. This is for 16-bit support. | ||
} | ||
|
||
static void *convert1616to32_lib2ine(const uint32 addr1616) | ||
{ | ||
return NULL; // always fail. This is for 16-bit support. | ||
} | ||
|
||
static uint32 convert32to1616_lib2ine(void *addr32) | ||
{ | ||
return 0; // always fail. This is for 16-bit support. | ||
} | ||
|
||
static LxModule *loadModule_lib2ine(const char *modname) | ||
{ | ||
return NULL; // lx_loader will do OS/2 modules, we don't. | ||
// !!! FIXME: maybe this should dlopen() native modules instead? | ||
} | ||
|
||
static char *makeUnixPath_lib2ine(const char *os2path, uint32 *err) | ||
{ | ||
// this doesn't convert any OS/2 paths. For native apps, use native paths. | ||
char *retval = strdup(os2path); | ||
if (!retval) { | ||
*err = 8; //ERROR_NOT_ENOUGH_MEMORY; | ||
} | ||
return retval; | ||
} | ||
|
||
static void __attribute__((noreturn)) terminate_lib2ine(const uint32 exitcode) | ||
{ | ||
exit((int) exitcode); // let static constructors and atexit() run. | ||
} | ||
|
||
|
||
LX_NATIVE_CONSTRUCTOR(lib2ine) | ||
{ | ||
if (pthread_key_create(&tlskey, NULL) != 0) { | ||
fprintf(stderr, "Couldn't create TLS key\n"); | ||
abort(); | ||
} | ||
|
||
memset(&GLoaderState, '\0', sizeof (GLoaderState)); | ||
GLoaderState.dosExit = dosExit_lib2ine; | ||
GLoaderState.initOs2Tib = initOs2Tib_lib2ine; | ||
GLoaderState.setOs2Tib = setOs2Tib_lib2ine; | ||
GLoaderState.getOs2Tib = getOs2Tib_lib2ine; | ||
GLoaderState.deinitOs2Tib = deinitOs2Tib_lib2ine; | ||
GLoaderState.findSelector = findSelector_lib2ine; | ||
GLoaderState.freeSelector = freeSelector_lib2ine; | ||
GLoaderState.convert1616to32 = convert1616to32_lib2ine; | ||
GLoaderState.convert32to1616 = convert32to1616_lib2ine; | ||
GLoaderState.loadModule = loadModule_lib2ine; | ||
GLoaderState.makeUnixPath = makeUnixPath_lib2ine; | ||
GLoaderState.terminate = terminate_lib2ine; | ||
|
||
struct rlimit rlim; | ||
if (getrlimit(RLIMIT_STACK, &rlim) == -1) { | ||
rlim.rlim_cur = 8 * 1024 * 1024; // oh well. | ||
} | ||
GLoaderState.initOs2Tib(GLoaderState.main_tibspace, &rlim, rlim.rlim_cur, 0); // hopefully that's close enough on the stack address. | ||
GLoaderState.main_tib_selector = GLoaderState.setOs2Tib(GLoaderState.main_tibspace); | ||
|
||
if (getenv("TRACE_NATIVE")) { | ||
GLoaderState.trace_native = 1; | ||
} | ||
|
||
if (getenv("TRACE_EVENTS")) { | ||
GLoaderState.trace_events = 1; | ||
} | ||
|
||
GLoaderState.subprocess = (getenv("IS_2INE") != NULL); | ||
|
||
initPib(); | ||
|
||
GLoaderState.running = 1; | ||
} | ||
|
||
LX_NATIVE_DESTRUCTOR(lib2ine) | ||
{ | ||
free(GLoaderState.pib.pib_pchenv); | ||
memset(&GLoaderState, '\0', sizeof (GLoaderState)); | ||
pthread_key_delete(tlskey); | ||
} | ||
|
||
|
||
// end of lib2ine.c ... | ||
|
||
|