tags:

views:

50

answers:

2

have C sources that must compile in 32bit and 64bit for multiple platforms. structure that takes the address of a buffer - need to fit address in a 32bit value.

obviously where possible these structures will use natural sized void * or char * pointers. however for some parts an api specifies the size of these pointers as 32bit.

on x86_64 linux with -m64 -mcmodel=small tboth static data and malloc()'d data fit within the 2Gb range. data on the stack, however, still starts in high memory.

so given a small utility _to_32() such as:

int _to_32( long l ) {
  int i = l & 0xffffffff;
  assert( i == l );
  return i;
}

then:

char *cp = malloc( 100 );
int a = _to_32( cp );

will work reliably, as would:

static char buff[ 100 ];
int a = _to_32( buff );

but:

char buff[ 100 ];
int a = _to_32( buff );

will fail the assert().

anyone have a solution for this without writing custom linker scripts?

or any ideas how to arrange the linker section for stack data, would appear it is being put in this section in the linker script:

.lbss   :
{
  *(.dynlbss)
  *(.lbss .lbss.* .gnu.linkonce.lb.*)
  *(LARGE_COMMON)
}

thanks!

A: 

The stack location is most likely specified by the operating system and has nothing to do with the linker.

I can't imagine why you are trying to force a pointer on a 64 bit machine into 32 bits. The memory layout of structures is mainly important when you are sharing the data with something which may run on another architecture and saving to a file or sending across a network, but there are almost no valid reasons that you would send a pointer from one computer to another. Debugging is the only valid reason that comes to mind.

Even storing a pointer to be used later by another run of your program on the same machine would almost certainly be wrong since where your program is loaded can differ. Making any use of such a pointer would be undefined abd unpredictable.

nategoose
yes the stack is laid out by the loader - based on instructions in the elf headers created by the linker.already explained above why is necessary to move between 64bit and 32bit pointer, and the steps taken to ensure pointer values remain in the 32bit range and detect (via the assert()) when this condition fails.and memory layout is defined by the calling routine - which may well be in a different language - and relies entirely on the api specification. isn't a different machine, never said anything like that. isn't storing the pointer either.
chaosless
@choasless: You did not explain why it is necessary to move between different pointer sizes, just that you needed to. After rereading your question I believe that you may be mixing up the sizeof the pointer with the sizeof the data type that it points to. Pointers for most common architectures are the same size regardless of what they point to. The exceptions are a few processors that have special memory for byte sized things and for 16 bit sized things.
nategoose
is necessary because the api provides a 32bit area for passing the pointer to data. after 20+ years am sure i know the difference between data and pointer to data.this is for code that must honor that api, which includes structures that allow 32bits for the pointer.as explained above, if the data is malloc()d or static then it falls within the -mcmodel=small control. which is why am asking here if anyone has any actually useful comments about how the loader determines where to place the stack address, and how to influence that in the linker scripts.
chaosless
If the API really does specify that the pointer has to be 32bits then it is a broken API. While I'm still unsure of exactly what you're working with, I wonder if a lookup table would suffice. Create a void * map_32_to_64[MAX_THINGS]; as conditional code for 64bit and just use uint32_t where you'd normally use a pointer in the API. It's not great as finding a pointer in that and finding an empty slot that you can use is more difficult. Or you could create an array things you would normally pass pointers to.
nategoose
really did you read anything i posted? the api is what it is - whether one might consider it 'broken' or not is irrelevant.if i was going to change the myriad of code that supports this api then i could change it to malloc() the space therefore falling within the 2Gb boundary.thanks for commenting though.
chaosless
A: 

the short answer appears to be there is no easy answer. at least no easy way to reassign range/location of the stack pointer.

the loader 'ld-linux.so' at a very early stage in process activation gets the address in the hurd loader - in the glibc sources, elf/ and sysdeps/x86_64/ search out elf_machine_load_address() and elf_machine_runtime_setup().

this happens in the preamble of calling your _start() entry and related setup to call your main(), is not for the faint hearted, even i couldn't convince myself this was a safe route.

as it happens - the resolution presents itself in some other old school tricks... pointer deflations/inflation...

with -mcmodel=small then automatic variables, alloca() addresses, and things like argv[], and envp are assigned from high memory from where the stack will grow down. those addresses are verified in this example code:

#include <stdlib.h>
#include <stdio.h>
#include <alloca.h>

extern char etext, edata, end;
char global_buffer[128];

int main( int argc, const char *argv[], const char *envp )
{
  char stack_buffer[128];
  static char static_buffer[128];
  char *cp = malloc( 128 );
  char *ap = alloca( 128 );
  char *xp = "STRING CONSTANT";

  printf("argv[0] %p\n",argv[0]);
  printf("envp    %p\n",envp);
  printf("stack   %p\n",stack_buffer);
  printf("global  %p\n",global_buffer);
  printf("static  %p\n",static_buffer);
  printf("malloc  %p\n",cp);
  printf("alloca  %p\n",ap);
  printf("const   %p\n",xp);
  printf("printf  %p\n",printf);

  printf("First address past:\n");
  printf("    program text (etext)      %p\n", &etext);
  printf("    initialized data (edata)  %p\n", &edata);
  printf("    uninitialized data (end)  %p\n", &end);
}

produces this output:

    argv[0] 0x7fff1e5e7d99  
    envp    0x7fff1e5e6c18  
    stack   0x7fff1e5e6a80  
    global  0x6010e0  
    static  0x601060  
    malloc  0x602010  
    alloca  0x7fff1e5e69d0  
    const   0x400850  
    printf  0x4004b0  
    First address past:  
        program text (etext)      0x400846  
        initialized data (edata)  0x601030  
        uninitialized data (end)  0x601160  

all access to/from the 32bit parts of structures must be wrapped with inflate() and deflate() routines, e.g.:

void *inflate( unsigned long );
unsigned int deflate( void *);

deflate() tests for bits set in the range 0x7fff00000000 and marks the pointer so that inflate() will recognize how to reconstitute the actual pointer.

hope that helps if anyone similarly must support structures with 32bit storage for 64bit pointers.

chaosless

related questions