tomyamaのブログ

日記・雑記。

シンプルなダイナミックリンクライブラリ(DLL)【1/3】

C言語でシンプルなダイナミックリンクライブラリを書いてみる。

 

単一のC言語ソースで、複数アーキテクチャ向けの共有ライブラリとしてビルドすることを考える。

 

想定しているのは以下のプラットフォーム(開発環境)です。

 


【1/3】DLLを作成してみる

 

目次


 

ソースコード

[simple_dll.h]

/****************************************************************//**
 * \file  simple_dll.h
 *
 * \brief  DLL(ダイナミックリンクライブラリ)のシンプルなサンプル
 *
 * ファイル
 *  - 文字コードUTF-8
 *  - 改行コードはLF
 *
 * \author   tomyama  Sep 2022
 ********************************************************************/

#ifndef _SIMPLE_DLL_H_
#define _SIMPLE_DLL_H_


/********************************************************************/
/*        include files                                             */
/********************************************************************/

 

/********************************************************************/
/*        defines                                                   */
/********************************************************************/


/********************************************************************/
/*        types                                                     */
/********************************************************************/

typedef char *( *PFN_GETGREETINGMSG )( void );


/********************************************************************/
/*        function prototype                                        */
/********************************************************************/

extern char *getGreetingMsg( void );


/********************************************************************/
/*        static value                                              */
/********************************************************************/


/********************************************************************/
/*        globals variable                                          */
/********************************************************************/

#endif /* _SIMPLE_DLL_H_ */

 

[simple_dll.c]

/****************************************************************//**
 * \file  simple_dll.c
 *
 * \brief  DLL(ダイナミックリンクライブラリ)のシンプルなサンプル
 *
 * ファイル
 *  - 文字コードUTF-8
 *  - 改行コードはLF
 *
 * \author   tomyama  Sep 2022
 ********************************************************************/


/********************************************************************/
/*        include files                                             */
/********************************************************************/

#include "simple_dll.h"


/********************************************************************/
/*        defines                                                   */
/********************************************************************/

#define GREETING_MESSAGE            "Hello, world!"


/********************************************************************/
/*        types                                                     */
/********************************************************************/


/********************************************************************/
/*        function prototype                                        */
/********************************************************************/


/********************************************************************/
/*        static value                                              */
/********************************************************************/


/********************************************************************/
/*        globals variable                                          */
/********************************************************************/


/********************************************************************/
/*        function (global)                                         */
/********************************************************************/

/********************************************//**
 * \brief  "hello, world!"を返す関数
 *
 * \return     "hello, world!"を指すnull終端文字列へのポインタを返す
 * \retval     NULL以外  正常終了(常に正常終了を返す)
 * \author     tomyama
 * \sa         -
 * \callgraph
 * \callergraph
 ************************************************/

char *getGreetingMsg( void )
{
    return GREETING_MESSAGE;
}


/********************************************************************/
/*        function (static)                                         */
/********************************************************************/

 

[simple_dll_msvc.def]

LIBRARY    "simple_dll_msvc"
EXPORTS
   getGreetingMsg        @1

 

Windows環境向けにビルドする手順

  1. スタートメニューからVisual Studioの『x86 Native Tools Command Prompt for VS 20xx』を起動する。
  2. 以下のバッチファイルを作成して実行する。
    • [build.bat]

      set CFLAGS=/nologo /source-charset:utf-8 /execution-charset:shift_jis /Wall /wd4464 /wd4668 /wd4710 /wd4820 /wd4996 /wd5045 /O2 /Ob0
      cl.exe %CFLAGS% /Fo:simple_dll_msvc.o /c simple_dll.c
      link.exe /OUT:simple_dll_msvc.dll simple_dll_msvc.o /DEF:simple_dll_msvc.def

       

  3. dll,exp,libという3つのファイルが生成されているはずです。
    • simple_dll_msvc.dll ←これがDLLファイル

    • simple_dll_msvc.exp
    • simple_dll_msvc.lib ←これらのファイルは動的リンクでアプリをビルドする時にリンカが使う
  4.  getGreetingMsg()関数がエクスポートされている事を確認する。
    • dumpbinコマンド

      >dumpbin /exports simple_dll_msvc.dll
      Microsoft (R) COFF/PE Dumper Version 14.32.31332.0
      Copyright (C) Microsoft Corporation.  All rights reserved.


      Dump of file simple_dll_msvc.dll

      File Type: DLL

        Section contains the following exports for simple_dll_msvc.dll

          00000000 characteristics
          FFFFFFFF time date stamp
              0.00 version
                 1 ordinal base
                 1 number of functions
                 1 number of names

          ordinal hint RVA      name

                1    0 00001000 getGreetingMsg

        Summary

              2000 .data
              6000 .rdata
              1000 .reloc
              C000 .text

      >

       

Windows以外の環境向けにビルドする手順

  1. 適当な端末エミュレータを起動する。
  2. 以下のスクリプトを作成して実行する。
    • [build.sh] 

      #!/bin/sh

      CFLAGS="-fPIC -Wall -O2"

      arch=`uname -mos | tr ' ./' '_'`
      echo "$arch"

      dll_prefix="lib"
      dll_ext="so"
      if [ "`uname -o`" = "Cygwin" ]; then
        CFLAGS="-Wall -O2"
        dll_prefix=""
        dll_ext="dll"
      fi

      gcc $CFLAGS -o simple_dll_${arch}.o -c simple_dll.c
      gcc -o ${dll_prefix}simple_dll_${arch}.${dll_ext} simple_dll_${arch}.o -lc -shared -Wl,-soname,${dll_prefix}simple_dll_${arch}.${dll_ext}

  3. DLLファイルが生成されているはずです。
  4. getGreetingMsg()関数が外部公開されている事を確認する。
    • Linuxの場合
      • $ nm libsimple_dll_Linux_x86_64_GNU_Linux.so | grep getGreetingMsg
        0000000000001100 T getGreetingMsg
        $

    • Cygwinの場合
      • $ nm simple_dll_CYGWIN_NT-10_0-19044_x86_64_Cygwin.dll | grep getGreetingMsg
        000000055bb11030 T getGreetingMsg
        $

      • CygwinのDLLはWindowsと同じフォーマット(PE)なので、WindowsVisual Studio )環境と同様にdumpbinコマンドを使った確認も可能です。

        >dumpbin /exports simple_dll_CYGWIN_NT-10_0-19044_x86_64_Cygwin.dll
        Microsoft (R) COFF/PE Dumper Version 14.32.31332.0
        Copyright (C) Microsoft Corporation.  All rights reserved.


        Dump of file simple_dll_CYGWIN_NT-10_0-19044_x86_64_Cygwin.dll

        File Type: DLL

          Section contains the following exports for simple_dll_CYGWIN_NT-10_0-19044_x86_64_Cygwin.dll

            00000000 characteristics
            633495DC time date stamp Thu Sep 29 03:43:40 2022
                0.00 version
                   1 ordinal base
                   3 number of functions
                   3 number of names

            ordinal hint RVA      name

                  1    0 00001020 __gcc_deregister_frame
                  2    1 00001000 __gcc_register_frame
                  3    2 00001030 getGreetingMsg

          Summary

                1000 .bss
                1000 .buildid
                1000 .data
                1000 .debug_abbrev
                1000 .debug_aranges
                1000 .debug_frame
                6000 .debug_info
                1000 .debug_line
                2000 .debug_line_str
                1000 .debug_loclists
                1000 .debug_str
                1000 .edata
                1000 .idata
                1000 .pdata
                1000 .rdata
                1000 .reloc
                1000 .text
                1000 .xdata

        >

 

解説

入力ファイル
プラットフォーム(開発環境) ファイル
WindowsのDLLファイル(Visual Studio

simple_dll.h

simple_dll.c

simple_dll_msvc.def
LinuxのSOファイル(gcc -
CygwinのDLLファイル(gcc
Android(Termux)のSOファイル(clang)

 

ビルド用の台本(スクリプト
プラットフォーム(開発環境) ファイル
WindowsのDLLファイル(Visual Studio build.bat
LinuxのSOファイル(gcc build.sh
CygwinのDLLファイル(gcc
Android(Termux)のSOファイル(clang)
  • Cygwinの無い環境でもビルドできるように「Visual Studio」の場合は .bat ファイルにしている。その他の環境ではシェルスクリプト
  • clangの場合は、gccでは無くclangを呼び出す方がより厳密的だが、clangはgccに擬態してくれるように作られているので(少なくとも現時点では)、それに頼ってscriptingしている。

 

中間生成物: 『simple_dll.c』のコンパイル
プラットフォーム(開発環境) コマンド
WindowsのDLLファイル(Visual Studio cl.exe  %CFLAGS%   /Fo:simple_dll_msvc.o    /c simple_dll.c
LinuxのSOファイル(gcc gcc -fPIC -Wall -O2 -o simple_dll_${arch}.o -c simple_dll.c
CygwinのDLLファイル(gcc gcc       -Wall -O2 -o simple_dll_${arch}.o -c simple_dll.c
Android(Termux)のSOファイル(clang)

gcc -fPIC -Wall -O2 -o simple_dll_${arch}.o -c simple_dll.c

  • 呼び出すコマンドやオプションスイッチに差異はあるものの、必要な要素(コマンドライン引数)は全プラットフォームでほぼ同じ
  • Cygwinは、ライブラリ用のソースをコンパイルするときに -fPICオプションを付けなくてもよい。(付けてもよい。どちらでも変わらない。)
  • CygwinWindowsと同じPEフォーマット)では、常に位置独立コード(Position-Independent Code [ PIC ])が生成される。

 

最終生成物(DLLなど): リンカでDLLを生成
プラットフォーム(開発環境) コマンド
WindowsのDLLファイル(Visual Studio link.exe /OUT:simple_dll_msvc.dll    simple_dll_msvc.o  /DEF:simple_dll_msvc.def
LinuxのSOファイル(gcc gcc     -o libsimple_dll_${arch}.so  simple_dll_${arch}.o -lc -shared -Wl,-soname,libsimple_dll_${arch}.so
CygwinのDLLファイル(gcc gcc     -o    simple_dll_${arch}.dll simple_dll_${arch}.o -lc -shared -Wl,-soname,simple_dll_${arch}.dll
Android(Termux)のSOファイル(clang) gcc     -o libsimple_dll_${arch}.so  simple_dll_${arch}.o -lc -shared -Wl,-soname,libsimple_dll_${arch}.so
  • Windows(Visual C)では、エクスポートするシンボルを指定する為に、defファイルが使える。コード中に識別子(__declspec(dllexport))を埋め込む方法もあるが、defファイルを使う方がソースコードにコードを追加する必要が無いので使い易いと思います。
  • それに対して、Windows以外のプラットフォーム(gcc, clang)では、「エクスポート」という概念が無く(C言語で外部公開しているシンボルには全てアクセス可能なはず)、defファイルのようなものを指定する必要は無い。
  • Cygwinでは、共有ライブラリの拡張子は .dll となる。(通常のUNIX(ELFフォーマット)の拡張子は .so
  • Cygwinは、先頭の lib も不要。