Discussion:
LARGEADDRESSAWARE linker flag, IMAGE_FILE_LARGE_ADDRESS_AWARE exe header bit
(too old to reply)
Ricardo E. Gayoso
2013-04-29 20:34:54 UTC
Permalink
Raw Message
Hi,
Did somebody play with the LARGEADDRESSAWARE linker flag to duplicate the
address space of 32bit exes under Win7 64bit?

I wrote mem_frag.cpp test program (see below) to fill available virtual
address
space, calling heap_dump() to check memory fragmentation.
When I set the exe header bit:
LOADED_IMAGE img;
img.FileHeader->FileHeader.Characteristics |=
IMAGE_FILE_LARGE_ADDRESS_AWARE;
the exe allocates 3.9 Gbytes of RAM on Win7 64bit (instead of the usual 1.9
Gbytes)

Use editbin.exe and dumpbin.exe from VS .NET 2003 or newer.

Compile and set flag:
wcl386 -d2 -w4 -zq mem_frag
editbin /LARGEADDRESSAWARE mem_frag.exe

Check flag:
dumpbin /HEADERS mem_frag.exe

FILE HEADER VALUES
14C machine (x86)
5 number of sections
517EC036 time date stamp Mon Apr 29 15:47:18 2013
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
1A2 characteristics
Executable
Application can handle large (>2GB) addresses <=====
Bytes reversed
32 bit word machine

What about pointer math, like placing a FIFO crossing the boundary address
0x7FFFFFFF, or located above 0x80000000?
I checked and the usual function to calculate the nbr of bytes inside FIFO
still works ok:
if ( (size = fi - fo) < 0 ) {
size += capacity;
}
See ptr32.cpp test program below.

What happens if STL allocates a container crossing the 0x7FFFFFFF boundary?
And if it allocates it 100% above 0x80000000?

Thanks,
Ricardo

********* mem_frag.cpp ************************
/*
This test program plays with heap up to 4 GB.
Must be run on a 64bit Win7.

Use editbin.exe and dumpbin.exe from VS .NET 2003 or newer.

Compile and set flag:
wcl386 -d2 -w4 -zq mem_frag
editbin /LARGEADDRESSAWARE mem_frag.exe

The /LARGEADDRESSAWARE does:
LOADED_IMAGE img;
img.FileHeader->FileHeader.Characteristics |=
IMAGE_FILE_LARGE_ADDRESS_AWARE;


Check flag
dumpbin /HEADERS mem_frag.exe


FILE HEADER VALUES
14C machine (x86)
5 number of sections
517EC036 time date stamp Mon Apr 29 15:47:18 2013
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
1A2 characteristics
Executable
Application can handle large (>2GB) addresses <=====
Bytes reversed
32 bit word machine
*/
/* ----------------------------------------------------------------------------------
*/
#include <stdio.h>
#include <malloc.h>
#include <string.h>
/* ----------------------------------------------------------------------------------
*/
void heap_dump( char *msg, int details )
/*
Check C heap. Prints msg if not NULL.
If details = 0, prints only totals.
If details = 1, prints whole list of used-free blocks.
*/
{
struct _heapinfo h_info;
int heap_status;
unsigned __int64 tot_used = 0;
unsigned __int64 tot_free = 0;

static unsigned __int64 lcl_used = 0;
static unsigned __int64 lcl_free = 0;

h_info._pentry = NULL;
for(;;) {
heap_status = _heapwalk( &h_info );
if( heap_status != _HEAPOK ) {
break;
}
if ( details ) {
printf( " %s block at %Fp of size %u\n",
(h_info._useflag == _USEDENTRY ? "USED" :
"FREE"),
h_info._pentry, h_info._size );
}
if ( h_info._useflag == _USEDENTRY ) {
tot_used += h_info._size;
} else {
tot_free += h_info._size;
}
}
if ( msg != NULL ) {
printf( "%s->", msg );
}
printf( "Used:%8I64u Free:%8I64u Both:%8I64u DUSed:%8I64d
DFree:%8I64d ",
tot_used, tot_free, tot_used + tot_free,
tot_used - lcl_used, tot_free - lcl_free );
lcl_used = tot_used;
lcl_free = tot_free;

switch( heap_status ) {
case _HEAPEND:
printf( "OK - end of heap\n" );
break;
case _HEAPEMPTY:
printf( "OK - heap is empty\n" );
break;
case _HEAPBADBEGIN:
printf( "ERROR - heap is damaged\n" );
break;
case _HEAPBADPTR:
printf( "ERROR - bad pointer to heap\n" );
break;
case _HEAPBADNODE:
printf( "ERROR - bad node in heap\n" );
}
}
/* ----------------------------------------------------------------------------------
*/
#define NUM_BLOCKS 20

void main()
{
int i, j;
void *temp;
void *pp[ NUM_BLOCKS ];
void *qq[ NUM_BLOCKS ];

memset( pp, 0, sizeof(pp) );
memset( qq, 0, sizeof(qq) );

// Alloc a large block, free it, and call _heapenable() to force all
OS mem requests to fail
#if 0
if ( (pp[0] = malloc( 400000000 )) == NULL ) {
printf( "error in initial large blk\n" );
} else {
free( pp[0] );
}
_heapenable(false);
#endif
heap_dump( "Heap:", 1 );

for ( i = 0; i < NUM_BLOCKS; ++i ) {
if ( (pp[i] = malloc( 100000000 )) == NULL ) {
printf( "error in malloc blk %d\n", i );
break;
}
if ( (qq[i] = new char[ 100000000 ]) == NULL ) {
printf( "error in new blk %d\n", i );
break;
}
}
if ( (temp = malloc( 1000000 )) == NULL ) {
printf( "error in malloc tamp\n" );
}
for ( j = 0; j < i; ++j ) {
if ( pp[j] ) {
free( pp[j] );
}
if ( qq[j] ) {
delete [] qq[j];
}
}
heap_dump( "Heap:", 1 );

if ( (pp[0] = malloc( 800000000 )) == NULL ) {
printf( "error in large blk\n" );
} else {
free( pp[0] );
}
heap_dump( "Heap:", 1 );

_heapshrink();
heap_dump( "Heap:", 1 );

}
/* ------------------------------------------------------------------------------------------------------------------------------
*/

******** ptr32.cpp ********************
// wcl386 -d2 -w4 -zq ptr32

#include <stdio.h>
#include <assert.h>
#include <stddef.h>


int calc( char *fi, char *fo )
{
ptrdiff_t size; // May be negative
size_t capacity = 256;

if ( fo > fi ) {
printf( "fo > fi\n" );
} else if ( fo == fi ) {
printf( "fo == fi\n" );
} else {
printf( "fo < fi\n" );
}
if ( (size = fi - fo) < 0 ) {
size += capacity;
}
printf( "bytes in fifo: %d\n", size );
return( size );
}

void main()
{
char *fi;
char *fo;

// Note: 'up to' doesn't include the end limit

// From 0x7FFFFFF0 up to 0x800000F0
// Empty ==========================
fo = (char*)0x7FFFFFF0;
fi = (char*)0x7FFFFFF0;
assert( calc( fi, fo ) == 0 );

// 18 bytes
fo = (char*)0x7FFFFFF0;
fi = (char*)0x80000002;
assert( calc( fi, fo ) == 18);

// 18 bytes
fo = (char*)0x80000000;
fi = (char*)0x80000012;
assert( calc( fi, fo ) == 18);

// 2 bytes
fo = (char*)0x800000EF;
fi = (char*)0x7FFFFFF1;
assert( calc( fi, fo ) == 2);

// 16 bytes
fo = (char*)0x800000EF;
fi = (char*)0x7FFFFFFF;
assert( calc( fi, fo ) == 16);

// 18 bytes
fo = (char*)0x800000EF;
fi = (char*)0x80000001;
assert( calc( fi, fo ) == 18);


// From 0x80000000 up to 0x80000100
// Empty ==========================
fo = (char*)0x80000000;
fi = (char*)0x80000000;
assert( calc( fi, fo ) == 0);

// 1 byte
fo = (char*)0x80000000;
fi = (char*)0x80000001;
assert( calc( fi, fo ) == 1);

// 1 byte
fo = (char*)0x800000FF;
fi = (char*)0x80000000;
assert( calc( fi, fo ) == 1);
}
Ricardo E. Gayoso
2013-05-10 21:23:51 UTC
Permalink
Raw Message
Hi,
I have been playing with a very large application with 4GB access and it
seems to run ok.
I also activated Microsoft's recommended test procedure, forcing Win7-64 to
allocate RAM to all processes from hi address to lo address.

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory
Management]
"AllocationPreference"=dword:00100000

Then the firsts malloc()s gets RAM at 0xFF000000.
Everything runs ok..
The only problem I found is that win32's GetMacAddress() fails, because I
pass the address of a param on the stack, and the stack itself is allocated
on high RAM.
This usually won't happen, because the stack is the first RAM to be
allocated.
It may happen if you create threads later on, after filling the first 2GB of
RAM. Then thread's stack may be allocated on high RAM, and so some Windows
or third party calls may fail.
Ricardo
Post by Ricardo E. Gayoso
Hi,
Did somebody play with the LARGEADDRESSAWARE linker flag to duplicate the
address space of 32bit exes under Win7 64bit?
I wrote mem_frag.cpp test program (see below) to fill available virtual
address
space, calling heap_dump() to check memory fragmentation.
LOADED_IMAGE img;
img.FileHeader->FileHeader.Characteristics |=
IMAGE_FILE_LARGE_ADDRESS_AWARE;
the exe allocates 3.9 Gbytes of RAM on Win7 64bit (instead of the usual 1.9
Gbytes)
Use editbin.exe and dumpbin.exe from VS .NET 2003 or newer.
wcl386 -d2 -w4 -zq mem_frag
editbin /LARGEADDRESSAWARE mem_frag.exe
dumpbin /HEADERS mem_frag.exe
FILE HEADER VALUES
14C machine (x86)
5 number of sections
517EC036 time date stamp Mon Apr 29 15:47:18 2013
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
1A2 characteristics
Executable
Application can handle large (>2GB) addresses <=====
Bytes reversed
32 bit word machine
What about pointer math, like placing a FIFO crossing the boundary address
0x7FFFFFFF, or located above 0x80000000?
I checked and the usual function to calculate the nbr of bytes inside FIFO
if ( (size = fi - fo) < 0 ) {
size += capacity;
}
See ptr32.cpp test program below.
What happens if STL allocates a container crossing the 0x7FFFFFFF boundary?
And if it allocates it 100% above 0x80000000?
Thanks,
Ricardo
********* mem_frag.cpp ************************
/*
This test program plays with heap up to 4 GB.
Must be run on a 64bit Win7.
Use editbin.exe and dumpbin.exe from VS .NET 2003 or newer.
wcl386 -d2 -w4 -zq mem_frag
editbin /LARGEADDRESSAWARE mem_frag.exe
LOADED_IMAGE img;
img.FileHeader->FileHeader.Characteristics |=
IMAGE_FILE_LARGE_ADDRESS_AWARE;
Check flag
dumpbin /HEADERS mem_frag.exe
FILE HEADER VALUES
14C machine (x86)
5 number of sections
517EC036 time date stamp Mon Apr 29 15:47:18 2013
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
1A2 characteristics
Executable
Application can handle large (>2GB) addresses <=====
Bytes reversed
32 bit word machine
*/
/* ----------------------------------------------------------------------------------
*/
#include <stdio.h>
#include <malloc.h>
#include <string.h>
/* ----------------------------------------------------------------------------------
*/
void heap_dump( char *msg, int details )
/*
Check C heap. Prints msg if not NULL.
If details = 0, prints only totals.
If details = 1, prints whole list of used-free blocks.
*/
{
struct _heapinfo h_info;
int heap_status;
unsigned __int64 tot_used = 0;
unsigned __int64 tot_free = 0;
static unsigned __int64 lcl_used = 0;
static unsigned __int64 lcl_free = 0;
h_info._pentry = NULL;
for(;;) {
heap_status = _heapwalk( &h_info );
if( heap_status != _HEAPOK ) {
break;
}
if ( details ) {
printf( " %s block at %Fp of size %u\n",
"FREE"),
h_info._pentry, h_info._size );
}
if ( h_info._useflag == _USEDENTRY ) {
tot_used += h_info._size;
} else {
tot_free += h_info._size;
}
}
if ( msg != NULL ) {
printf( "%s->", msg );
}
printf( "Used:%8I64u Free:%8I64u Both:%8I64u DUSed:%8I64d
DFree:%8I64d ",
tot_used, tot_free, tot_used + tot_free,
tot_used - lcl_used, tot_free - lcl_free );
lcl_used = tot_used;
lcl_free = tot_free;
switch( heap_status ) {
printf( "OK - end of heap\n" );
break;
printf( "OK - heap is empty\n" );
break;
printf( "ERROR - heap is damaged\n" );
break;
printf( "ERROR - bad pointer to heap\n" );
break;
printf( "ERROR - bad node in heap\n" );
}
}
/* ----------------------------------------------------------------------------------
*/
#define NUM_BLOCKS 20
void main()
{
int i, j;
void *temp;
void *pp[ NUM_BLOCKS ];
void *qq[ NUM_BLOCKS ];
memset( pp, 0, sizeof(pp) );
memset( qq, 0, sizeof(qq) );
// Alloc a large block, free it, and call _heapenable() to force
all OS mem requests to fail
#if 0
if ( (pp[0] = malloc( 400000000 )) == NULL ) {
printf( "error in initial large blk\n" );
} else {
free( pp[0] );
}
_heapenable(false);
#endif
heap_dump( "Heap:", 1 );
for ( i = 0; i < NUM_BLOCKS; ++i ) {
if ( (pp[i] = malloc( 100000000 )) == NULL ) {
printf( "error in malloc blk %d\n", i );
break;
}
if ( (qq[i] = new char[ 100000000 ]) == NULL ) {
printf( "error in new blk %d\n", i );
break;
}
}
if ( (temp = malloc( 1000000 )) == NULL ) {
printf( "error in malloc tamp\n" );
}
for ( j = 0; j < i; ++j ) {
if ( pp[j] ) {
free( pp[j] );
}
if ( qq[j] ) {
delete [] qq[j];
}
}
heap_dump( "Heap:", 1 );
if ( (pp[0] = malloc( 800000000 )) == NULL ) {
printf( "error in large blk\n" );
} else {
free( pp[0] );
}
heap_dump( "Heap:", 1 );
_heapshrink();
heap_dump( "Heap:", 1 );
}
/* ------------------------------------------------------------------------------------------------------------------------------
*/
******** ptr32.cpp ********************
// wcl386 -d2 -w4 -zq ptr32
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
int calc( char *fi, char *fo )
{
ptrdiff_t size; // May be negative
size_t capacity = 256;
if ( fo > fi ) {
printf( "fo > fi\n" );
} else if ( fo == fi ) {
printf( "fo == fi\n" );
} else {
printf( "fo < fi\n" );
}
if ( (size = fi - fo) < 0 ) {
size += capacity;
}
printf( "bytes in fifo: %d\n", size );
return( size );
}
void main()
{
char *fi;
char *fo;
// Note: 'up to' doesn't include the end limit
// From 0x7FFFFFF0 up to 0x800000F0
// Empty ==========================
fo = (char*)0x7FFFFFF0;
fi = (char*)0x7FFFFFF0;
assert( calc( fi, fo ) == 0 );
// 18 bytes
fo = (char*)0x7FFFFFF0;
fi = (char*)0x80000002;
assert( calc( fi, fo ) == 18);
// 18 bytes
fo = (char*)0x80000000;
fi = (char*)0x80000012;
assert( calc( fi, fo ) == 18);
// 2 bytes
fo = (char*)0x800000EF;
fi = (char*)0x7FFFFFF1;
assert( calc( fi, fo ) == 2);
// 16 bytes
fo = (char*)0x800000EF;
fi = (char*)0x7FFFFFFF;
assert( calc( fi, fo ) == 16);
// 18 bytes
fo = (char*)0x800000EF;
fi = (char*)0x80000001;
assert( calc( fi, fo ) == 18);
// From 0x80000000 up to 0x80000100
// Empty ==========================
fo = (char*)0x80000000;
fi = (char*)0x80000000;
assert( calc( fi, fo ) == 0);
// 1 byte
fo = (char*)0x80000000;
fi = (char*)0x80000001;
assert( calc( fi, fo ) == 1);
// 1 byte
fo = (char*)0x800000FF;
fi = (char*)0x80000000;
assert( calc( fi, fo ) == 1);
}
Loading...