Help us understand the problem. What is going on with this article?

Go の標準パッケージにないシステムコールを使う

More than 1 year has passed since last update.


Qiita 初投稿です。

Go の には、OS のシステムコールの Wrapper 関数が定義されていますが、全てが網羅されているわけではありません。

例えば、windows package を見てみると、WSARecv() などはありますが、WSASocket() がない。



  • Windows 10
  • go version go1.13.3 windows/amd64

Syscall の構造

まず、WSARecv() がどのように定義されているか見ていきます。
WSARecv() の定義は にあります。

func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) {
    r1, _, e1 := syscall.Syscall9(procWSARecv.Addr(), 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0)
    if r1 == socket_error {
        if e1 != 0 {
            err = errnoErr(e1)
        } else {
            err = syscall.EINVAL

はい、謎の関数 syscall.Syscall9() が現れました。

// Code generated by 'go generate'; DO NOT EDIT.


同ディレクトリの syscall_windows.go に生成する関数の定義があります。

//sys   WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error) [failretval==socket_error] = ws2_32.WSARecv



system call のコード生成であることを表す。このコメントを、後述の mkwinsyscall が拾う。

WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (err error)

定義する関数シグネチャ。WSABuf とか Overlapped とかの型は、windows package で定義されている。


system call が戻り値 socket_error を返したときにエラーとする。socket_error は windows package の package private な定数。

const socket_error = uintptr(^uint32(0))


If no error occurs and the receive operation has completed immediately, WSARecv returns zero. In this case, the completion routine will have already been scheduled to be called once the calling thread is in the alertable state. Otherwise, a value of SOCKET_ERROR is returned

= ws2_32.WSARecv

上述の関数を、ws2_32.dll 内の WSARecv 関数にマッピングする。


生成コマンド定義は mksyscall.go にあります。

//go:generate go run -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go

//go:generate go run までは、Go generate の書式。 eventlog.go service.go syscall_windows.go security_windows.go から //sys の定義を拾い上げて、zsyscall_windows.go を生成しているようです。

自前で WSASocket() を定義してみる

仕組みはわかったので、自前で WSASocket() を定義してみます。

import ""

const (
    wsaprotocol_len    = 255
    max_protocol_chain = 7
    invalid_socket     = ^windows.Handle(0)

type GROUP uint32

type GUID struct {
    Data1 uint32
    Data2 uint16
    Data3 uint16
    Data4 [8]byte

    ChainLen     int32
    ChainEntries [max_protocol_chain]uint32

type WSAPROTOCOL_INFO struct {
    ServiceFlags1     uint32
    ServiceFlags2     uint32
    ServiceFlags3     uint32
    ServiceFlags4     uint32
    ProviderFlags     uint32
    ProviderID        GUID
    CatalogEntryID    uint32
    ProtocolChain     WSAPROTOCOLCHAIN
    Version           int32
    AddressFamily     int32
    MaxSockAddr       int32
    MinSockAddr       int32
    SocketType        int32
    Protocol          int32
    ProtocolMaxOffset int32
    NetworkByteOrder  int32
    SecurityScheme    int32
    MessageSize       uint32
    ProviderReserved  uint32
    Protocols         [wsaprotocol_len + 1]uint16

//go:generate go run -output zwinsys.go winsys.go
//sys WSASocket(af int32, tp int32, protocol int32, protocolInfo *WSAPROTOCOL_INFO, g GROUP, flags uint32) (socket windows.Handle, err error) [failretval==invalid_socket] = ws2_32.WSASocketW

なにやら構造体やら色々でてきましたが、これらは C の構造体やマクロ定義を移植したものです。(この辺の情報を一々調べて定義するのが面倒。。。)

ここで出てきた WORD, DWORD, int の対応は次の通り。

C Go
WORD uint16
DWORD uint32
int int32


//go:generate を記述した package のディレクトリで次のコマンドを実行すると、zmysys.go が生成されます。

$ go generate



sock, err := WSASocket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP, nil, 0, 0)
fmt.Println(sock, err)
// => 344 <nil>



const badArg int32 = 9999
sock, err := WSASocket(badArg, badArg, badArg, nil, 0, 0)
fmt.Println(sock, err)
// => 18446744073709551615 The support for the specified socket type does not exist in this address family.

不正な値を与えたので、WSAESOCKTNOSUPPORT のエラーが返ってきました。
このメッセージは FormatMessageWで取得されたものです。

エラーコードは syscall.Syscall<N>() の3番目の戻り値として返ってきます。その実装は runtime のアセンブラで定義されていてコードを追いきれませんでしたが、GetLastError() の結果を返してきている模様。(たぶんこの辺)
本来、WSAXXX() のエラーコードは WSAGetLastError() で取得するものですが、WSAGetLastError()GetLastError() の Wrapper であるがため、問題なく動いているようです。


標準package で定義されていない system call も、 を使えば、比較的簡単に呼び出し可能。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away