分享

cygwin跨平台移植 gcc+vc联合使用的方法和注意事项

 quasiceo 2018-04-05


(2012-04-11 15:59:12)

前段时间做一个把linux代码移植到vc上的工程,选择cygwin环境,直接交叉编译通过。后来为了调试方便需要把源代码全部放到vc下编译,在网上搜索相关资料,发现在cygwin官方的FAQ中给出了具体的方法。下面我就里面的关键步骤做一个简单的解释:首先是官方的原文:

 

How do I use cygwin1.dll with Visual Studio or MinGW?
 
  If you want to load the DLL dynamically, read winsup/cygwin/how-cygtls-works.txt and the sample code in winsup/testsuite/cygload to understand how this works. The short version is:

Make sure you have 4K of scratch space at the bottom of your stack.

Invoke cygwin_dll_init():

HMODULE h = LoadLibrary("cygwin1.dll");
void (*init)() = GetProcAddress(h, "cygwin_dll_init");
init();
If you want to link statically from Visual Studio, to my knowledge none of the Cygwin developers have done this, but we have this report from the mailing list that it can be done this way:

Use the impdef program to generate a .def file for the cygwin1.dll (if you build the cygwin dll from source, you will already have a def file)

impdef cygwin1.dll > cygwin1.def
Use the MS VS linker (lib) to generate an import library

lib /def=cygwin1.def /out=cygwin1.lib
Create a file "my_crt0.c" with the following contents

#include <sys/cygwin.h>
#include <stdlib.h>

typedef int (*MainFunc) (int argc, char *argv[], char **env);

void
  my_crt0 (MainFunc f)
  {
    cygwin_crt0(f);
  }
Use gcc in a Cygwin prompt to build my_crt0.c into a DLL (e.g. my_crt0.dll). Follow steps 1 and 2 to generate .def and .lib files for the DLL.

Download crt0.c from the cygwin website and include it in your sources. Modify it to call my_crt0() instead of cygwin_crt0().

view plaincopy to clipboardprint?
 
 
 
 
#ifdef __i386__  
#define FPU_RESERVED 0xF0C0  
#define FPU_DEFAULT  0x033f  
 
 
int __cygwin_crt0_bp = 0;  
#endif  
 
typedef int (*MainFunc) (int argc, char *argv[], char **env);  
 
void  my_crt0 (MainFunc f);  
//extern int main (int argc, char **argv);  
extern int main (int argc, char **argv, char **env);  
//void cygwin_crt0 (int (*main) (int, char **));  
 
void 
mainCRTStartup ()  
 
#ifdef __i386__  
  (void)__builtin_return_address(1);  
  asm volatile ("andl $-16,%%esp" ::: "%esp");  
  if (__cygwin_crt0_bp)  
    asm volatile ("int3");  
 
  
    volatile unsigned short cw;  
 
     
    __asm__ volatile ("fnstcw %0" : "=m" (cw) : );  
 
     
    cw &= FPU_RESERVED;  
    cw |= FPU_DEFAULT;  
 
     
    __asm__ volatile ("fldcw %0" :: "m" (cw));  
  
#endif  
 
  my_crt0 (main);  
 
 
//void WinMainCRTStartup(void) __attribute__ ((alias("mainCRTStartup"))); 

#ifdef __i386__
#define FPU_RESERVED 0xF0C0
#define FPU_DEFAULT  0x033f


int __cygwin_crt0_bp = 0;
#endif

typedef int (*MainFunc) (int argc, char *argv[], char **env);

void  my_crt0 (MainFunc f);
//extern int main (int argc, char **argv);
extern int main (int argc, char **argv, char **env);
//void cygwin_crt0 (int (*main) (int, char **));

void
mainCRTStartup ()
{
#ifdef __i386__
  (void)__builtin_return_address(1);
  asm volatile ("andl $-16,%%esp" ::: "%esp");
  if (__cygwin_crt0_bp)
    asm volatile ("int3");

  {
    volatile unsigned short cw;

   
    __asm__ volatile ("fnstcw %0" : "=m" (cw) : );

   
    cw &= FPU_RESERVED;
    cw |= FPU_DEFAULT;

   
    __asm__ volatile ("fldcw %0" :: "m" (cw));
  }
#endif

  my_crt0 (main);
}

//void WinMainCRTStartup(void) __attribute__ ((alias("mainCRTStartup")));

 

Build your object files using the MS VC compiler cl.

Link your object files, cygwin1.lib, and my_crt0.lib (or whatever you called it) into the executable.

 

    cygnus当初首先把gcc,gdb,gas等开发工具进行了改进,使他们能够生成并解释win32的目标文件。然后,他们要把这些工具移植到windows平台上去。一种方案是基于win32 api对这些工具的源代码进行大幅修改,这样做显然需要大量工作。因此,他们采取了一种不同的方法——他们写了一个共享库(就是cygwin dll),把win32 api中没有的unix风格的调用(如fork,spawn,signals,select,sockets等)封装在里面,也就是说,他们基于win32 api写了一个unix系统库的模拟层。这样,只要把这些工具的源代码和这个共享库连接到一起,就可以使用unix主机上的交叉编译器来生成可以在windows平台上运行的工具集。以这些移植到windows平台上的开发工具为基础,cygnus又逐步把其他的工具(几乎不需要对源代码进行修改,只需要修改他们的配置脚本)软件移植到windows上来。这样,在windows平台上运行bash和开发工具、用户工具,感觉好像在unix上工作。(来源-百度百科)

    1.如果想要运行cygwin环境编译出来的可执行文件,必须要有cygwin1.dll库。虽然是动态链接但是在vc程序链接的过程中也需要.lib导入库,.lib文件主要存放一些函数和全局变量的符号信息,动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。但是cygwin本身并不提供cygwin1.lib导入库,这样在vc链接的时候就会出现N个符号未定义的错误,解决的办法就是以上前两步的用impdef工具把dll的符号表导出来,然后生成.lib文件。(当然网上也有其他一些方法把dll转换成lib)这样在程序链接的时候就可以找到相关函数的符号申明,就不会有链接错误了。

    2.按照常理编译链接通过,相关的dll库都齐全的话,程序就可以运行了,其实并非如此。上述资料最令人费解的地方就是为什么还要创建一个my_crt0.c文件,然后将其编译成DLL库。这其实跟vc的mscrt运行时有关。有相关常识的人都知道其实程序的执行入口并非是我们所熟知的main函数。因为在执行main之前必须做很多的前期工作。

    操作系统装载程序之后,首先运行的代码并不是main的第一行,而是某些别的代码,这些代码负责准备好main函数执行所需要的环境,并且负责调用main函数,这时候你才可以在main函数里放心大胆地写各种代码:申请内存、使用系统调用、触发异常、访问I/O。在main返回之后,它会记录main函数的返回值,调用atexit注册的函数,然后结束进程。

运行这些代码的函数称为入口函数或入口点(Entry Point),视平台的不同而有不同的名字。程序的入口点实际上是一个程序的初始化和结束部分,它往往是运行库的一部分。一个典型的程序运行步骤大致如下:

        操作系统在创建进程后,把控制权交到了程序的入口,这个入口往往是运行库中的某个入口函数。

         入口函数对运行库和程序运行环境进行初始化,包括堆、I/O、线程、全局变量构造,等等。

         入口函数在完成初始化之后,调用main函数,正式开始执行程序主体部分。

      main函数执行完毕以后,返回到入口函数,入口函数进行清理工作,包括全局变量析构、堆销毁、关闭I/O等,然后进行系统调用结束进程。

   上面已经介绍过,cygwin使用了基于win32 api写了一个unix系统库的模拟层即cygwin.dll库,并且每个c程序的执行都需要运行时的支持。只是在默认的情况下vc会链接到它自带的运行时库即msvccrt.dll上。所以我们想能不能不用vc的那套东西,而是直接使用cygwin提供的运行时库。所以在my_crt0.c中就把函数的入口点给改了,让它指定到cygwin的入口函数点cygwin_crt0,在这里会调用cygwin1.dll里的函数来完成系统初始化工作包括申请堆内存,初始化IO等。另外vc++默认的函数入口是WinMainCRTStartup而c程序的入口函数是mainCRTStartup所以需要在设置里把VC的入口点为mainCRTStartup。

 

PS:1.另外如果发现类似unresolved external symbol @__security_check_cookie@4的链接错误出现,需要在Visual Studio中,把缓冲区检查开关关掉即可,此开关可在"C/C++"选项页的"代码生成"选项中找到。

   2.最好去掉所有的优化选项,以免产生一些编译器只做聪明的进行一些优化而带来的意想不到的错误。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多