在MinGW和VS中使用cygwin1.dll

在將代碼從Linux平臺移植到Windows平臺的時候, 首先想到的是Cygwin這個項目, 其核心是利用cygwin1.dll來模擬linux接口, 可是這樣也帶來了一個問題, 就是隻能用Cygwin裏面自帶的GCC編譯環境來編譯程序, 不能在MinGW或VS中編譯。因此便想到了在MinGW以及VS中使用cygwin1.dll。html

對於這個問題, Cygwin官方的FAQ有相關的解釋說明:How do I use cygwin1.dll with Visual Studio or Mingw-w64。從中能夠看到, 對於在MinGW和VS中動態調用cygwin1.dll, 官方有具體的說明, 只要按照上面所寫的去作就能夠了。可是對於靜態調用cygwin1.dll, 官方並無嘗試過, 可是有人在mailinglist中分享了本身的操做步驟。本身分別在MinGW和VS中嘗試並改進了一下, 也成功了, 因而在這裏分享一下個人具體步驟。linux

    1 . 在MinGW中調用cygwin1.dllgit

  • 準備

在MinGW中調用cygwin1.dll與在Cygwin中直接編譯程序有點相似。首先, 去找一個Cygwin的源, 如http://mirrors.ustc.edu.cn/cygwin/x86/release/cygwin/, 下載最新版本的源代碼, 如cygwin-2.5.2-1-src.tar.xz, 而後將其解壓, 提取其中的winsup\cygwin\crt0.c文件, 其部分源代碼以下:github

/* crt0.c

This software is a copyrighted work licensed under the terms of the
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
details. */

/* In the following ifdef'd i386 code, the FPU precision is set to 80 bits
   and all FPU exceptions are masked.  The former is needed to make long
   doubles work correctly.  The latter causes the FPU to generate NaNs and
   Infinities instead of signals for certain operations.  */

#include "winlean.h"
#include <sys/cygwin.h>

注意到crt0.c中引用了winlean.h這個頭文件, 因此在提取時要將crt0.c與winlean.h一塊兒提取出來。這個是在編譯時要加入到工程中的源文件。bash

接着下載最新版本的編譯好的二進制文件, 如cygwin-2.5.2-1.tar.xz, 解壓, 找到並提取usr\bin\cygwin1.dll文件, 這個是最後程序運行所須要的動態庫文件。函數

最後, 到源(如http://mirrors.ustc.edu.cn/cygwin/x86/release/cygwin/cygwin-devel/)裏面去下載dev庫, 如cygwin-devel-2.5.2-1.tar.xz, 解壓, 找到並提取其中的usr\lib\libcygwin.a文件, 這個是連接時用到的導入庫。工具

以上步驟僅僅是準備工做, 接下來開始編譯及連接源程序。命令行

  • 編譯

把crt0.c和winlean.h文件添加進項目源代碼中, 而後正常編譯, 生成目標文件。code

  • 連接

連接libcygwin.a庫, 能夠用-lcygwin, 也能夠直接把libcygwin.a做爲參數傳給編譯器, 而後再額外添加以下選項:orm

-nostartfiles -e mainCRTStartup

若是不添加-nostartfiles選項, 就會報符號重定義的錯誤。選項-e mainCRTStartup指明程序入口點爲mainCRTStartup, 但實際使用時, MinGW的GCC總會報一個warning說"cannot find entry symbol mainCRTStartup; defaulting to 00401000"。可是最終的程序是能夠正常運行的。

    2 . 在VS中調用cygwin1.dll

  • 準備

首先, 新建一個源文件my_crt0.c, 內容以下:

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

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

void my_crt0 (MainFunc f)
{
    cygwin_crt0(f);
}

其中, my_crt0函數名能夠改成任意符合C語言的命名規範的函數名, 只是要記住後續的操做也應跟着同步修改。同時, 在代碼中main函數的定義應儘可能與my_crt0.c中的聲明保持一致, 防止出現一些比較奇怪的問題。

而後, 在Cygwin的命令行環境下用gcc編譯並連接這個文件:

gcc -shared my_crt0.c -o my_crt0.dll -Wl,--output-def,my_crt0.def

在VS的命令行環境下用上一步驟生成的def文件生成my_crt0.dll的導入庫, 用於後續的連接步驟:

lib /def:my_crt0.def /out:my_crt0.lib

接着, 去newlib-cygwin的鏡像上面去下載Cygwin 1.7.5版本的源代碼並解壓, 找到並提取winsup\cygwin\crt0.c文件, 這個文件是在編譯時要加入工程中的源文件。注意, 這裏選用的版本是1.7.5的。至於爲何選這個版本, 將代碼比對一下就能夠看出來。這是cygwin1.7.5版本的crt0.c的部分代碼:

void mainCRTStartup ()
{
    #ifdef __i386__
        //...
    #endif

    cygwin_crt0 (main);
}

這是cygwin1.7.5版本後的crt0.c的部分代碼:

void mainCRTStartup ()
{
    #ifdef __i386__
        //...
    #endif

    cygwin_crt0 (main);

    /* These are never actually called.  They are just here to force the inclusion
     of things like -lbinmode.  */

    cygwin_premain0 (0, NULL, NULL);
    cygwin_premain1 (0, NULL, NULL);
    cygwin_premain2 (0, NULL, NULL);
    cygwin_premain3 (0, NULL, NULL);
}

代碼中主要多了cygwin_premainx()函數, 雖然從註釋看出這些調用並無什麼實際用處, 可是爲了不出現一些奇怪的問題, 仍是用老版本爲好。

在提取crt0.c以後, 對其進行修改, 將其中的cygwin_crt0調用修改成my_crt0, 即crt0.c中調用的函數應與my_crt0.c中的函數保持一致。

而後, 去MYSY2的裏面下載工具mingw-w64-i686-tools-git-x.x.x.xxxx.xxxxxxx-x-any.pkg.tar.xz, 而後解壓, 找到裏面的gendef.exe, 用以下命令生成cygwin1.dll的def文件:

gendef cygwin1.dll

按照剛纔生成my_crt0.lib的方法去從cygwin1.def生成cygwin1.lib, 用於後續的連接步驟:

lib /def:cygwin1.def /out:cygwin1.lib

至此, 準備工做完成, 而後開始編譯和連接源程序。

  • 編譯

將crt0.c加入工程完成編譯生成目標文件便可。

  • 連接

連接時, 把my_crt0.lib和cygwin1.lib文件加入連接器, 同時給傳遞以下選項以設置程序入口點:

/entry:mainCRTStartup

還有, 運行時不要忘了加上cygwin1.dll動態庫。若是在編譯時報錯, 能夠先在MinGW中編譯成目標文件, 而後再在VS中完成連接。另外, 這個方法一樣適用於MinGW。

從上面的整個流程來看, 比較繁瑣的仍是在VS中調用cygwin1.dll。並且, 源代碼在VS中還不必定可以完成編譯, 須要藉助MinGW編譯成目標文件, 而後才能繼續在VS中使用。所以, 通常狀況下能夠優先考慮在MinGW中使用cygwin1.dll。

相關文章
相關標籤/搜索