0

I have the following program to print a number via the printf function:

format: .ascii "Your number is: %d.\n\0"

.globl main
main:

    // printf('%d', 55);
    mov $format, %rdi
    mov $55, %rsi
    mov $0, %eax

    // call printf with 16-byte aligned stack:
    sub $8, %rsp
    call printf
    add $8, %rsp

    // return 0;
    mov $0, %eax
    ret
$ gcc -no-pie int.s -o int; ./int
Your number is: 55.

I have a few questions about this as I was writing this:

  • Does the sub $8...add $8 work fine to preserve alignment? For example, the same as doing push %rbp...pop %rbp.
  • I tried adding in some .data and .rodata and .text directives/sections but each time I would get a warning/error. Why aren't those allowed when invoking an assembly program via gcc? How, for example, does C know that "format" is .data and "main" is in .text ?
  • Is mov $0, %eax; ret the proper way to exit the C main function from assembly?
  • Finally, what modifications would I need to make this program run without doing -no-pie ?
carl.hiass
  • 1,526
  • 1
  • 6
  • 26

1 Answers1

2

Does the sub $8...add $8 work fine to preserve alignment? For example, the same as doing push %rbp...pop %rbp.

Yes. If you read the instruction description of push and pop you'll see that they have the same effect on the stack pointer, besides reading/writing to the register and stack memory which you don't care about. However push and pop are shorter instructions.

How, for example, does C know that "format" is .data and "main" is in .text ?

Unless you tell it, it doesn't, and they aren't. If you dump your executable with objdump --full-contents you will see that your string has been put in .text along with everything else, since you never told the assembler to do otherwise.

Is mov $0, %eax; ret the proper way to exit the C main function from assembly?

Yes, though xor %eax, %eax ; ret is more efficient. What is the best way to set a register to zero in x86 assembly: xor, mov or and?

Finally, what modifications would I need to make this program run without doing -no-pie ?

You're not allowed to use absolute addresses as immediates in a position-independent executable, so mov $format, %rdi is no good. Instead use RIP-relative addressing: lea format(%rip), %rdi. Everything else is fine.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • thanks again for this great answer. You mention that doing `ret` is fine in this question but in the previous comment you mentioned not to use `ret` to exit from C's main. What's the difference between the two? – carl.hiass Mar 25 '21 at 23:51
  • 1
    @carl.hiass: I was confused about the context in the previous comment. You can `ret` from `main`, but not from `_start`. – Nate Eldredge Mar 25 '21 at 23:52
  • I get something similar to this when removing the `no-pie`, even with changing the `lea format`: https://stackoverflow.com/questions/48071280/nasm-symbol-printf-causes-overflow-in-r-x86-64-pc32-relocation. In `gdb` it's the `call printf` statement that says invalid memory address. – carl.hiass Mar 25 '21 at 23:56
  • @carl.hiass: Show **your** code, please. It worked for me. – Nate Eldredge Mar 26 '21 at 00:08
  • sure, updated here: https://stackoverflow.com/questions/66809271/removing-the-need-for-no-pie-in-an-assembly-program. – carl.hiass Mar 26 '21 at 00:09
  • 1
    *You're not allowed to use absolute addresses as immediates in a position-independent executable* - You can't use *32-bit* immediates, but Linux does actually allow text relocations / fixups in a PIE or shared lib so `movabs $symbol, %rdi` will link and run in a PIE executable. It's obviously worse than `lea symbol(%rip), %rdi`, but the real reason `mov $foo, %rdi` doesn't work is that it assembles as `mov $sign_extended_imm32, %rdi` with an R_X86_64_32S relocation, not R_X86_64_64. [How to load address of function or label into register](https://stackoverflow.com/q/57212012) – Peter Cordes Mar 26 '21 at 01:23
  • (And [32-bit absolute addresses no longer allowed in x86-64 Linux?](https://stackoverflow.com/q/43367427)). Even more fun, `mov rdi, symbol` works in NASM code (but not YASM), because of their choices for what size to assume for an unknown symbol address. Oh, current `ld` warns about creating a DT_TEXTREL in a PIE; that's good. – Peter Cordes Mar 26 '21 at 01:24
  • I thought I remembered needing to write `call printf@plt` (or `call *printf@GOTPCREL(%rip)`) for code to link into a PIE. But apparently `ld` will create PLT stubs if needed when linking a PIE now; I'm pretty sure it didn't used to. Yeah, [How can I call printf normally in assembly without @PLT but just call printf with -l option in gcc with standard library,](https://stackoverflow.com/q/56404855) confirms that older systems used to try to link `call printf` directly, without a PLT stub, leading to overflow of the text relocation when libc isn't loaded within 2GiB (which it won't be). – Peter Cordes Mar 26 '21 at 01:31