5

If I compile this program:

#include <stdio.h>

int main(int argc, char** argv) {
    printf("hello world!\n");
    return 0;
}

for x86-64, the asm output uses movl $.LC0, %edi / call puts. (See full asm output / compile options on godbolt.)

My question is: How can GCC know that the the string's address can fit in a 32bit immediate operand? Why doesn't it need to use movabs $.LC0, %rdi (i.e. a mov r64, imm64, not a zero or sign-extended imm32).

AFAIK, there's nothing saying the loader has to decide to load the data section at any particular address. If the string is stored at some address above 1ULL << 32 then the higher bits will be ignored by the movl. I get similar behavior with clang, so I don't think this is unique to GCC.


The reason I care is I want to create my own data segment that lives in memory at any arbitrary address I choose (above 2^32 potentially).

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Scott Carr
  • 113
  • 1
  • 4
  • And you compile for a 64 bit target? – too honest for this site Mar 23 '16 at 16:30
  • Hazarding a guess: in the virtual memory layout for your process, the .data segment is near the beginning, after the .text segment. You don't have 4GB of code, so it's probably safe to assume .data it starts in the 32 bit space. – Ari Mar 23 '16 at 16:37
  • BTW `1<<32` is sometimes UB. – user3528438 Mar 23 '16 at 16:47
  • I got the question reopened by inserting more details on what happens on Linux. You should add enough detail into the question itself so that it doesn't get closed. – Antti Haapala -- Слава Україні Mar 24 '16 at 09:00
  • The linker decides where the `.rodata` and `.data` sections will be in virtual memory. The ELF loader in the kernel must map the executable to the addresses its metadata specifies. Linux executables do *not* have to be position-independent. Only shared libraries have to be PIC. This is there can be a compiler option for code model without any mention of OS support. Good question, though. It's not obvious how to google this: You wouldn't know what to search on unless you already know the answer. – Peter Cordes Mar 24 '16 at 09:19
  • @AnttiHaapala: It's weird that this got closed. The original question was well-written and pretty clear. I knew right away what was being asked. I have to assume that the close-voters didn't know the answer and didn't know that amd64 compilers do this for accessing static data. To be fair, it's *usually* a safe assumption that a low-rep user asking a question has some silly misunderstanding or mistake like building in 32bit mode, and a question with an interesting answer is the exception. Anyway, I cleaned up your edit to leave out the noisy full compiler output. – Peter Cordes Mar 24 '16 at 12:21

2 Answers2

6

In GCC manual:

https://gcc.gnu.org/onlinedocs/gcc-4.5.3/gcc/i386-and-x86_002d64-Options.html

3.17.15 Intel 386 and AMD x86-64 Options

-mcmodel=small

Generate code for the small code model: the program and its symbols must be linked in the lower 2 GB of the address space. Pointers are 64 bits. Programs can be statically or dynamically linked. This is the default code model.

-mcmodel=kernel Generate code for the kernel code model. The kernel runs in the negative 2 GB of the address space. This model has to be used for Linux kernel code.

-mcmodel=medium

Generate code for the medium model: The program is linked in the lower 2 GB of the address space. Small symbols are also placed there. Symbols with sizes larger than -mlarge-data-threshold are put into large data or bss sections and can be located above 2GB. Programs can be statically or dynamically linked.

-mcmodel=large

Generate code for the large model: This model makes no assumptions about addresses and sizes of sections.


https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html

3.18.1 AArch64 Options

-mcmodel=tiny

Generate code for the tiny code model. The program and its statically defined symbols must be within 1GB of each other. Pointers are 64 bits. Programs can be statically or dynamically linked. This model is not fully implemented and mostly treated as ‘small’.

-mcmodel=small

Generate code for the small code model. The program and its statically defined symbols must be within 4GB of each other. Pointers are 64 bits. Programs can be statically or dynamically linked. This is the default code model.

-mcmodel=large

Generate code for the large code model. This makes no assumptions about addresses and sizes of sections. Pointers are 64 bits. Programs can be statically linked only.

user3528438
  • 2,737
  • 2
  • 23
  • 42
1

I can confirm that this happens on 64-bit compilation:

gcc -O1 foo.c

Then objdump -d a.out (notice also that printf("%s\n") can be optimized into puts!):

0000000000400536 <main>:
  400536:       48 83 ec 08             sub    $0x8,%rsp
  40053a:       bf d4 05 40 00          mov    $0x4005d4,%edi
  40053f:       e8 cc fe ff ff          callq  400410 <puts@plt>
  400544:       b8 00 00 00 00          mov    $0x0,%eax
  400549:       48 83 c4 08             add    $0x8,%rsp
  40054d:       c3                      retq   
  40054e:       66 90                   xchg   %ax,%ax

The reason is that GCC defaults to -mcmodel=small where the static data is linked in the bottom 2G of address space.


Notice that string constants do not go to the data segment, but they're within the code segment instead, unless -fwritable-strings. Also if you want to relocate the object code freely in memory, you'd probably want to compile with -fpic to make the code RIP relative instead of putting 64-bit addresses everywhere.

Community
  • 1
  • 1
  • 1
    You can actually use `-fpie` to make position independent executables. This may enable slightly faster code than `-fpic`, depending on the target architecture, since the code doesn't have to be linkable into a shared library. I'm not sure if this makes a difference for i386 or amd64 targets. – Peter Cordes Mar 24 '16 at 11:16
  • Update, `-fpie` can still inline non-`static` functions, but `-fpic` can't because it has to assume symbol-interposition. See https://www.macieira.org/blog/2012/01/sorry-state-of-dynamic-libraries-on-linux/ for `-fPIC`, and my answer on [32-bit absolute addresses no longer allowed in x86-64 Linux?](https://stackoverflow.com/q/43367427) for more about `-fpie` vs. PIC. `-fpie -pie` is the default for gcc on most recent Linux distros. – Peter Cordes Sep 10 '18 at 17:10