当 -m64 -mcmodel=small 时强制使用 32 位堆栈

发布于 2024-08-31 06:38:16 字数 890 浏览 1 评论 0原文

具有必须为多个平台编译为 32 位和 64 位的 C 源代码。 获取缓冲区地址的结构 - 需要将地址放入 32 位值中。

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

在 x86_64 linux 上,使用 -m64 -mcmodel=small 静态数据和 malloc() 数据都适合在 2Gb 范围内。然而,堆栈上的数据仍然从高端内存开始。

因此,给定一个小实用程序 _to_32() 例如:

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

then:

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

将可靠地工作,就像:

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

but:

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

将使assert() 失败。

有人可以在不编写自定义链接器脚本的情况下解决此问题吗?

或者任何如何安排堆栈数据的链接器部分的想法,都会出现在链接器脚本中的此部分中:

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

谢谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小梨窩很甜 2024-09-07 06:38:16

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

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

即使存储一个指针供稍后在同一台机器上运行程序的另一次使用也几乎肯定是错误的,因为程序加载的位置可能不同。使用这样的指针将是未定义的和不可预测的。

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.

浮生未歇 2024-09-07 06:38:16

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

加载程序“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  

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

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

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文