blob: 142c70367d69a825af5cf0a468af67bf007160cd [file] [log] [blame]
/*
* Selector manipulation functions
*
* Copyright 1993 Robert J. Amstadt
* Copyright 1995 Alexandre Julliard
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#ifndef WINELIB
#ifdef __linux__
#include <sys/mman.h>
#include <linux/unistd.h>
#include <linux/head.h>
#include <linux/mman.h>
#include <linux/a.out.h>
#include <linux/ldt.h>
#endif
#if defined(__NetBSD__) || defined(__FreeBSD__)
#include <sys/mman.h>
#include <machine/segments.h>
#endif
#include "windows.h"
#include "ldt.h"
#include "wine.h"
#include "global.h"
#include "dlls.h"
#include "neexe.h"
#include "if1632.h"
#include "prototypes.h"
#include "stddebug.h"
/* #define DEBUG_SELECTORS */
#include "debug.h"
#define MAX_ENV_SIZE 16384 /* Max. environment size (ought to be dynamic) */
static HANDLE EnvironmentHandle = 0;
WORD PSPSelector = 0;
#define MAX_SELECTORS (512 * 2)
int max_selectors = 0;
extern char WindowsPath[256];
extern char **Argv;
extern int Argc;
extern char **environ;
unsigned int
GetEntryPointFromOrdinal(struct w_files * wpnt, int ordinal);
/**********************************************************************
* Check whether pseudo-functions like __0040H for direct memory
* access are referenced and return 1 if so.
* FIXME: Reading and writing to the returned selectors has no effect
* (e.g. reading from the Bios data segment (esp. clock!) )
*/
unsigned int GetMemoryReference( char *dll_name, char *function,
WORD *sel, WORD *offset )
{
static HANDLE memory_handles[ 10 ] = { 0,0,0,0,0,0,0,0,0,0 };
static char *memory_names[ 10 ] = { "segment 0xA000",
"segment 0xB000",
"segment 0xB800",
"Bios-Rom",
"segment 0xD000",
"segment 0x0000",
"segment 0xE000",
"segment 0xF000",
"segment 0xC000",
"Bios data segment" };
short nr;
if( strcasecmp( dll_name, "KERNEL" ) )
return 0;
if( HIWORD( function ) ) {
if( ( *function != '_' ) || ( *(function+1) != '_' ) )
return 0;
if( !strcasecmp( function, "__A000H" ) ) nr = 0;
else if( !strcasecmp( function, "__B000H" ) ) nr = 1;
else if( !strcasecmp( function, "__B800H" ) ) nr = 2;
else if( !strcasecmp( function, "__ROMBIOS" ) ) nr = 3;
else if( !strcasecmp( function, "__D000H" ) ) nr = 4;
else if( !strcasecmp( function, "__0000H" ) ) nr = 5;
else if( !strcasecmp( function, "__E000H" ) ) nr = 6;
else if( !strcasecmp( function, "__F000H" ) ) nr = 7;
else if( !strcasecmp( function, "__C000H" ) ) nr = 8;
else if( !strcasecmp( function, "__0040H" ) ) nr = 9;
else if( !strcasecmp( function, "__AHIncr" ) ) {
*sel = *offset = __AHINCR;
return 1;
}
else if( !strcasecmp( function, "__AHShift" ) ) {
*sel = *offset = __AHSHIFT;
return 1;
}
else
return 0;
}
else {
switch( LOWORD( function ) ) {
case 174: nr = 0; break;
case 181: nr = 1; break;
case 182: nr = 2; break;
case 173: nr = 3; break;
case 179: nr = 4; break;
case 183: nr = 5; break;
case 190: nr = 6; break;
case 194: nr = 7; break;
case 195: nr = 8; break;
case 193: nr = 9; break;
case 114:
*sel = *offset = __AHINCR;
return 1;
case 113:
*sel = *offset = __AHSHIFT;
return 1;
default: return 0;
}
}
if( !memory_handles[ nr ] ) {
fprintf( stderr, "Warning: Direct access to %s!\n", memory_names[ nr ] );
memory_handles[ nr ] = GlobalAlloc( GMEM_FIXED, 65535 );
}
*sel = *offset = memory_handles[ nr ];
return 1;
}
/**********************************************************************
* GetEntryPointFromOrdinal
*/
union lookup{
struct entry_tab_header_s *eth;
struct entry_tab_movable_s *etm;
struct entry_tab_fixed_s *etf;
char * cpnt;
};
unsigned int GetEntryDLLName( char * dll_name, char * function,
WORD* sel, WORD *offset )
{
struct dll_table_s *dll_table;
struct w_files * wpnt;
char * cpnt;
int ordinal, j, len;
if( GetMemoryReference( dll_name, function, sel, offset ) )
return 0;
dll_table = FindDLLTable(dll_name);
if(dll_table) {
ordinal = FindOrdinalFromName(dll_table->dll_table,function);
if(!ordinal){
dprintf_module(stddeb,"GetEntryDLLName: %s.%s not found\n",
dll_name, function);
*sel = *offset = 0;
return -1;
}
*sel = dll_table->dll_table[ordinal].selector;
*offset = dll_table->dll_table[ordinal].offset;
#ifdef WINESTAT
dll_table->dll_table[ordinal].used++;
#endif
return 0;
};
/* We need a means of determining the ordinal for the function. */
/* Not a builtin symbol, look to see what the file has for us */
for(wpnt = wine_files; wpnt; wpnt = wpnt->next){
if(strcasecmp(wpnt->name, dll_name)) continue;
cpnt = wpnt->ne->nrname_table;
while(1==1){
if( ((int) cpnt) - ((int)wpnt->ne->nrname_table) >
wpnt->ne->ne_header->nrname_tab_length) return 1;
len = *cpnt++;
if(strncmp(cpnt, function, len) == 0) break;
cpnt += len + 2;
};
ordinal = *((unsigned short *) (cpnt + len));
j = GetEntryPointFromOrdinal(wpnt, ordinal);
*offset = LOWORD(j);
*sel = HIWORD(j);
return 0;
};
return 1;
}
unsigned int GetEntryDLLOrdinal( char * dll_name, int ordinal,
WORD *sel, WORD *offset )
{
struct dll_table_s *dll_table;
struct w_files * wpnt;
int j;
if( GetMemoryReference( dll_name, (char*)ordinal, sel, offset ) )
return 0;
dll_table = FindDLLTable(dll_name);
if(dll_table) {
*sel = dll_table->dll_table[ordinal].selector;
*offset = dll_table->dll_table[ordinal].offset;
#ifdef WINESTAT
dll_table->dll_table[ordinal].used++;
#endif
return 0;
};
/* Not a builtin symbol, look to see what the file has for us */
for(wpnt = wine_files; wpnt; wpnt = wpnt->next){
if(strcasecmp(wpnt->name, dll_name)) continue;
j = GetEntryPointFromOrdinal(wpnt, ordinal);
*offset = LOWORD(j);
*sel = HIWORD(j);
return 0;
};
return 1;
}
unsigned int
GetEntryPointFromOrdinal(struct w_files * wpnt, int ordinal)
{
union lookup entry_tab_pointer;
struct entry_tab_header_s *eth;
struct entry_tab_movable_s *etm;
struct entry_tab_fixed_s *etf;
int current_ordinal;
int i;
entry_tab_pointer.cpnt = wpnt->ne->lookup_table;
/*
* Let's walk through the table until we get to our entry.
*/
current_ordinal = 1;
while (1)
{
/*
* Read header for this bundle.
*/
eth = entry_tab_pointer.eth++;
if (eth->n_entries == 0)
return 0xffffffff; /* Yikes - we went off the end of the table */
if (eth->seg_number == 0)
{
current_ordinal += eth->n_entries;
if(current_ordinal > ordinal) return 0;
continue;
}
/*
* Read each of the bundle entries.
*/
for (i = 0; i < eth->n_entries; i++, current_ordinal++)
{
if (eth->seg_number >= 0xfe)
{
etm = entry_tab_pointer.etm++;
if (current_ordinal == ordinal)
{
return MAKELONG(etm->offset,
wpnt->ne->selector_table[etm->seg_number-1]);
}
}
else
{
etf = entry_tab_pointer.etf++;
if (current_ordinal == ordinal)
{
return MAKELONG( etf->offset[0] + ((int)etf->offset[1]<<8),
wpnt->ne->selector_table[eth->seg_number-1]);
}
}
}
}
}
WNDPROC GetWndProcEntry16( char *name )
{
WORD sel, offset;
GetEntryDLLName( "WINPROCS", name, &sel, &offset );
return (WNDPROC) MAKELONG( offset, sel );
}
/***********************************************************************
* GetDOSEnvironment (KERNEL.131)
*/
SEGPTR GetDOSEnvironment(void)
{
return WIN16_GlobalLock( EnvironmentHandle );
}
/**********************************************************************
* CreateEnvironment
*/
static HANDLE CreateEnvironment(void)
{
HANDLE handle;
char **e;
char *p;
handle = GlobalAlloc( GMEM_MOVEABLE, MAX_ENV_SIZE );
if (!handle) return 0;
p = (char *) GlobalLock( handle );
/*
* Fill environment with Windows path, the Unix environment,
* and program name.
*/
strcpy(p, "PATH=");
strcat(p, WindowsPath);
p += strlen(p) + 1;
for (e = environ; *e; e++)
{
if (strncasecmp(*e, "path", 4))
{
strcpy(p, *e);
p += strlen(p) + 1;
}
}
*p++ = '\0';
/*
* Display environment
*/
p = (char *) GlobalLock( handle );
dprintf_selectors(stddeb, "Environment at %p\n", p);
for (; *p; p += strlen(p) + 1) dprintf_selectors(stddeb, " %s\n", p);
return handle;
}
/**********************************************************************
* GetCurrentPDB (KERNEL.37)
*/
WORD GetCurrentPDB()
{
return PSPSelector;
}
/**********************************************************************
* CreatePSP
*/
static WORD CreatePSP(void)
{
HANDLE handle;
struct dos_psp_s *psp;
char *p1, *p2;
int i;
WORD sel, offset;
handle = GlobalAlloc( GMEM_MOVEABLE, sizeof(*psp) );
if (!handle) return 0;
psp = (struct dos_psp_s *) GlobalLock( handle );
/*
* Fill PSP
*/
psp->pspInt20 = 0x20cd;
psp->pspDispatcher[0] = 0x9a;
GetEntryDLLName( "KERNEL", "DOS3Call", &sel, &offset );
*(unsigned short *)&psp->pspDispatcher[1] = offset;
*(unsigned short *)&psp->pspDispatcher[3] = sel;
GetEntryDLLName( "KERNEL", "FatalAppExit", &sel, &offset );
psp->pspTerminateVector[0] = offset;
psp->pspTerminateVector[1] = sel;
psp->pspControlCVector[0] = offset;
psp->pspControlCVector[1] = sel;
psp->pspCritErrorVector[0] = offset;
psp->pspCritErrorVector[1] = sel;
psp->pspEnvironment = SELECTOROF( GetDOSEnvironment() );
p1 = psp->pspCommandTail;
for (i = 1; i < Argc; i++)
{
if ((int) ((int) p1 - (int) psp->pspCommandTail) +
strlen(Argv[i]) > 124)
break;
if (i != 1)
*p1++ = ' ';
for (p2 = Argv[i]; *p2 != '\0'; )
*p1++ = *p2++;
}
*p1 = '\0';
psp->pspCommandTailCount = strlen(psp->pspCommandTail);
return SELECTOROF( WIN16_GlobalLock( handle ) );
}
/**********************************************************************
* CreateSelectors
*/
unsigned short *CreateSelectors(struct w_files * wpnt)
{
int fd = wpnt->fd;
struct ne_segment_table_entry_s *seg_table = wpnt->ne->seg_table;
struct ne_header_s *ne_header = wpnt->ne->ne_header;
unsigned short auto_data_sel;
int old_length, file_image_length = 0;
int saved_old_length = 0;
int i, length;
void *base_addr;
WORD *selectors;
HGLOBAL handle;
auto_data_sel=0;
/*
* Allocate memory for the table to keep track of all selectors.
*/
selectors = (WORD *) malloc( ne_header->n_segment_tab * sizeof(WORD) );
if (selectors == NULL)
return NULL;
/*
* Step through the segment table in the exe header.
*/
for (i = 0; i < ne_header->n_segment_tab; i++)
{
/*
* Is there an image for this segment in the file?
*/
if (seg_table[i].seg_data_offset == 0)
{
/*
* No image in exe file, let's allocate some memory for it.
*/
length = seg_table[i].min_alloc;
}
else
{
/*
* Image in file, let's just point to the image in memory.
*/
length = seg_table[i].min_alloc;
file_image_length = seg_table[i].seg_data_length;
if (file_image_length == 0) file_image_length = 0x10000;
}
if (length == 0) length = 0x10000;
old_length = length;
/*
* If this is the automatic data segment, its size must be adjusted.
* First we need to check for local heap. Second we nee to see if
* this is also the stack segment.
*/
if (i + 1 == ne_header->auto_data_seg || i + 1 == ne_header->ss)
{
length = 0x10000;
ne_header->sp = length - 2;
dprintf_selectors(stddeb,"Auto data image length %x\n",file_image_length);
}
/*
* Is this a DATA or CODE segment?
*/
if (seg_table[i].seg_flags & NE_SEGFLAGS_DATA)
{
handle = GLOBAL_Alloc( GMEM_ZEROINIT, length, 0, FALSE,
seg_table[i].seg_flags & NE_SEGFLAGS_READONLY );
}
else
{
handle = GLOBAL_Alloc( 0, length, 0, TRUE,
seg_table[i].seg_flags & NE_SEGFLAGS_EXECUTEONLY );
}
base_addr = GlobalLock( handle );
selectors[i] = GlobalHandleToSel( handle );
if (seg_table[i].seg_data_offset != 0)
{
/*
* Image in file.
*/
lseek( fd, seg_table[i].seg_data_offset *
(1 << ne_header->align_shift_count), SEEK_SET );
if(read(fd, base_addr, file_image_length) != file_image_length)
{
fprintf( stderr, "Unable to read segment %d from file\n", i+1);
exit(1);
}
}
/*
* If this is the automatic data segment, then we must initialize
* the local heap.
*/
if (i + 1 == ne_header->auto_data_seg)
{
auto_data_sel = selectors[i];
saved_old_length = old_length;
}
}
if(!auto_data_sel)dprintf_selectors(stddeb,"Warning: No auto_data_sel\n");
for (i = 0; i < ne_header->n_segment_tab; i++)
{
/* Segments[s->selector >> __AHSHIFT].owner = auto_data_sel; */
if (selectors[i] == auto_data_sel)
LocalInit( auto_data_sel, saved_old_length,
0x10000 - 2
- ne_header->stack_length );
#if 0
HEAP_LocalInit(auto_data_sel,
(char *)GET_SEL_BASE(selectors[i]) + saved_old_length,
0x10000 - 2 - saved_old_length
- ne_header->stack_length);
#endif
}
if(!EnvironmentHandle)
{
EnvironmentHandle = CreateEnvironment();
PSPSelector = CreatePSP();
}
return selectors;
}
/**********************************************************************
*/
void
FixupFunctionPrologs(struct w_files * wpnt)
{
union lookup entry_tab_pointer;
struct entry_tab_header_s *eth;
struct entry_tab_movable_s *etm;
struct entry_tab_fixed_s *etf;
unsigned char *fixup_ptr;
int i;
/* if (!(ne_header->format_flags & NE_FFLAGS_SINGLEDATA))
return;
*/
entry_tab_pointer.cpnt = wpnt->ne->lookup_table;
/*
* Let's walk through the table and fixup prologs as we go.
*/
while (1)
{
/* Get bundle header */
eth = entry_tab_pointer.eth++;
/* Check for end of table */
if (eth->n_entries == 0)
return;
/* Check for empty bundle */
if (eth->seg_number == 0)
continue;
/* Examine each bundle */
for (i = 0; i < eth->n_entries; i++)
{
/* Moveable segment */
if (eth->seg_number >= 0xfe)
{
etm = entry_tab_pointer.etm++;
/* FIXME: Does anyone know the exact meaning of these flags? */
/* 0x0001 seems to mean: Fix up the function prolog */
dprintf_selector(stddeb,"ETM_Flags: %04x ",etm->flags);
if (!(etm->flags & 0x0001)) continue;
fixup_ptr = (char *)GET_SEL_BASE(wpnt->ne->selector_table[etm->seg_number-1]) + etm->offset;
}
else
{
etf = entry_tab_pointer.etf++;
dprintf_selector(stddeb,"ETF_Flags: %04x ",etf->flags);
if (!(etf->flags & 0x0001)) continue;
fixup_ptr = (char *)GET_SEL_BASE(wpnt->ne->selector_table[eth->seg_number-1])
+ (int) etf->offset[0]
+ ((int) etf->offset[1] << 8);
}
dprintf_selector(stddeb,"Signature: %02x %02x %02x,ff %x\n",
fixup_ptr[0],fixup_ptr[1],fixup_ptr[2],
wpnt->ne->ne_header->format_flags);
/* Verify the signature */
if (((fixup_ptr[0] == 0x1e && fixup_ptr[1] == 0x58)
|| (fixup_ptr[0] == 0x8c && fixup_ptr[1] == 0xd8))
&& fixup_ptr[2] == 0x90)
{
if (wpnt->ne->ne_header->format_flags & NE_FFLAGS_SINGLEDATA) {
fixup_ptr[0] = 0xb8; /* MOV AX, */
fixup_ptr[1] = wpnt->hinstance;
fixup_ptr[2] = (wpnt->hinstance >> 8);
} else {
fixup_ptr[0] = 0x90; /* non-library: NOPs */
fixup_ptr[1] = 0x90;
fixup_ptr[2] = 0x90;
}
}
}
}
}
#endif /* ifndef WINELIB */