Hsiao-Hui Chiu
Hsiao-Hui Chiu

Reputation: 201

How to limit a 64-bit process address space to less than 4G?

When I execute a 64-bit process and look at /proc/[pid]/maps, the layout show that the shared libraries and stack section are in a larger address; such as the following:

7ffff7ffc000-7ffff7ffd000 r--p 0001d000 08:03 16643      /lib/ld-2.11.2.so
7ffff7ffd000-7ffff7ffe000 rw-p 0001e000 08:03 16643      /lib/ld-2.11.2.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffea000-7ffffffff000 rw-p 00000000 00:00 0         [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]

Can I limit these sections to less 4G space and it must still be a 64-bit process rather than compile it into a 32-bit process?

Upvotes: 3

Views: 1353

Answers (5)

Alex Y.
Alex Y.

Reputation: 1

Maybe this is what you are looking for:

man setrlimit

Upvotes: -1

caf
caf

Reputation: 239041

Based on your comment:

I am using llvm jit and forcing it to generate the x86 code in a 64-bit process. When I generate code for a "printf" instruction, it will call the function in libc; but the size of register is become 32-bit, I can't reach to address (The libc address is in 7fffxxxxxxxx.)

...merely limiting libraries to 32 bit addresses is entirely insufficient. For one thing, the calling convention is different in 64 bit mode - the 64 bit printf() expects its first parameter in %rdi, but the 32 bit code will instead push it on the stack.

You will need to have the generated code call a printf() wrapper that sets up the parameters correctly for the call to the real printf(). You can put that wrapper in a MAP_32BIT region.

Upvotes: 0

Chad
Chad

Reputation: 3008

Seeing I was voted down I went and re-corrected the answer.

For Microsoft Visual Studio C++ if the compiler is a 32bit compiler, the /LARGEADDRESSAWARE flag sets the virtual address space from 2gb to 3gb this is only when running on a 32bit OS.

When running the same 32bit program on a 64bit os, the application will be given access to the full 4gb of virtual address space. For 64bit compilers the /LARGEADDRESSAWARE flag is enabled by default.

Seeing this question is for GCC I guess its not important for your problem.

Upvotes: -1

sarnold
sarnold

Reputation: 104050

While the prelink(8) tool and concept are widely detested (and probably not shipped on your distribution), you may be able to use it to link libraries into a binary into low memory:

-r --reloc-only=ADDRESS
    Instead of prelinking, just relink given shared libraries
    to the specified base address.

Since the address that libraries will be mapped into the process is determined by ld(1), you might be able to modify your Makefile to invoke ld with different --section-start values:

--section-start SECTION=ADDRESS
                           Set address of named section
-Tbss ADDRESS               Set address of .bss section
-Tdata ADDRESS              Set address of .data section
-Ttext ADDRESS              Set address of .text section
-Ttext-segment ADDRESS      Set address of text segment

I moved the text and bss segments down to lower addresses:

$ gcc -Wl,-Ttext-segment=0x200000 -Wl,-Tbss=0x400000 -o broken broken.c
$ readelf -a broken
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x200450
...

If you can move all the sections provided in the executable with --section-start and move the libraries down with prelink(8), you might be able to get all the code loaded below 4 gigabytes.

Upvotes: 3

Prof. Falken
Prof. Falken

Reputation: 24867

I don't know any general answer to this, but if you patched libc and the kernel, you could modify them to never create any address higher than 4G. Then your user mode code would not have to changed at all, any generated address through for example malloc() would never be above 4G.

It may also be most convenient if the machine you ran the kernel also has at most 4G combined swap and physical memory.

One thing to try, is to turn off overcommit. According to this page, if you set overcommit to "2" (off), and use say, 512 megabyte physical memory (including swap) then your address space should never grow outside of what a 32 bit address can hold. There may also be some kind of offset added to the 64 bit addresses, but in that case you should be able to find it and remove it.

Yet another idea is very hackish, but might work. Allocate shared memory from a 32 bit process and use this memory in your 64 bit process.

Upvotes: 0

Related Questions