当-m64 -mcmodel = small时,强制堆栈w / i为32位

时间:2010-05-04 18:04:02

标签: 32bit-64bit

有C源,必须在32位和64位编译多个平台。 获取缓冲区地址的结构 - 需要使地址符合32位值。

显然,在可能的情况下,这些结构将使用自然大小的void *或char *指针。 但是对于某些部分,api将这些指针的大小指定为32位。

在x86_64 linux上使用-m64 -mcmodel =小tboth静态数据和malloc()'d数据适合2Gb范围。但是,堆栈上的数据仍然以高内存开始。

所以给出了一个小实用程序_to_32(),例如:

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

然后:

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

将可靠地运作,如下:

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

但:

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

将使assert()失败。

任何人都有一个解决方案,而无需编写自定义链接描述文件?

或任何关于如何为堆栈数据安排链接器部分的想法,似乎将在链接器脚本的这一部分中添加:

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

谢谢!

2 个答案:

答案 0 :(得分:0)

堆栈位置很可能是由操作系统指定的,与链接器无关。

我无法想象为什么你试图将64位机器上的指针强制为32位。当您使用可能在另一个体系结构上运行并保存到文件或通过网络发送的内容共享数据时,结构的内存布局非常重要,但几乎没有任何正当理由可以将指针从一台计算机发送到另一个。调试是我想到的唯一正当理由。

即使存储一个指针,以便稍后由同一台计算机上的另一个程序运行使用,几乎肯定会出错,因为加载程序的位置可能不同。使用这样的指针将是不可定义的,不可预测的。

答案 1 :(得分:0)

简短的回答似乎是没有简单的答案。至少没有简单的方法来重新分配堆栈指针的范围/位置。

加载程序'ld-linux.so'在进程激活的早期阶段获取hurd加载器中的地址 - 在glibc源代码中,elf /和sysdeps / x86_64 /搜索elf_machine_load_address()和elf_machine_runtime_setup()。

这发生在调用你的_start()条目和相关设置调用你的main()的序言中,不是为了胆小的人,即使我无法说服自己这是一条安全的路线。

正如它所发生的那样 - 该决议在其他一些旧学校的技巧中出现......指针通缩/通货膨胀......

使用-mcmodel = small然后自动变量,alloca()地址,以及像argv []和envp这样的东西从高内存中分配,堆栈将从那里开始增长。在此示例代码中验证了这些地址:

#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);
}

生成此输出:

    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  

所有对32bit部分结构的访问必须用inflate()和deflate()例程包装,例如:

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

deflate()测试在0x7fff00000000范围内设置的位并标记指针,以便inflate()识别如何重构实际指针。

如果有人同样必须支持64位指针的32位存储结构,那么希望能有所帮助。