当前位置 : 主页 > 手机开发 > 其它 >

Do not hybrid compile and link source code by using VC and GCC!

来源:互联网 收集:自由互联 发布时间:2021-06-12
Do not hybrid compile and link source code by using VC and GCC! GCC owns cross-compiling abilities in Windows. The normal environment is MinGW or Cygwin. This article focuses on the MinGW, and others are similar with it. If comparing with t

Do not hybrid compile and link source code by using VC and GCC!

 

GCC owns cross-compiling abilities in Windows. The normal environment is MinGW or Cygwin. This article focuses on the MinGW, and others are similar with it.

If comparing with the calling convention of VC, GCC has some gaps in the environment of MinGW/Cygwin. The program will be crashed if SSEx instructions are adopted in the source coded built by GCC, however they are linked with VC. It is a common case, such as many open source codes are ported from Linux.

To ease to understand these serious risks, the article will set about an example. If readers follow it step by step, the result will become obvious.

 

Preface:


1.  foo_gcc.c/foo_gcc.h is compiled by GCC.

2.  foo_vc_call_gcc.c is compiled by VC.

 

C Source codes:

 

1. foo_gcc.h/c

Header file

Source code

#ifndef _FOO_GCC_H

#define _FOO_GCC_H

 

#if defined( __cplusplus ) || defined( c_plusplus )

extern "C" {

#endif

 

#ifdef BUILD_DLL

#define GCC_API __declspec( dllexport )

#else

#define GCC_API __declspec( dllimport )

#endif

 

GCC_API int foo_gcc( int i );

 

#if defined( __cplusplus ) || defined( c_plusplus )

}

#endif

 

#endif

#include "foo_gcc.h"

 

GCC_API  int foo_gcc( int i )

{

    char unused[ 16 ] __attribute__ ((__aligned__ ( 16 )));

 

    __asm__ __volatile__( "movdqa %%xmm0, %0/n/t" : "=m"(unused) );

   

    return i * i;

}

 

2. foo_vc_call_gcc.c

#include "foo_gcc.h"

 

int foo_vc( int i )

{

    __declspec( align( 16 ) ) char unused[ 16 ];

   

    _asm

    {

        lea esi, unused       

        movdqa xmm0, xmmword ptr[ esi ]

    }

    i = foo_gcc( i );

 

    return i * i;

}

 

int main( void )

{

    foo_vc( 10 );

   

    return 0;

}

 

Compile

 

A bash file a convenient for this boring task, and please refer it herein.

#!/bin/sh

 

rm ./foo_gcc.o

rm ./foo_gcc.dll

rm ./foo_gcc.s

rm ./foo_gcc.lib

rm ./libfoo_gcc.a

rm ./foo_gcc.exp

rm ./foo_gcc.def

gcc -S ./foo_gcc.c

gcc -c -O3 -DBUILD_DLL ./foo_gcc.c

gcc -shared -o ./foo_gcc.dll ./foo_gcc.o -Wl,--out-implib,./libfoo_gcc.a

pexports.exe ./foo_gcc.dll > ./foo_gcc.def

lib /machine:i386 /def:foo_gcc.def /out:foo_gcc.lib

 

rm ./foo_vc_call_gcc.obj

rm ./foo_vc_call_gcc.asm

rm ./foo_vc_call_gcc.exe

cl -Fa -O2 ./foo_vc_call_gcc.c ./foo_gcc.lib

Reader can refer to How to use GCC to build DLL by DEF file in MinGW? and How to generate DLL files by GCC in the MinGW? articles to build DLL by GCC in MinGW.

 

Execute

 

$ ./foo_vc_call_gcc.exe

What will be happened? I think that the program will be crashed in your system, right?!

 

Herein, you may be wonder why?????? Please keep patience! It is an issue of the calling convention. Before making it clear, the source codes of ASM must be investigated.

 

Root cause

 

1. foo_gcc.s

Foo_gcc.s

       .file  "foo_gcc.c"

       .text

.globl _foo_gcc

       .def  _foo_gcc;       .scl  2;    .type       32;   .endef

_foo_gcc:

       pushl      %ebp

       movl %esp, %ebp

       subl $24, %esp

/APP

 # 7 "./foo_gcc.c" 1

       movdqa %xmm0, -24(%ebp)

      

 # 0 "" 2

/NO_APP

       movl 8(%ebp), %eax

       imull       8(%ebp), %eax

       leave

       ret

A. eax is used to transfer parameter. It is __stdcall calling function of DLL.

B. eax is used to return value.

C. GCC uses static strategy to align stack if SSEx instructions are used. The AMS marked by red color shows the rule(32bytes = 4bytes (returning address) + 4bytes (save ebp) + 24bytes). It hits that the esp must be aligned when invoking foo_gcc function.

 

2. foo_vc_call_gcc.asm

foo_icl_call_gcc.asm

; Listing generated by Microsoft (R) Optimizing Compiler Version 14.00.50727.42

 

       TITLE      c:/temp/risks/foo_vc_call_gcc.c

       .686P

       .XMM

       include listing.inc

       .model    flat

 

INCLUDELIB LIBCMT

INCLUDELIB OLDNAMES

 

PUBLIC   __$ArrayPad$

PUBLIC   _foo_vc

EXTRN    __imp__foo_gcc:PROC

EXTRN    ___security_cookie:DWORD

EXTRN    @__security_check_cookie@4:PROC

; Function compile flags: /Ogtpy

;      COMDAT _foo_vc

_TEXT     SEGMENT

_unused$ = -32                                         ; size = 16

__$ArrayPad$ = -4                             ; size = 4

_i$ = 8                                              ; size = 4

_foo_vc   PROC                                        ; COMDAT

; File c:/temp/risks/foo_vc_call_gcc.c

; Line 4

       push       ebp

       mov ebp, esp

       and  esp, -16                       ; fffffff0H

       sub  esp, 44                               ; 0000002cH

       mov eax, DWORD PTR ___security_cookie

       xor   eax, esp

       mov DWORD PTR __$ArrayPad$[esp+44], eax

       push       esi

; Line 9

       lea   esi, DWORD PTR _unused$[esp+48]

; Line 10

       movdqa   xmm0, XMMWORD PTR [esi]

; Line 12

       mov eax, DWORD PTR _i$[ebp]

       push       eax

       call  DWORD PTR __imp__foo_gcc

; Line 14

       imul eax, eax

; Line 15

       mov ecx, DWORD PTR __$ArrayPad$[esp+52]

       add  esp, 4

       pop  esi

       xor   ecx, esp

       call  @__security_check_cookie@4

       mov esp, ebp

       pop  ebp

       ret   0

_foo_vc   ENDP

_TEXT     ENDS

PUBLIC   __$ArrayPad$

PUBLIC   _main

; Function compile flags: /Ogtpy

;      COMDAT _main

_TEXT     SEGMENT

_unused$699 = -32                                   ; size = 16

__$ArrayPad$ = -4                             ; size = 4

_main     PROC                                        ; COMDAT

; Line 18

       push       ebp

       mov ebp, esp

       and  esp, -16                       ; fffffff0H

       sub  esp, 44                               ; 0000002cH

       mov eax, DWORD PTR ___security_cookie

       xor   eax, esp

       mov DWORD PTR __$ArrayPad$[esp+44], eax

       push       esi

; Line 19

       lea   esi, DWORD PTR _unused$699[esp+48]

       movdqa   xmm0, XMMWORD PTR [esi]

       push       10                                ; 0000000aH

       call  DWORD PTR __imp__foo_gcc

; Line 22

       mov ecx, DWORD PTR __$ArrayPad$[esp+52]

       add  esp, 4

       pop  esi

       xor   ecx, esp

       xor   eax, eax

       call  @__security_check_cookie@4

       mov esp, ebp

       pop  ebp

       ret   0

_main     ENDP

_TEXT     ENDS

END

A. Stack is used to transfer parameter.

B. eax is used to return value.

C. Dynamically align the address of stack. “and esp, -16” equals “and esp, 0xFFFFFFF0”, and it makes address aligned by 16bytes. However, when invoking foo_gcc function, the esp does not been aligned ((44 + 4 + 4) % 16 = 4, they are marked by red color). The operations will lead to faults of SSEx instruction, and the symptom is that program is crashed in the runtime.

 

 

Summarization:

1. The calling convention among compilers has gaps, and it will lead to crash if SSEx instructions are adopted.

2. It is a common issue, so all compilers compatible with VC have same results.

3. If the functions using SSEx instructions are not APIs of DLL, maybe the program can be run. In fact, it depends on the optimizing method of GCC. The risks are big!

网友评论