gcc - Calling printf in extended inline ASM -


i'm trying output same string twice in extended inline asm in gcc, on 64-bit linux.

int main() {     const char* test = "test\n";      asm(         "movq %[test], %%rdi\n"    // debugger shows rdi = *address of string*           "movq $0, %%rax\n"          "push %%rbp\n"         "push %%rbx\n"         "call printf\n"                  "pop %%rbx\n"         "pop %%rbp\n"          "movq %[test], %%rdi\n" // debugger shows rdi = 0         "movq $0, %%rax\n"          "push %%rbp\n"         "push %%rbx\n"         "call printf\n"              "pop %%rbx\n"         "pop %%rbp\n"         :          :  [test] "g" (test)         : "rax", "rbx","rcx", "rdx", "rdi", "rsi", "rsp"         );      return 0; } 

now, string outputted once. have tried many things, guess missing caveats calling convention. i'm not sure if clobber list correct or if need save , restore rbp , rbx @ all.

why string not outputted twice?

looking debugger shows me somehow when string loaded rdi second time has value 0 instead of actual address of string.

i cannot explain why, seems after first call stack corrupted? have restore in way?

specific problem code: rdi not maintained across function call (see below). correct before first call printf clobbered printf. you'll need temporarily store elsewhere first. register isn't clobbered convenient. can save copy before printf, , copy rdi after.


i not recommend doing suggesting (making function calls in inline assembler). difficult compiler optimize things.

among other things 64-bit system v abi mandates 128-byte red zone. means can't push onto stack without potential corruption. remember: doing call pushes return address on stack. quick , dirty way resolve problem subtract 128 rsp when inline assembler starts , add 128 when finished.

the 128-byte area beyond location pointed %rsp considered reserved , shall not modified signal or interrupt handlers.8 therefore, functions may use area temporary data not needed across function calls. in particular, leaf functions may use area entire stack frame, rather adjusting stack pointer in prologue , epilogue. area known red zone.

another issue concerned requirement stack 16-byte aligned (or possibly 32-byte aligned depending on parameters) prior function call. required 64-bit abi well:

the end of input argument area shall aligned on 16 (32, if __m256 passed on stack) byte boundary. in other words, value (%rsp + 8) multiple of 16 (32) when control transferred function entry point.

note: requirement 16-byte alignment upon call function required on 32-bit linux gcc >= 4.5:

in context of c programming language, function arguments pushed on stack in reverse order. in linux, gcc sets de facto standard calling conventions. since gcc version 4.5, stack must aligned 16-byte boundary when calling function (previous versions required 4-byte alignment.)

since call printf in inline assembler should ensure align stack 16-byte boundary before making call.

you have aware when calling function registers preserved across function call , not. may clobbered function call listed in figure 3.4 of 64-bit abi (see previous link). registers rax, rcx, rdx, rd8-rd11, xmm0-xmm15, mmx0-mmx7, st0-st7 . these potentially destroyed should put in clobber list if don't appear in input , output constraints.

the following code should satisfy of conditions ensure inline assembler calls function not inadvertently clobber registers, preserves redzone, , maintains 16-byte alignment before call:

int main() {     const char* test = "test\n";     long dummyreg; /* dummyreg used allow gcc pick available register */      __asm__(         "add $-128, %%rsp\n\t"   /* skip current redzone */         "mov %%rsp, %[temp]\n\t" /* copy rsp available register */         "and $-16, %%rsp\n\t"    /* align stack 16-byte boundary */         "mov %[test], %%rdi\n\t" /* rdi address of string */         "xor %%eax, %%eax\n\t"   /* variadic function set eax. case 0 */         "call printf\n\t"         "mov %[test], %%rdi\n\t" /* rdi address of string again */         "xor %%eax, %%eax\n\t"   /* variadic function set eax. case 0 */         "call printf\n\t"         "mov %[temp], %%rsp\n\t" /* restore rsp */         "sub $-128, %%rsp\n\t"   /* add 128 rsp restore orig */          :  [temp]"=&r"(dummyreg) /* allow gcc pick available output register. modified                                     before inputs consumed use & clobber*/         :  [test]"r"(test),      /* choose available register input operand */          : "rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11",           "xmm0","xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",           "xmm8","xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",           "mm0","mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm6",           "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)" "st(7)"         );      return 0; } 

i used input constraint allow template choose available register used pass str address through. ensures have register store str address between calls printf. assembler template choose available location storing rsp temporarily using dummy register. registers chosen not include 1 chosen/listed input/output/clobber operand.

this looks messy, failure correctly lead problems later program becomes more complex. why calling functions conform system v 64-bit abi within inline assembler not best way things.


Comments

Popular posts from this blog

ios - RestKit 0.20 — CoreData: error: Failed to call designated initializer on NSManagedObject class (again) -

java - Digest auth with Spring Security using javaconfig -

laravel - PDOException in Connector.php line 55: SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: YES) -