Page 1 of 2

GCC fails to reserve space for local variables

Posted: Mon Dec 31, 2012 6:18 pm
by rdos
I'm really puzzled as why GCC obviously generates incorrect code. It uses references to rbp that are below stack buttom, and thus would be overwritten by stack operations. Comparing to something that works, GCC should have included "sub rsp,xx" or called some stack-validator.

Disassembled code:

Code: Select all

00000180e00000b0 <main>:
 180e00000b0:   55                      push   %rbp
 180e00000b1:   48 89 e5                mov    %rsp,%rbp
 180e00000b4:   53                      push   %rbx
 180e00000b5:   48 8d 05 6c 02 00 00    lea    0x26c(%rip),%rax        # ffffffffe0000328 <_end+0xfffffe7ebffff970>
 180e00000bc:   48 89 45 e8             mov    %rax,-0x18(%rbp)
 180e00000c0:   c6 45 e7 00             movb   $0x0,-0x19(%rbp)
 180e00000c4:   b8 08 00 00 00          mov    $0x8,%eax
 180e00000c9:   48 83 c0 01             add    $0x1,%rax
 180e00000cd:   48 89 45 d8             mov    %rax,-0x28(%rbp)
 180e00000d1:   48 8b 45 e8             mov    -0x18(%rbp),%rax
 180e00000d5:   0f b6 4d e7             movzbl -0x19(%rbp),%ecx
 180e00000d9:   4c 8b 55 d8             mov    -0x28(%rbp),%r10
 180e00000dd:   48 89 c7                mov    %rax,%rdi
 180e00000e0:   51                      push   %rcx
 180e00000e1:   57                      push   %rdi
 180e00000e2:   41 54                   push   %r12
 180e00000e4:   41 57                   push   %r15
 180e00000e6:   4d 89 d4                mov    %r10,%r12
 180e00000e9:   49 c7 c7 7b 00 00 00    mov    $0x7b,%r15
 180e00000f0:   0f 05                   syscall
 180e00000f2:   72 06                   jb     180e00000fa <main+0x4a>
 180e00000f4:   48 0f b7 c3             movzwq %bx,%rax
 180e00000f8:   eb 03                   jmp    180e00000fd <main+0x4d>
 180e00000fa:   48 31 c0                xor    %rax,%rax
 180e00000fd:   41 5f                   pop    %r15
 180e00000ff:   41 5c                   pop    %r12
 180e0000101:   5f                      pop    %rdi
 180e0000102:   59                      pop    %rcx
 180e0000103:   48 89 45 d0             mov    %rax,-0x30(%rbp)
 180e0000107:   48 8b 45 d0             mov    -0x30(%rbp),%rax
 180e000010b:   89 45 f4                mov    %eax,-0xc(%rbp)
 180e000010e:   83 7d f4 00             cmpl   $0x0,-0xc(%rbp)
 180e0000112:   74 69                   je     180e000017d <main+0xcd>
 180e0000114:   8b 45 f4                mov    -0xc(%rbp),%eax
 180e0000117:   89 45 cc                mov    %eax,-0x34(%rbp)
 180e000011a:   48 8b 05 e7 fe ff 3f    mov    0x3ffffee7(%rip),%rax        # 20000008 <usergate_entries+0x1ffffeb4>
 180e0000121:   48 89 45 c0             mov    %rax,-0x40(%rbp)
 180e0000125:   c7 45 bc f4 01 00 00    movl   $0x1f4,-0x44(%rbp)
 180e000012c:   8b 45 cc                mov    -0x34(%rbp),%eax
 180e000012f:   8b 4d bc                mov    -0x44(%rbp),%ecx
 180e0000132:   48 8b 7d c0             mov    -0x40(%rbp),%rdi
 180e0000136:   89 c3                   mov    %eax,%ebx
 180e0000138:   51                      push   %rcx
 180e0000139:   57                      push   %rdi
 180e000013a:   41 54                   push   %r12
 180e000013c:   41 57                   push   %r15
 180e000013e:   49 89 cc                mov    %rcx,%r12
 180e0000141:   49 89 c8                mov    %rcx,%r8
 180e0000144:   49 c7 c7 86 00 00 00    mov    $0x86,%r15
 180e000014b:   0f 05                   syscall
 180e000014d:   73 03                   jae    180e0000152 <main+0xa2>
 180e000014f:   48 31 c0                xor    %rax,%rax
 180e0000152:   41 5f                   pop    %r15
 180e0000154:   41 5c                   pop    %r12
 180e0000156:   5f                      pop    %rdi
 180e0000157:   59                      pop    %rcx
 180e0000158:   48 89 45 b0             mov    %rax,-0x50(%rbp)
 180e000015c:   48 8b 45 b0             mov    -0x50(%rbp),%rax
 180e0000160:   89 45 f0                mov    %eax,-0x10(%rbp)
 180e0000163:   8b 45 f4                mov    -0xc(%rbp),%eax
 180e0000166:   89 45 ac                mov    %eax,-0x54(%rbp)
 180e0000169:   44 8b 55 ac             mov    -0x54(%rbp),%r10d
 180e000016d:   44 89 d3                mov    %r10d,%ebx
 180e0000170:   41 57                   push   %r15
 180e0000172:   49 c7 c7 7d 00 00 00    mov    $0x7d,%r15
 180e0000179:   0f 05                   syscall
 180e000017b:   41 5f                   pop    %r15
 180e000017d:   b8 00 00 00 00          mov    $0x0,%eax
 180e0000182:   5b                      pop    %rbx
 180e0000183:   5d                      pop    %rbp
 180e0000184:   c3                      retq
Actually code:

Code: Select all

#include "string.h"

#undef RDOSAPI
#define RDOSAPI static inline volatile __attribute__ ((always_inline))

#define UserGateRetEax(nr, ret) \
  asm ( \
    "pushq %%r15\n\t" \
    "movq %1, %%r15\n\t" \
    "syscall\n\t" \
    "popq %%r15\n\t" \
    : "=a" (ret) : "i" (nr) : "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r11"\
    );

#define UserGateEdiEcxRetEbx(nr, rdi, rcx, len, ret) \
  asm ( \
    "pushq %%rcx\n\t" \
    "pushq %%rdi\n\t" \
    "pushq %%r12\n\t" \
    "pushq %%r15\n\t" \
    "movq %4,%%r12\n\t" \
    "movq %1, %%r15\n\t" \
    "syscall\n\t" \
    "jc 1f\n\t" \
    "movzx %%bx,%%rax\n\t" \
    "jmp 2f\n\t" \
    "1: \n\t" \
    "xorq %%rax,%%rax\n\t" \
    "2: \n\t" \
    "popq %%r15\n\t" \
    "popq %%r12\n\t" \
    "popq %%rdi\n\t" \
    "popq %%rcx\n\t" \
    : "=a" (ret) : "i" (nr), "D" (rdi), "c" (rcx), "r" (len) : "rbx", "rdx", "rsi", "r8", "r9", "r11", "cc" \
    );

#define UserGateEbxEcxEdiRetEax(nr, rbx, rcx, rdi, ret) \
  asm ( \
    "pushq %%rcx\n\t" \
    "pushq %%rdi\n\t" \
    "pushq %%r12\n\t" \
    "pushq %%r15\n\t" \
    "movq %%rcx,%%r12\n\t" \
    "movq %%rcx,%%r8\n\t" \
    "movq %1, %%r15\n\t" \
    "syscall\n\t" \
    "jnc 1f\n\t" \
    "xorq %%rax,%%rax\n\t" \
    "1: \n\t" \
    "popq %%r15\n\t" \
    "popq %%r12\n\t" \
    "popq %%rdi\n\t" \
    "popq %%rcx\n\t" \
    : "=a" (ret) : "i" (nr), "b" (rbx), "c" (rcx), "D" (rdi) : "rdx", "rsi", "r8", "r9", "r11", "cc" \
    );

#define UserGateEbx(nr, rbx) \
  asm ( \
    "pushq %%r15\n\t" \
    "movq %0, %%r15\n\t" \
    "syscall\n\t" \
    "popq %%r15\n\t" \
    : : "i" (nr), "b" (rbx) : "rax", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r11" \
    );

RDOSAPI long RdosOpenFile(const char *FileName, char Access)
{
    long res;
    long size = strlen(FileName) + 1;
    UserGateEdiEcxRetEbx(usergate_open_file, FileName, Access, size, res);
    return res;
}

RDOSAPI long RdosReadFile(int Handle, void *Buf, int Size)
{
    long res;
    UserGateEbxEcxEdiRetEax(usergate_read_file, Handle, Size, Buf, res);
    return res;
}

RDOSAPI RdosCloseFile(int Handle)
{
    UserGateEbx(usergate_close_file, Handle);
}

char buf[500];
  
int main()
{
    int handle;
    int count;

    handle = RdosOpenFile("test.exe", 0);
    if (handle)
    {
        count = RdosReadFile(handle, buf, 500);
        RdosCloseFile(handle);
    }
       
    return 0;
}

Re: GCC fails to reserve space for local variables

Posted: Mon Dec 31, 2012 6:37 pm
by Owen
The AMD64 SystemV ABI defines a 256 byte "Red zone" below RSP that is defined to be untouched by signal handlers/the OS/etc. This gives leaf functions free stack space

Obviously this is not possible in the kernel. There people doable the feature.

You may wish to learn the many flags that GCC accepts, and read the ABI specification more closely in future

Re: GCC fails to reserve space for local variables

Posted: Mon Dec 31, 2012 6:47 pm
by rdos
Owen wrote:The AMD64 SystemV ABI defines a 256 byte "Red zone" below RSP that is defined to be untouched by signal handlers/the OS/etc. This gives leaf functions free stack space
That would mean that inline assembly is not allowed to use push/pop, since those would destroy things in the red zone.

What about if I define the code as functions instead of inlines. Would that mean there would be no code using the red zone?

Is there alternative ABIs that doesn't suffer from this problem?

Besides, this would also mean that the syscall entry-point and exit point would not be allowed to use the application stack, and must make sure no interrupts could occur until the stack is switched.

Re: GCC fails to reserve space for local variables

Posted: Mon Dec 31, 2012 7:12 pm
by Owen
rdos wrote:
Owen wrote:The AMD64 SystemV ABI defines a 256 byte "Red zone" below RSP that is defined to be untouched by signal handlers/the OS/etc. This gives leaf functions free stack space
That would mean that inline assembly is not allowed to use push/pop, since those would destroy things in the red zone.
push and pop in inline assembly are almost always wrong. Mark your clobbers/inputs outputs as appropriate and let the compiler do the saving.
What about if I define the code as functions instead of inlines. Would that mean there would be no code using the red zone?
No
Is there alternative ABIs that doesn't suffer from this problem?
Is not a problem, merely a quirk that needs to be understood. For the kernel, tell the compiler not to use the red zone.
Besides, this would also mean that the syscall entry-point and exit point would not be allowed to use the application stack, and must make sure no interrupts could occur until the stack is switched.
The application could quite easily load RSP with a kernel address before invoking the sys call instruction anyhow. The application stack should never be considered trustworthy.

Bugs that Intel class as architectural features in their implementation of AMD64 have caused security holes in pretty much every OS. It's an area which needs caution. See http://blog.xen.org/index.php/2012/06/1 ... scalation/

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 3:47 am
by rdos
Owen wrote: push and pop in inline assembly are almost always wrong. Mark your clobbers/inputs outputs as appropriate and let the compiler do the saving.
Yes. I just couldn't figure out how to handle registers that are used for input but are clobbered. I suppose I needed to learn how to do handle that.
Owen wrote: Is not a problem, merely a quirk that needs to be understood. For the kernel, tell the compiler not to use the red zone.
I need some quirk in the single-step handler. Probably I'll decode "pop rsp" in combination with sysret, and then set TR flag in R11 and do a go instead of a single step. That also means I can remove the IST for single step again.
Owen wrote: The application could quite easily load RSP with a kernel address before invoking the sys call instruction anyhow. The application stack should never be considered trustworthy.

Bugs that Intel class as architectural features in their implementation of AMD64 have caused security holes in pretty much every OS. It's an area which needs caution. See http://blog.xen.org/index.php/2012/06/1 ... scalation/
I think I pass that problem. My kernel doesn't save anything on the application stack. It also saves RCX on top of the kernel stack, so it is not possible to manipulate it in the syscall in kernel. Syscall also is setup to disable interrupts.

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 4:19 am
by bluemoon
rdos wrote:Is there alternative ABIs that doesn't suffer from this problem?
You can use the same ABI, but disable red-zone with -mno-red-zone

My CFLAGS for kernel look like this:
CFLAGS =-ffreestanding -masm=intel -std=c99 -O2 -mcmodel=kernel -mno-red-zone \
-mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow -c -D_$(ARCH) -Werror $(CWARN)

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 9:42 am
by rdos
I've tweaked the defines a little, and removed all push/pop. Now the optimized code looks awfully close to hand-written assembly, which is nice:

Code: Select all

00000180e00000b0 <main>:
 180e00000b0:   41 56                   push   %r14
 180e00000b2:   41 54                   push   %r12
 180e00000b4:   53                      push   %rbx
 180e00000b5:   41 bc 09 00 00 00       mov    $0x9,%r12d
 180e00000bb:   41 be 7b 00 00 00       mov    $0x7b,%r14d
 180e00000c1:   41 b8 00 00 00 00       mov    $0x0,%r8d
 180e00000c7:   48 8d 3d ea 01 00 00    lea    0x1ea(%rip),%rdi        # ffffffffe00002b8 <_end+0xfffffe7ebffff900>
 180e00000ce:   0f 05                   syscall
 180e00000d0:   72 06                   jb     180e00000d8 <main+0x28>
 180e00000d2:   48 0f b7 c3             movzwq %bx,%rax
 180e00000d6:   eb 03                   jmp    180e00000db <main+0x2b>
 180e00000d8:   48 31 c0                xor    %rax,%rax
 180e00000db:   85 c0                   test   %eax,%eax
 180e00000dd:   41 89 c1                mov    %eax,%r9d
 180e00000e0:   74 2d                   je     180e000010f <main+0x5f>
 180e00000e2:   41 b8 f4 01 00 00       mov    $0x1f4,%r8d
 180e00000e8:   41 bc f4 01 00 00       mov    $0x1f4,%r12d
 180e00000ee:   41 be 86 00 00 00       mov    $0x86,%r14d
 180e00000f4:   48 8b 3d 0d ff ff 3f    mov    0x3fffff0d(%rip),%rdi        # 20000008 <usergate_entries+0x1ffffeb4>
 180e00000fb:   89 c3                   mov    %eax,%ebx
 180e00000fd:   0f 05                   syscall
 180e00000ff:   73 03                   jae    180e0000104 <main+0x54>
 180e0000101:   48 31 c0                xor    %rax,%rax
 180e0000104:   41 be 7d 00 00 00       mov    $0x7d,%r14d
 180e000010a:   44 89 cb                mov    %r9d,%ebx
 180e000010d:   0f 05                   syscall
 180e000010f:   5b                      pop    %rbx
 180e0000110:   41 5c                   pop    %r12
 180e0000112:   31 c0                   xor    %eax,%eax
 180e0000114:   41 5e                   pop    %r14
 180e0000116:   c3                      retq
 
Here is how the syscalls are defined: (which wasn't easy to get to)

Code: Select all


#include "string.h"

#undef RDOSAPI
#define RDOSAPI static inline volatile __attribute__ ((always_inline))

#define UserGateSetup(nr) \
  asm volatile ( \
    "movl %0, %%r14d\n\t" \
     : : "g" (nr) : "r14" \
   );

#define UserGateClobberRdi \
  asm volatile ( \
    "\n\t" \
     : : : "rdi" \
   );

#define UserGateSetupRcx(val) \
  asm volatile ( \
    "movl %0, %%r8d\n\t" \
     : : "g" (val) : "r8" \
   );

#define UserGateSetupPar0(val) \
  asm volatile ( \
    "movl %0, %%r12d\n\t" \
     : : "g" (val) : "r12" \
   );

#define UserGateSetupParRcx(val) \
  asm volatile ( \
    "movl %0, %%r8d\n\t" \
    "movl %0, %%r12d\n\t" \
     : : "g" (val) : "r8", "r12" \
   );

#define UserGateNoPar \
  asm volatile ( \
    "syscall\n\t" \
    : : : "rax", "rbx", "rcx" ,"rdx", "rsi", "rdi", "r11", "cc" \
    );

#define UserGateEdiRetEbx(rdi, res) \
  asm volatile ( \
    "syscall\n\t" \
    "jc 1f\n\t" \
    "movzx %%bx,%%rax\n\t" \
    "jmp 2f\n\t" \
    "1: \n\t" \
    "xorq %%rax,%%rax\n\t" \
    "2: \n\t" \
    : "=a" (res) : "D" (rdi) : "rbx", "rcx" ,"rdx", "rsi", "r11", "cc" \
    ); \
    UserGateClobberRdi;

#define UserGateEbxEdiRetEax(rbx, rdi, res) \
  asm volatile ( \
    "syscall\n\t" \
    "jnc 1f\n\t" \
    "xorq %%rax,%%rax\n\t" \
    "1: \n\t" \
    : "=a" (res) : "b" (rbx), "D" (rdi) : "rcx" ,"rdx", "rsi", "r11", "cc" \
    ); \
    UserGateClobberRdi;

#define UserGateEbx(rbx) \
  asm volatile ( \
    "syscall\n\t" \
    : : "b" (rbx) : "rax", "rcx" ,"rdx", "rsi", "rdi", "r11" \
    );

RDOSAPI int RdosOpenFile(const char *FileName, char Access)
{
    int res;
    int size = strlen(FileName) + 1;
    UserGateSetupPar0(size);    
    UserGateSetup(usergate_open_file);
    UserGateSetupRcx(Access);
    UserGateEdiRetEbx(FileName, res);
    return res;
}

RDOSAPI int RdosReadFile(int Handle, void *Buf, int Size)
{
    int res;
    UserGateSetupParRcx(Size);    
    UserGateSetup(usergate_read_file);
    UserGateEbxEdiRetEax(Handle, Buf, res);
    return res;
}

RDOSAPI RdosCloseFile(int Handle)
{
    UserGateSetup(usergate_close_file);
    UserGateEbx(Handle);
}

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 9:54 am
by bluemoon
rdos wrote: #define UserGateSetupRcx(val) \
asm volatile ( \
"movl %0, %%r8d\n\t" \
: : "g" (val) : "r8" \
);

#define UserGateSetupPar0(val) \
asm volatile ( \
"movl %0, %%r12d\n\t" \
: : "g" (val) : "r12" \
);
Note that gcc would have no idea you want r8, r12 value to stay between these defines and the syscall; so in theory this can bite you later.

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 10:53 am
by rdos
bluemoon wrote:
rdos wrote: #define UserGateSetupRcx(val) \
asm volatile ( \
"movl %0, %%r8d\n\t" \
: : "g" (val) : "r8" \
);

#define UserGateSetupPar0(val) \
asm volatile ( \
"movl %0, %%r12d\n\t" \
: : "g" (val) : "r12" \
);
Note that gcc would have no idea you want r8, r12 value to stay between these defines and the syscall; so in theory this can bite you later.
The problem with both of these is that neither r8, nor r12 can be specified as a fixed register, but needs to use "g" instead. When I mix them with the fixed registers, GCC seems to output the wrong code.

Since all the macros are "asm volatile", GCC should not be able to omit or rearrange them, which should mean it should work.

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 11:36 am
by bluemoon
asm volatile only assure your asm block do not get eliminated by optimizer, nor moving out of a loop (by satisfying side-effects). It can be re-ordered; it's not in your case just because your current code sequence happens to be simple.

Think about this:

Code: Select all

RDOSAPI int RdosOpenFile(const char *FileName, char Access){
    int res;
    UserGateSetup(usergate_open_file);
    int size = strlen(FileName) + 1;
    UserGateSetupPar0(size);    
    UserGateSetupRcx(Access);
    UserGateEdiRetEbx(FileName, res);
    return res;
}
Now, gcc may just make use of r14 for the size variable, and it's totally legal; it may also make use of register other than r14 and generate correct code for you, but it's pure luck.

ps. To prevent gcc even re-order your code, simply clobber memory.

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 12:11 pm
by Owen
In your previous post on the topic I demonstrated how to make GCC place your parameter in a specific register
  1. Use the explicit register variables extension to declare the register you desire, e.g.

    Code: Select all

        register int inEAX asm("eax");
    
  2. Pass said variable using the "r" constraint, e.g

    Code: Select all

       asm("syscall" : "+r"(inEAX));
    
Example syscall code:

Code: Select all

#define SYSCALL5(result, number, arg0, arg1, arg2, arg3, arg4) do { \
    register uintptr_t _RETN asm("rax") = (number); \
    register typeof(arg0) _Arg0 asm("rdi") = (arg0); \
    register typeof(arg1) _Arg1 asm("rsi") = (arg1); \
    register typeof(arg2) _Arg2 asm("r10") = (arg2); \
    register typeof(arg3) _Arg3 asm("r8") = (arg3); \
    register typeof(arg3) _Arg4 asm("r9") = (arg4); \
    asm volatile("syscall" \
        : "+r"(_RETN) \
       :: "r"(_Arg0), "r"(_Arg1), "r"(_Arg2), "r"(_Arg3), "r"(_Arg4) \
        : "memory" \
    ); \
    *result = ((typeof(*result)) _RETN; \
} while(0)
GCC guarantees to place any register variables in their declared registers whenever passing them in using the "r" constraint.

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 12:42 pm
by rdos
Owen wrote:In your previous post on the topic I demonstrated how to make GCC place your parameter in a specific register
  1. Use the explicit register variables extension to declare the register you desire, e.g.

    Code: Select all

        register int inEAX asm("eax");
    
  2. Pass said variable using the "r" constraint, e.g

    Code: Select all

       asm("syscall" : "+r"(inEAX));
    
Example syscall code:

Code: Select all

#define SYSCALL5(result, number, arg0, arg1, arg2, arg3, arg4) do { \
    register uintptr_t _RETN asm("rax") = (number); \
    register typeof(arg0) _Arg0 asm("rdi") = (arg0); \
    register typeof(arg1) _Arg1 asm("rsi") = (arg1); \
    register typeof(arg2) _Arg2 asm("r10") = (arg2); \
    register typeof(arg3) _Arg3 asm("r8") = (arg3); \
    register typeof(arg3) _Arg4 asm("r9") = (arg4); \
    asm volatile("syscall" \
        : "+r"(_RETN) \
       :: "r"(_Arg0), "r"(_Arg1), "r"(_Arg2), "r"(_Arg3), "r"(_Arg4) \
        : "memory" \
    ); \
    *result = ((typeof(*result)) _RETN; \
} while(0)
GCC guarantees to place any register variables in their declared registers whenever passing them in using the "r" constraint.
The problem with the above code, is that when SYSCALL5 is used multiple times in the same procedure, GCC will complain about already defined variables. That's one of the alternatives I tried and discarded. I was able to hide it as a "scope" variable by putting {} around the declaration, but that can also have unwanted side-effects. I was not able to place the whole inline within a private scope.

Additionally, it seems like GCC can remove these assignments in the optimization process, and it was not possible to use volatile with register to make GCC always keep these asignments.

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 12:44 pm
by rdos
bluemoon wrote:asm volatile only assure your asm block do not get eliminated by optimizer, nor moving out of a loop (by satisfying side-effects). It can be re-ordered; it's not in your case just because your current code sequence happens to be simple.

Think about this:

Code: Select all

RDOSAPI int RdosOpenFile(const char *FileName, char Access){
    int res;
    UserGateSetup(usergate_open_file);
    int size = strlen(FileName) + 1;
    UserGateSetupPar0(size);    
    UserGateSetupRcx(Access);
    UserGateEdiRetEbx(FileName, res);
    return res;
}
Now, gcc may just make use of r14 for the size variable, and it's totally legal; it may also make use of register other than r14 and generate correct code for you, but it's pure luck.

ps. To prevent gcc even re-order your code, simply clobber memory.
So if I put a "memory" in the clobbered field on each of these, they should always work?

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 12:54 pm
by bluemoon
No. It's not guarantee that register state remain unchanged between individual block of inline assembly.
I use external assembly code to handle syscall (and I have the same ABI used for gcc code generation), for example:

Code: Select all

; ----------------------------------------------
; int open ( const char * file, int flags, int mode );
open:
    mov     eax, 2
    mov     r10, rcx    ; rcx is used by syscall
    syscall
    ret
; ----------------------------------------------
; int close ( int fd );
close:
    mov     eax, 3
    syscall
    ret
; ----------------------------------------------
; int read ( int fd, char *buf, int nbytes );
read:
    mov     eax, 4
    mov     r10, rcx    ; rcx is used by syscall
    syscall
    ret
It's not like you'd do syscall million time per second, so IMO the extra "call" is neglectable.
Furthermore, by moving the whole stuff external, it make life much easier without needing the clobber register list, which are defined to be trashed across function.

For inline assembly treatment, owen's solution is by far the best I'd ever see, except that you may need to add a bunch of registers in the clobber register list, and perhaps adding "syscall_linkage attribute" in the block.

Re: GCC fails to reserve space for local variables

Posted: Tue Jan 01, 2013 1:16 pm
by rdos
I don't want to have it in a separate assembler file. Not only because it is slower, but also because it is far harder to write & maintain such a solution.

In worst case scenario, I could remove the inline to make the C-code an external procedure. Then the result is 100% dependable since the code is only compiled in its own content.

But I want to test the alternative first. And I don't want a solution that cannot handle multiple syscalls in the same procedure.

Additionally, I think I could provide the 32-bit interface with conditionals using the same code base. The difference primary is that ECX is passed in ECX instead of R8 (a macro), that the UserGate macro is completely different (uses a series of dbs). The size macro is empty.

Alternative ABIs should also be handled by the compiler.