wine/tools/winedump/ne.c
Eric Pouech afe309b7d9 winedump: Changed PE, NE, LE dumping so that they are consistent in terms of dump function signatures.
Avoid passing around the base of the module, but use instead the PRD function.
2006-11-30 12:34:50 +01:00

393 lines
13 KiB
C

/*
* Dumping of NE binaries
*
* Copyright 2002 Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "wine/port.h"
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "windef.h"
#include "winbase.h"
#include "wine/winbase16.h"
#include "winedump.h"
struct ne_segtable_entry
{
WORD seg_data_offset; /* Sector offset of segment data */
WORD seg_data_length; /* Length of segment data */
WORD seg_flags; /* Flags associated with this segment */
WORD min_alloc; /* Minimum allocation size for this */
};
struct relocation_entry
{
BYTE address_type; /* Relocation address type */
BYTE relocation_type; /* Relocation type */
WORD offset; /* Offset in segment to fixup */
WORD target1; /* Target specification */
WORD target2; /* Target specification */
};
#define NE_RADDR_LOWBYTE 0
#define NE_RADDR_SELECTOR 2
#define NE_RADDR_POINTER32 3
#define NE_RADDR_OFFSET16 5
#define NE_RADDR_POINTER48 11
#define NE_RADDR_OFFSET32 13
#define NE_RELTYPE_INTERNAL 0
#define NE_RELTYPE_ORDINAL 1
#define NE_RELTYPE_NAME 2
#define NE_RELTYPE_OSFIXUP 3
#define NE_RELFLAG_ADDITIVE 4
static inline WORD get_word( const BYTE *ptr )
{
return ptr[0] | (ptr[1] << 8);
}
static void dump_ne_header( const IMAGE_OS2_HEADER *ne )
{
printf( "File header:\n" );
printf( "Linker version: %d.%d\n", ne->ne_ver, ne->ne_rev );
printf( "Entry table: %x len %d\n", ne->ne_enttab, ne->ne_cbenttab );
printf( "Checksum: %08x\n", ne->ne_crc );
printf( "Flags: %04x\n", ne->ne_flags );
printf( "Auto data segment: %x\n", ne->ne_autodata );
printf( "Heap size: %d bytes\n", ne->ne_heap );
printf( "Stack size: %d bytes\n", ne->ne_stack );
printf( "Stack pointer: %x:%04x\n", SELECTOROF(ne->ne_sssp), OFFSETOF(ne->ne_sssp) );
printf( "Entry point: %x:%04x\n", SELECTOROF(ne->ne_csip), OFFSETOF(ne->ne_csip) );
printf( "Number of segments: %d\n", ne->ne_cseg );
printf( "Number of modrefs: %d\n", ne->ne_cmod );
printf( "Segment table: %x\n", ne->ne_segtab );
printf( "Resource table: %x\n", ne->ne_rsrctab );
printf( "Resident name table: %x\n", ne->ne_restab );
printf( "Module table: %x\n", ne->ne_modtab );
printf( "Import table: %x\n", ne->ne_imptab );
printf( "Non-resident table: %x\n", ne->ne_nrestab );
printf( "Exe type: %x\n", ne->ne_exetyp );
printf( "Other flags: %x\n", ne->ne_flagsothers );
printf( "Fast load area: %x-%x\n", ne->ne_pretthunks << ne->ne_align,
(ne->ne_pretthunks+ne->ne_psegrefbytes) << ne->ne_align );
printf( "Expected version: %d.%d\n", HIBYTE(ne->ne_expver), LOBYTE(ne->ne_expver) );
}
static void dump_ne_names( const IMAGE_OS2_HEADER *ne )
{
const unsigned char *pstr = (const unsigned char *)ne + ne->ne_restab;
printf( "\nResident name table:\n" );
while (*pstr)
{
printf( " %4d: %*.*s\n", get_word(pstr + *pstr + 1), *pstr, *pstr, pstr + 1 );
pstr += *pstr + 1 + sizeof(WORD);
}
if (ne->ne_cbnrestab)
{
printf( "\nNon-resident name table:\n" );
pstr = PRD(ne->ne_nrestab, 0);
while (*pstr)
{
printf( " %4d: %*.*s\n", get_word(pstr + *pstr + 1), *pstr, *pstr, pstr + 1 );
pstr += *pstr + 1 + sizeof(WORD);
}
}
}
static const char *get_resource_type( WORD id )
{
static char buffer[5];
switch(id)
{
case NE_RSCTYPE_CURSOR: return "CURSOR";
case NE_RSCTYPE_BITMAP: return "BITMAP";
case NE_RSCTYPE_ICON: return "ICON";
case NE_RSCTYPE_MENU: return "MENU";
case NE_RSCTYPE_DIALOG: return "DIALOG";
case NE_RSCTYPE_STRING: return "STRING";
case NE_RSCTYPE_FONTDIR: return "FONTDIR";
case NE_RSCTYPE_FONT: return "FONT";
case NE_RSCTYPE_ACCELERATOR: return "ACCELERATOR";
case NE_RSCTYPE_RCDATA: return "RCDATA";
case NE_RSCTYPE_GROUP_CURSOR: return "CURSOR_GROUP";
case NE_RSCTYPE_GROUP_ICON: return "ICON_GROUP";
default:
sprintf( buffer, "%04x", id );
return buffer;
}
}
static void dump_ne_resources( const IMAGE_OS2_HEADER *ne )
{
const NE_NAMEINFO *name;
const void *res_ptr = (const char *)ne + ne->ne_rsrctab;
WORD size_shift = get_word(res_ptr);
const NE_TYPEINFO *info = (const NE_TYPEINFO *)((const WORD *)res_ptr + 1);
int count;
printf( "\nResources:\n" );
while (info->type_id != 0 && (const char *)info < (const char *)ne + ne->ne_restab)
{
name = (const NE_NAMEINFO *)(info + 1);
for (count = info->count; count > 0; count--, name++)
{
if (name->id & 0x8000) printf( " %d", (name->id & ~0x8000) );
else printf( " %.*s", *((const unsigned char *)res_ptr + name->id),
(const char *)res_ptr + name->id + 1 );
if (info->type_id & 0x8000) printf( " %s", get_resource_type(info->type_id) );
else printf( " %.*s", *((const unsigned char *)res_ptr + info->type_id),
(const char *)res_ptr + info->type_id + 1 );
printf(" flags %04x length %04x\n", name->flags, name->length << size_shift);
dump_data( PRD(name->offset << size_shift, name->length << size_shift),
name->length << size_shift, " " );
}
info = (const NE_TYPEINFO *)name;
}
}
static const char *get_export_name( const IMAGE_OS2_HEADER *ne, int ordinal )
{
static char name[256];
const BYTE *pstr;
int pass = 0;
/* search the resident names */
while (pass < 2)
{
if (pass == 0) /* resident names */
{
pstr = (const BYTE *)ne + ne->ne_restab;
if (*pstr) pstr += *pstr + 1 + sizeof(WORD); /* skip first entry (module name) */
}
else /* non-resident names */
{
if (!ne->ne_cbnrestab) break;
pstr = PRD(ne->ne_nrestab, 0);
}
while (*pstr)
{
WORD ord = get_word(pstr + *pstr + 1);
if (ord == ordinal)
{
memcpy( name, pstr + 1, *pstr );
name[*pstr] = 0;
return name;
}
pstr += *pstr + 1 + sizeof(WORD);
}
pass++;
}
name[0] = 0;
return name;
}
static void dump_ne_exports( const IMAGE_OS2_HEADER *ne )
{
const BYTE *ptr = (const BYTE *)ne + ne->ne_enttab;
const BYTE *end = ptr + ne->ne_cbenttab;
int i, ordinal = 1;
if (!ne->ne_cbenttab || !*ptr) return;
printf( "\nExported entry points:\n" );
while (ptr < end && *ptr)
{
BYTE count = *ptr++;
BYTE type = *ptr++;
switch(type)
{
case 0: /* next bundle */
ordinal += count;
break;
case 0xff: /* movable */
for (i = 0; i < count; i++)
{
printf( " %4d MOVABLE %d:%04x %s\n",
ordinal + i, ptr[3], get_word(ptr + 4),
get_export_name( ne, ordinal + i ) );
ptr += 6;
}
ordinal += count;
break;
case 0xfe: /* constant */
for (i = 0; i < count; i++)
{
printf( " %4d CONST %04x %s\n",
ordinal + i, get_word(ptr + 1),
get_export_name( ne, ordinal + i ) );
ptr += 3;
}
ordinal += count;
break;
default: /* fixed */
for (i = 0; i < count; i++)
{
printf( " %4d FIXED %d:%04x %s\n",
ordinal + i, type, get_word(ptr + 1),
get_export_name( ne, ordinal + i ) );
ptr += 3;
}
ordinal += count;
break;
}
}
}
static const char *get_reloc_name( BYTE addr_type, int additive )
{
switch(addr_type & 0x7f)
{
case NE_RADDR_LOWBYTE: return additive ? "byte add" : "byte";
case NE_RADDR_OFFSET16: return additive ? "off16 add" : "off16";
case NE_RADDR_POINTER32: return additive ? "ptr32 add" : "ptr32";
case NE_RADDR_SELECTOR: return additive ? "sel add" : "sel";
case NE_RADDR_POINTER48: return additive ? "ptr48 add" : "ptr48";
case NE_RADDR_OFFSET32: return additive ? "off32 add" : "off32";
}
return "???";
}
static const char *get_seg_flags( WORD flags )
{
static char buffer[256];
buffer[0] = 0;
#define ADD_FLAG(x) if (flags & NE_SEGFLAGS_##x) strcat( buffer, " " #x );
ADD_FLAG(DATA);
ADD_FLAG(ALLOCATED);
ADD_FLAG(LOADED);
ADD_FLAG(ITERATED);
ADD_FLAG(MOVEABLE);
ADD_FLAG(SHAREABLE);
ADD_FLAG(PRELOAD);
ADD_FLAG(EXECUTEONLY);
ADD_FLAG(READONLY);
ADD_FLAG(RELOC_DATA);
ADD_FLAG(SELFLOAD);
ADD_FLAG(DISCARDABLE);
ADD_FLAG(32BIT);
#undef ADD_FLAG
if (buffer[0])
{
buffer[0] = '(';
strcat( buffer, ")" );
}
return buffer;
}
static void dump_relocations( const IMAGE_OS2_HEADER *ne, WORD count,
const struct relocation_entry *rep )
{
const WORD *modref = (const WORD *)((const BYTE *)ne + ne->ne_modtab);
const BYTE *mod_name, *func_name;
WORD i;
for (i = 0; i < count; i++, rep++)
{
int additive = rep->relocation_type & NE_RELFLAG_ADDITIVE;
switch (rep->relocation_type & 3)
{
case NE_RELTYPE_ORDINAL:
mod_name = (const BYTE *)ne + ne->ne_imptab + modref[rep->target1 - 1];
printf( "%6d: %s = %*.*s.%d\n", i + 1, get_reloc_name( rep->address_type, additive ),
*mod_name, *mod_name, mod_name + 1, rep->target2 );
break;
case NE_RELTYPE_NAME:
mod_name = (const BYTE *)ne + ne->ne_imptab + modref[rep->target1 - 1];
func_name = (const BYTE *)ne + ne->ne_imptab + rep->target2;
printf( "%6d: %s = %*.*s.%*.*s\n", i + 1, get_reloc_name( rep->address_type, additive ),
*mod_name, *mod_name, mod_name + 1,
*func_name, *func_name, func_name + 1 );
break;
case NE_RELTYPE_INTERNAL:
if ((rep->target1 & 0xff) == 0xff)
{
/* the module itself */
mod_name = (const BYTE *)ne + ne->ne_restab;
printf( "%6d: %s = %*.*s.%d\n", i + 1, get_reloc_name( rep->address_type, additive ),
*mod_name, *mod_name, mod_name + 1, rep->target2 );
}
else
printf( "%6d: %s = %d:%04x\n", i + 1, get_reloc_name( rep->address_type, additive ),
rep->target1, rep->target2 );
break;
case NE_RELTYPE_OSFIXUP:
/* Relocation type 7:
*
* These appear to be used as fixups for the Windows
* floating point emulator. Let's just ignore them and
* try to use the hardware floating point. Linux should
* successfully emulate the coprocessor if it doesn't
* exist.
*/
printf( "%6d: %s = TYPE %d, OFFSET %04x, TARGET %04x %04x\n",
i + 1, get_reloc_name( rep->address_type, additive ),
rep->relocation_type, rep->offset,
rep->target1, rep->target2 );
break;
}
}
}
static void dump_ne_segment( const IMAGE_OS2_HEADER *ne, int segnum )
{
const struct ne_segtable_entry *table = (const struct ne_segtable_entry *)((const BYTE *)ne + ne->ne_segtab);
const struct ne_segtable_entry *seg = table + segnum - 1;
printf( "\nSegment %d:\n", segnum );
printf( " File offset: %08x\n", seg->seg_data_offset << ne->ne_align );
printf( " Length: %08x\n", seg->seg_data_length );
printf( " Flags: %08x %s\n", seg->seg_flags, get_seg_flags(seg->seg_flags) );
printf( " Alloc size: %08x\n", seg->min_alloc );
if (seg->seg_flags & NE_SEGFLAGS_RELOC_DATA)
{
const BYTE *ptr = PRD((seg->seg_data_offset << ne->ne_align) + seg->seg_data_length, 0);
WORD count = get_word(ptr);
ptr += sizeof(WORD);
printf( " Relocations:\n" );
dump_relocations( ne, count, (const struct relocation_entry *)ptr );
}
}
void ne_dump( void )
{
unsigned int i;
const IMAGE_DOS_HEADER *dos;
const IMAGE_OS2_HEADER *ne;
dos = PRD(0, sizeof(*dos));
if (!dos) return;
ne = PRD(dos->e_lfanew, sizeof(*ne));
dump_ne_header( ne );
dump_ne_names( ne );
dump_ne_resources( ne );
dump_ne_exports( ne );
for (i = 1; i <= ne->ne_cseg; i++) dump_ne_segment( ne, i );
}