2021/03,江端さんの技術メモ

■外部から強制的に書き込もうとすると失敗します

ubuntu@ip-172-26-7-19:~/codes/xxxxxxx_ride_hailing_go$ docker ps

095fe60f02cc nginx:1.15-alpine "nginx -g 'daemon of…" 8 weeks ago Up 22 minutes 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp xxxxxxx_ride_hailing_go_nginx_1

でもって、

ubuntu@ip-172-26-7-19:~/codes/xxxxx_ride_hailing_go$ sudo docker cp fullchain.pem 095fe60f02cc://etc/nginx/conf.d/

Error response from daemon: Error processing tar file(exit status 1): unlinkat /etc/nginx/conf.d/fullchain.pem: device or resource busy (失敗します)

しかし、dockerの中に入って、

ubuntu@ip-172-26-7-19:~/codes/xxxxxx_ride_hailing_go$ docker exec -i -t xxxxx_ride_hailing_go_nginx_1 sh

で内側から、killすると、dockerごとダウンして、なんとも悩ましい、にわとりと卵の関係になってしまいます。

■解決方法

ubuntu@ip-172-26-7-19:~/codes/xxxxxxx_ride_hailing_go$ sudo docker cp fullchain.pem 095fe60f02cc://etc/nginx/conf.d/fullchain.pem.new

ubuntu@ip-172-26-7-19:~/codes/xxxxxxx_ride_hailing_go$ sudo docker cp privkey.pem 095fe60f02cc://etc/nginx/conf.d/privkey.pem.new

でもって、 (今後は、new2, new3, new4 とか、どんどん増やしていく)

ubuntu@ip-172-26-7-19:~/codes/xxxxxxx_ride_hailing_go$ vi nginx.conf

をして、

———————————————————

ssl_certificate /etc/nginx/conf.d/fullchain.pem.new;

ssl_certificate_key /etc/nginx/conf.d/privkey.pem.new;

———————————————————

と書き換える。

さらに、

docker-compose.yml の最後の行に、

volumes:

- "./nginx.conf:/etc/nginx/conf.d/default.conf"

- "./fullchain.pem:/etc/nginx/conf.d/fullchain.pem.new"

- "./privkey.pem:/etc/nginx/conf.d/privkey.pem.new"

も変える。

でもって、 docker-compose stop; docker-compose start をすれば、新しい鍵で動き出します。不細工ではありますが、プログラムは動くようになります。

■ところで今思いついたんだけど、いい方法がありました。

現在は、fullchain.pem.new と、privkey.pem.new で動いているのだから、fullchain.pem と、privkey.pemは、rm できるはず。

で、cp fullchain.pem.new fullchain.pem, cp privkey.pem.new privkey.pem として、さらに、nginx.confの設定を元に戻せば、fullchain.pem と、privkey.pem の名前を使えるはずです

(まあ、動いているので、今は、このままにしています)

■もっと、いい方法がありました。

docker-compose.yml の最後の行に、

volumes:

- "./nginx.conf:/etc/nginx/conf.d/default.conf"

- "./fullchain.pem:/etc/nginx/conf.d/fullchain.pem"

- "./privkey.pem:/etc/nginx/conf.d/privkey.pem"

の3行を加えれば、Dockerの外側に、最新の"fullchain.pem"と、"privkey.pem"を置いておけば、自動的にDockerコンテナの中に取り込まれます(今、そうなっている)

2021/03,江端さんの技術メモ

http://mstc.or.jp/faop/committee/11b4d681d4059ef7fed08409b8b34e1f2c1c4029.PDF

nxdlink_lua

--[[ Wireshark用 NX/Dlinkプロトコル解析プログラム 
 	       			      	ver0.03	  2012年2月16日
 	       			      	ver0.02	  2012年2月10日
 	       			      	ver0.01	  2012年2月1日

						  江端智一
使用条件 
 	    絶対的な意味において無保証
 
History
   Ver 0.03	重複登録を一応回避

   Ver 0.02	TCP/UDPデータに"NUXM"があったら、送信ポート番号を調べて
       		動的にDissectorをWiresharkに登録するようにした

   Ver 0.01	とりあえず動作確認のみ
		udpポート番号が特定の値に固定されている
		フィールドの位置がズレている可能性あり

使い方
 (Step.1)	c:/Program Files/Wireshark/init.lua の一部を書き換え
   		
	(a)	disable_lua = true; do return end; 
 			     ↓ 
		-- disable_lua = true; do return end; 

	(b)	run_user_scripts_when_superuser = false
  			     ↓ 
		run_user_scripts_when_superuser = true

	(c)	最終行に 以下の一行を追加
		"init.lua"と同じディレクトリに置く
		dofile("nxdlink.lua")

 (Step.2)	このファイルを c:/Program Files/Wireshark/ に
 		"nxdlink.lua"という名前で保存
]]

do
    nxdlink_proto = Proto("NXDlink", "nxdlink protocol dissector")


    nxdlink_proto.dissector = function(buffer, pinfo, tree)

       -- nexus header type : "NUXM"
       local    hd_h_type_range = buffer(0,4)
       local    hd_h_type = hd_h_type_range:string()

       -- message length ( 16K + 64 )
       local 	hd_ml_range = buffer(4,4)
       local    hd_ml = hd_ml_range:uint()

       -- source protocol address
       local	hd_sa_range = buffer(8,4)
       local	hd_sa = hd_sa_range:uint()

       -- destination address	
       local 	hd_da_range = buffer(12,4)
       local 	hd_da = hd_da_range:uint()

       -- boot time stamp
       local 	hd_v_seq_range = buffer(16,4)
       local 	hd_v_seq = hd_v_seq_range:uint()

       -- message number
       local    hd_seq_range = buffer(20,4)
       local    hd_seq = hd_seq_range:uint()
		
       --[[ /* message control type		*/
         			/* UDP_MSG : multicast send	*/
				/* UDP_INQ : multicast inquire	*/
				/* UDP_NIQ : multicast Ninquire	*/
				/* TCP_MSG : peer send		*/
				/* TCP_INQ : peer inquire	*/
				/* TCP_RPL : peer reply		*/]]

       local	hd_m_ctl_range = buffer(24,4)
       local	hd_m_ctl = hd_m_ctl_range:uint()

       local    control_type  = { 
            [0x80000000] = "UDP_MSG(0x80000000) : multicast send", 
            [0xa0000000] = "UDP_INQ(0xa0000000) : multicast inquire",
	    [0x88000000] = "UDP_NIQ(0x88000000) : multicast Ninquire",
	    [0x40000000] = "TCP_MSG(0x40000000) : peer send",
	    [0x60000000] = "TCP_INQ(0x60000000) : peer inquire",
	    [0x50000000] = "TCP_RPL(0x50000000) : peer reply",
       }

       -- /* inquire ID parameter		*/
       
	-- /* inquire source address	 */
       local	inq_id_range = buffer(28,12)

	-- /* transaction code		*/
       local 	hd_tcd_range = buffer(40,2)
       local 	hd_tcd = hd_tcd_range:uint()

       -- /* program version number	*/
       local    hd_ver_range = buffer(42,2)
       local    hd_ver = hd_ver_range:uint()

       -- /* future use			*/
       local 	hd_fu0_range = buffer(44,3)
       local 	hd_fu0 = hd_fu0_range:uint()

       -- /* acknowledge request mode	*/
		--		/* PT_REQ : request message	*/
		--		/* PT_ACK : response message	*/
       local    hd_pkind_range = buffer(47,1)
       local    hd_pkind = hd_pkind_range:uint()

       -- /* packet seqence number	*/
       local    hd_pseq_range = buffer(48,4)
       local    hd_pseq = hd_pseq_range:uint()

       -- /* message mode			*/
		--		/* HEAD_ONLINE : online mode	*/
		--		/* HEAD_TEST   : test   mode	*/
       local 	hd_mode_range = buffer(52,2)
       local 	hd_mode = hd_mode_range:uint()

       -- /* protocol version number	*/
	-- /* NEXUS_DLINK : NeXUS/Dlink	*/
	-- /* NEXUS_T     : NeXUS/T	*/
       local    hd_pver_range = buffer(54,1)
       local    hd_pver = hd_pver_range:uint()

       -- /* message service level	*/
       local    hd_pri_range = buffer(55,1)
       local    hd_pri = hd_pri_range:uint()

       -- /* current block number		*/
       local    hd_cbn_range = buffer(56,1)
       local    hd_cbn = hd_cbn_range:uint()

       -- /* total block number		*/
       local    hd_tbn_range = buffer(57,1)
       local    hd_tbn = hd_tbn_range:uint()

       -- /* segmenting block size	*/
       local    hd_bsize_range = buffer(58,2)
       local    hd_bsize = hd_bsize_range:uint()

       -- /* future use			*/
       local 	hd_fu1_range = buffer(60,4)
       local    hd_fu1 = hd_fu1_range:uint()

       -- data
       local	data_range = buffer(64)
       local	data = data_range:string()
        
       local subtree = tree:add("NX Dlink Protocol")


       -- nexus header type : "NUXM"
       subtree:add(hd_h_type_range, "Type:",hd_h_type)

       -- message length ( 16K + 64 )
       subtree:add(hd_ml_range, "Length:",hd_ml)

       -- source protocol address
       dispatch_addr("source protocol address:",hd_sa_range, pinfo, subtree)

       -- /* destination address		*/
       dispatch_addr("destination address:",hd_da_range, pinfo, subtree)

       -- /* boot time stamp		*/
       subtree:add(hd_v_seq_range, "boot time stamp:",hd_v_seq)

       -- /* message number		*/
       subtree:add(hd_seq_range,"message number:",hd_seq)

       -- /* message control type		*/

       dispatch_cnttype(string.format("message control type: %s",control_type[hd_m_ctl]), hd_m_ctl_range, pinfo, subtree)

	-- /* inquire ID parameter */
       dispatch_inq("inquire ID parameter:",inq_id_range, pinfo, subtree)

	-- /* transaction code		*/
       subtree:add(hd_tcd_range, "transaction code:",hd_tcd)

       -- /* program version number	*/
       subtree:add(hd_ver_range, "program version number:",hd_ver)

       -- /* future use			*/
       subtree:add(hd_fu0_range, "future use:",hd_fu0)

       -- /* acknowledge request mode	*/
       subtree:add(hd_pkind_range, "acknowledge request mode:",hd_pkind)

       -- /* packet seqence number	*/
       subtree:add(hd_pseq_range, "packet seqence number:",hd_pseq)

       -- /* message mode			*/
       subtree:add(hd_mode_range, "message mode(1:online 0:test) :",hd_mode)

       -- /* protocol version number	*/
       subtree:add(hd_pver_range, "NX protocol version number:",hd_pver)

       -- /* message service level	*/
       subtree:add(hd_pri_range, "message service level:",hd_pri)

       -- /* current block number		*/
       subtree:add(hd_cbn_range, "current block number:",hd_cbn)

       -- /* total block number		*/
       subtree:add(hd_tbn_range, "total block number:",hd_tbn)

       -- /* segmenting block size	*/
       subtree:add(hd_bsize_range, "segmenting block size:",hd_bsize)

       -- /* future use			*/
       subtree:add(hd_fu1_range, "future use:",hd_fu1)

       -- data
       subtree:add(data_range, "data:",data)
--       dispatch_inq("Data:",data_range, pinfo, subtree)	 

        pinfo.cols.protocol = "NX/Dlink"
        pinfo.cols.info = control_type[hd_m_ctl]
    end

   -- リスナーを定義
   function init_listener()

       u = {} 
       t = {} 

       u_cnt = 1;
       t_cnt = 1;

       u_bool = true
       t_bool = true

       -- UDP/TCPデータの中に"NUXM"があったらフックする	    
       tap = Listener.new("frame", "udp contains NUXM or tcp contains NUXM")   


        function tap.reset()
            print("passed tap.reset")
            u_cnt = 0;
            t_cnt = 0;
        end

       -- Dissector を Wireshark に追加登録
       -- 重複登録問題を(不細工だけけど)以下で対応
       function tap.packet(pinfo,tvb,ip)
       	   -- UDPの場合	
           if ( pinfo.ipproto == 17 ) then

 	       u_flag = 1
 
	       for i=0, u_cnt do 
	           if u[i] == pinfo.dst_port then 
	               u_flag = 0 
  	           end
	       end
	   
	       if u_flag == 1 then 
  	           u_cnt = u_cnt + 1
	           u[u_cnt] = pinfo.dst_port
	           udp_table = DissectorTable.get("udp.port")
	           udp_table:add(pinfo.dst_port, nxdlink_proto)
               end

	   -- TCPの場合  (まだ実験前)
	   elseif ( pinfo.ipproto == 6 ) then

 	       t_flag = 1

	       for i=0, t_cnt do 
	           if u[i] == pinfo.dst_port then 
	               c_flag = 0 
  	           end
	       end

	       if t_flag == 1 then 
  	           t_cnt = t_cnt + 1
	           t[t_cnt] = pinfo.dst_port
  	           tcp_table = DissectorTable.get("tcp.port")
	           tcp_table:add(pinfo.dst_port, nxdlink_proto)
               end
           end
       end
   end

   init_listener()

end

function dispatch_cnttype(string, buffer, pinfo, subtree)
   local subsubtree = subtree:add(buffer(0), string, buffer(0):tvb())	

    subsubtree:add(buffer(0,1),string.format("%d... .... .... .... .... .... .... .... = multicast flag",buffer(0,1):bitfield(0)))
    subsubtree:add(buffer(0,1),string.format(".%d.. .... .... .... .... .... .... .... = unicast flag",buffer(0,1):bitfield(1)))
    subsubtree:add(buffer(0,1),string.format("..%d. .... .... .... .... .... .... .... = inquire flag",buffer(0,1):bitfield(2)))
    subsubtree:add(buffer(0,1),string.format("...%d .... .... .... .... .... .... .... = reply flag",buffer(0,1):bitfield(3)))
    subsubtree:add(buffer(3,1),string.format(".... .... .... .... .... .... .... .%d.. = ack flag(future use)",buffer(3,1):bitfield(5)))
    subsubtree:add(buffer(3,1),string.format(".... .... .... .... .... .... .... ...%d = ack flag(future use)",buffer(3,1):bitfield(7)))

end


function dispatch_udp_port(string, buffer, pinfo, subtree)
    local subsubtree = subtree:add(buffer(0), string, buffer(0):tvb())	

    subsubtree:add(buffer(0,2),"source port:", buffer(0,2):uint())
    subsubtree:add(buffer(2,2),"destination port:", buffer(2,2):uint())
end

function dispatch_addr(string, buffer, pinfo, subtree)
    local subsubtree = subtree:add(buffer(0), string, buffer(0):tvb())	

    subsubtree:add(buffer(0,1),"Domain Number:", buffer(0,1):uint())
    subsubtree:add(buffer(1,1),"Data Field Number:", buffer(1,1):uint())
    subsubtree:add(buffer(2,2),"Node Number/Multicast Group Number:", buffer(2,2):uint())
end

function dispatch_inq(string, buffer, pinfo, subtree)
    local subsubtree = subtree:add(buffer(0), string, buffer(0):tvb())	
    
    subsubtree:add(buffer(0,2), "inquire source address:",buffer(0,2):uint())
    subsubtree:add(buffer(2,2), "inquire control block address:",buffer(2,2):uint())
    subsubtree:add(buffer(4,2), "inquire ID sequence number:",buffer(4,2):uint())
end

2021/03,江端さんの技術メモ

これが出ると、流石にパニックになる。

なんどリスタートしても状況が改善されないことがある。

コンピュータの再起動もやったし、

を繰り返したりした。

しかし状況が改善されない。

そして、世界には、「これ」に関する情報がほとんどない。

現状、Dockerなしでの仕事は考えられないので、かなり青ざめた。

ところが、これが意味不明に「突然直る」ことがある

ということで、私から私への提案であるが、

何か別の仕事をしながら、時々、"Restart Docker" を試してみる

を、提案する(エンジニアとしては、かなり腹立たしい対応であることは分かっているが)

焦って再インストールしたり、

https://github.com/docker/for-win/issues/7677 に記載されているような

I have made the following steps and was able to start docker successfully:
1)run "cmd" with administrator rights
2) type Regedit and enter
3)In registry editor find this folder Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Policies\Microsoft\FVE
4)inside this folder you can find "FDVDenyWriteAccess" this rule click on it choose modify and replace value data 1 with 0 , then restart docker and wait for it.

のような方法は、少なくとも1日待って、駄目だったら、試すくらいの気持ちでいよう。

合言葉は、

Docker Desktop for Windows は馬鹿

で、行こう。

========

続編

Docker Desktop for Windowsのメモリ管理やら、面倒なことを弄って、そして、論文やら報告書やらで、1月近く放っておいたら、全く動かなくなった。こいつは、構ってやらないと動かなくなるらしいです。

このアイコンの帆の部分が出なくなって、「docker desktop is runnning」の状態のまま続いて、もうウンともスンとも言わないらしいです。

ここのところ、Windows10を軽量化する為に、色々カルトな設定をしていたので、その中の一つが、Dockerのご機嫌を損ねた可能性があります

Hyper-V 周りがあやしい、と思って、管理者モードで立ち上げた、PowerShellから、

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All

なんぞを打ち来んで、待つこと10分。

無事終って再起動したのだけど、全く改善がありません。

ずっとこんな感じ。

『再インストールするしかないのかな』と暗い気持ちになっているところに、この記事を見付けました。

macでdocker desktopが起動しないときのシンプルな対処方法

要するに、

対処方法「Reset to factory defaults」の実行

というのをやればいいらしいようです。色々失うものがありそうですが、(間違いなく、Dockerのイメージは消えるだろうが)、この際『かまわん』と腹をくくって、Windows10でのやり方を試みました。

からSetttingで、てんとうむしみたいなアイコンをクリックします。

すると、「Reset to factory defaults」というのがあるので、これを押下します。

まあ、結果的に、これでdocker for Windowsは動き出すようです(前述のHyper-Vとかも関係あるかもしれません)。

ただ、

ディスクスペースを開けるのに"docker system prune"は便利だが、濫用しないこと。docker-compose buildが再び上手く動くという保障はないぞ

みたいに、何もかも「真っ白」になるので、その覚悟はして下さい。

まあ、「Dockerを再インストールするよりはいいよね」というくらいに追い込まれた時の最後の手段として使って下さい。

========

パソコンを立ち上げ直すたびに、"Reset to factory defaults"をしないと動かないので、Dockerを再インストールしましたが、状況が改善されません。

ほとほと困っていますが、Docker  Imageを毎回作り直している訳にもいきません。

で、この問題を解決する手段として、経験的に分かったことを書き下します。

(1)Docker Desktopはまともに起動するのに、PC起動後10分程度かかる(ような)気がします。

(2)もし「エントツの出てこないクジラ」のアイコンが出てきて手が打てないような状況になっていれば、[タスクマネージャ]→[詳細]→Docker Desktop.exe、その他 Dockerと名前のついているのを全部殺す

(3)メニューから、手動で、"Docker Desktop"を起動する。

これで動き出すことがあるようです。もう、「良い悪い」といっている場合ではないので、私は、Docker desktopの自動起動のオプションを外して、手動で立ち上げることにしました。なお、手動で立ち上げても、上記の対応が必要となることがあります。

まあ、何かの拍子に直ることを期待しましょう。

 

 

 

 

2021/03,江端さんの技術メモ

自宅の塗装工事後に、以下のような現象が現われてきました。チョークで書かれたような白いものが壁に浮き出てきます。

白華現象(エフロ)と言われるものらしく、壁の内部から出てくる水分によって引き起こされる現象とのことです。

そこで、本日、この対応を実地で教えて貰いましたので、記録しておきます。

(Step.1)対象箇所を、水(水道水)で塗らす。

(Step.2) 市販の溶液を準備する

(Step.3)溶液を塗布する

(Step.4)3分ほど待ってから、水で洗い流す

以上

2021/03,江端さんの技術メモ

https://www.meti.go.jp/meti_lib/report/H30FY/000677.pdf

 

マイクロトランジッド

 

Chariot (サンフランシスコ周辺など)
• フォードが買収した通勤者向けの乗合バスサービス
• 利用者数に基づく柔軟なルート設定に特徴
• 14人乗りのシャトルバスを利用し、1日当たり100を超える路線
でサービス提供
出所: Tech Crunch, Chariot
Via (ニューヨーク、ワシントン、シカゴ 等)
• 乗客と車両の座席とをリアルタイムに関連づけ、同一ルートで
移動できる乗客をグループ化して配車する乗合バスサービス
• 車両の最適なルートに合わせて、利用者の乗降車スポットを
自動的に指定する仕組み
• テキサス州アーリントン市と提携。市の補助で運賃3ドルで運行

2021/03,江端さんの技術メモ

GO 1.5 と C++ を SWIG でブリッジさせる方法

を試してみたのですが、私は上手く動かすことができませんでした。

で、こんな風にしてみたら動いたので、記録を残しておきます。

ebata@DESKTOP-P6KREM0 MINGW64 ~/kese/swig
$ tree
.
├── main1.go
└── sc1
    ├── nop.go
    ├── sc1.h
    └── sc1.swigcxx

1 directory, 4 files
// main1.go

package main

import (
	"fmt"

	"./sc1"
)

func Dump(p interface{}) {
	fmt.Printf("%v:%T\n", p, p)
}

func main() {
	Dump(sc1.EchoIntN(8))
	Dump(sc1.EchoDoubleN(9))
}

でもって、nop.go

package sc1

sc1.h

#pragma once 

inline  int
EchoIntN( int p ) {
    return p;
}

inline  double
EchoDoubleN( double p ) {
    return p;
}

sc1.swigcxx

%module sc1
%{
#include    "sc1.h"
%}

%include    "sc1.h"

ちなみに、sc1.hのインラインをやめて、sc1.hとsc1.cに分けて実施したら、こうなった

$ go build main1.go
# _/C_/Users/ebata/kese/swig2/sc1
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../x86_64-w6
4-mingw32/bin/ld.exe: $WORK\b002\_x004.o:/tmp/go-build/sc1_wrap.cxx:279: undefin
ed reference to `EchoIntN(int)'
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../x86_64-w6
4-mingw32/bin/ld.exe: $WORK\b002\_x004.o:/tmp/go-build/sc1_wrap.cxx:292: undefin
ed reference to `EchoDoubleN(double)'
collect2.exe: error: ld returned 1 exit status

ずいぶん悩んだが、もしかしたらと思い、sc1.c → sc1.cpp   にリネームしたら、あっさり通った

sc1.hは以下の通り

#pragma once 

int EchoIntN(int);

double EchoDoubleN(double);

sc1.cppは以下の通り

//#pragma once
#include "sc1.h"

int EchoIntN( int p ) {
    return p;
}

double EchoDoubleN( double p ) {
    return p;
}

以上

 

 

 

 

2021/03,江端さんの技術メモ

C 言語から GO 言語の関数を呼ぶ方法

を参考にさせて頂いて、実験中です。

大変申し訳ありませんが、ほぼ丸パクりさせて下さい。これから、頻繁に見る必要があり、万一ページがexpiredされたら青冷めますので。

hoge.goというファイル名のPrintHoge関数を C 言語から呼べるようにします。

  1. Cをインポートする
  2. C 言語から呼びたい関数に//export XXXXを書く (普通、//とするとコメントなんだけどなぁ)
  3. コンパイルする
package main

import (
    "C"
    "fmt"
)

//export PrintHoge
func PrintHoge() {
    fmt.Println("hoge")
}

func main() {}

main関数がないとエラーになるので、空で良いので書いておきます。

-buildmode=c-archiveオプションをつけてgo buildします。

$ go build -buildmode=c-archive hoge.go

すると、hoge.aアーカイブとhoge.hヘッダが生成されます。

生成されたヘッダをインクルードして、Go 言語の関数を呼びます。

#include <stdio.h>
#include "hoge.h"

int main() {
    PrintHoge();
    return 0;    
}

生成されたアーカイブと一緒にコンパイルします。

$ gcc -o hoge hoge.a main.c

ところが、

$ gcc -o hoge hoge.a main.c
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/10.2.0/../../../../x86_64-w6
4-mingw32/bin/ld.exe: C:\Users\ebata\AppData\Local\Temp\cc1F3Mbe.o:main.c:(.text
+0xe): undefined reference to `PrintHoge'
collect2.exe: error: ld returned 1 exit status

が出てきて、「変だなー」と思いつつ、色々調べてみたのですが、hoge.aを一番最後にしたら、動きました。

$ gcc -o hoge main.c hoge.a
$ ./hoge
hoge
以上