1

A friend helped me come up with the following code to use inline assembly in GCC on a 64-bit Windows machine:

int main() {
    char* str = "Hello World";
    int ret;

    asm volatile(
        "call puts"
        : "=a" (ret), "+c" (str)
        :
        : "rdx", "rdi", "rsi", "r8", "r9", "r10", "r11");

    return 0;
}

After compiling with -S -masm=intel (I prefer Intel syntax), I get this assembly code:

    .file   "hello.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .section .rdata,"dr"
.LC0:
    .ascii "Hello World\0"
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    push    rbp
    .seh_pushreg    rbp
    push    rdi
    .seh_pushreg    rdi
    push    rsi
    .seh_pushreg    rsi
    mov rbp, rsp
    .seh_setframe   rbp, 0
    sub rsp, 48
    .seh_stackalloc 48
    .seh_endprologue
    call    __main
    lea rax, .LC0[rip]
    mov QWORD PTR -8[rbp], rax
    mov rax, QWORD PTR -8[rbp]
    mov rcx, rax
/APP
 # 7 "hello.c" 1
    call puts
 # 0 "" 2
/NO_APP
    mov DWORD PTR -12[rbp], eax
    mov QWORD PTR -8[rbp], rcx
    mov eax, 0
    add rsp, 48
    pop rsi
    pop rdi
    pop rbp
    ret
    .seh_endproc
    .ident  "GCC: (x86_64-posix-seh-rev1, Built by MinGW-W64 project) 4.9.2"

It works, but it sure looks messy with what appears to be superfluous code. Then again, my last experience with assembly was with the 65816 back in the 80s, and it wasn't inline. Anyway, I cleaned up the code, and the following accomplishes the exact same thing, as far as I can tell:

.intel_syntax noprefix
.data:
    .ascii "Hello World\0"
.text
.globl  main
main:
    sub     rsp, 48 
    lea     rax, .data[rip]
    mov     rcx, rax
    call    puts
    mov     eax, 0
    add     rsp, 48
    ret

Much simpler. What's all that extra stuff GCC added?

Edit: Not a duplicate because in addition to the structured exception handling, I'm also asking about the callee-saved registers, the call to __main, the explicit size directives, and the APP/NO_APP section.

svadhisthana
  • 163
  • 2
  • 16
  • possible duplicate of [What are .seh\_\* assembly commands that gcc outputs?](http://stackoverflow.com/questions/20819927/what-are-seh-assembly-commands-that-gcc-outputs) – a3f Apr 19 '15 at 13:00
  • 3
    That's SEH instructions. SEH stands for structured exception handling. It's a Windows mechanism for handling exceptions. IIRC, it was designed to circumvent exploitation of some vulnerabilities in some Windows binaries. –  Apr 19 '15 at 13:10
  • You also should have specified that `str` was an input operand since you're only reading it, and you don't clobber any registers in your inline ASM, so you don't need to specify any clobbered registers. I'd also recommend that you add the option `-fomit-frame-pointer` if you want to avoid the push/pop of the `rbp` register. Also, `-fverbose-asm` can help you to understand what's going on with some of the registers, though there are still instructions that seem unnecessary to me (saving `rax`, which has the address of `str`, to `-8[ebp]` is pointless; `str` isn't used at all later) –  Apr 19 '15 at 13:56
  • 1
    `asm volatile("call puts" :"=a"(ret) :"c"(str));` is my asm line. You can also use the `-O2` command line option to clean things up considerably. –  Apr 19 '15 at 13:59
  • @ChronoKitsune, the clobbers and output specification for `str` are needed as the register are changed by `puts`. – Timothy Baldwin Apr 19 '15 at 17:47
  • This code also fails to reserve shadow space which is required by the ABI. – Timothy Baldwin Apr 19 '15 at 17:49
  • @TimothyBaldwin `str` is an input operand, not an output operand; the `puts` function doesn't write to the string at all. As for the clobbers, I'll assume you're right: ["Across calls, these registers must be preserved: RBX, RBP, RDI, RSI, R12, R13, R14, and R15."](https://msdn.microsoft.com/en-us/magazine/cc300794.aspx). As for shadow space, 48 bytes should be enough: you're only required to reserve 32 bytes, and adding the 8 bytes for the `str` pointer, 4 bytes for `ret`, and 4 bytes to preserve stack alignment since RSP must be 16-byte aligned results in 48 bytes. Is more than 48 required? –  Apr 19 '15 at 20:07
  • RCX is clobbered by puts and it is the outputness of `str` that informs the compiler of this, a separate output could be clearer. The inline assembler source does not reserve shadow space, sometimes, like is this case, undefined behaviour works as intended, sometimes it doesn’t. – Timothy Baldwin Apr 19 '15 at 20:55

0 Answers0