update: 實現在此,歡迎star: https://github.com/dearplain/goloader
實現后的一些介紹:http://www.cnblogs.com/dearplain/p/8543804.html
golang動態加載原生代碼思路(非plugin,非so文件。使用mmap形式運行機器碼,可釋放)
1.用go tool objdump,可以看到任意函數的機器碼、匯編指令、偏移。(go源碼下面有一個cmd/internal/goobj包,可以讀到.o文件的重定向信息,更好。)
2.修改里面的golang內部函數的相對跳轉,指向加載者相同的函數的地址(可以用cmd/internal/goobj包讀取所有函數的地址),常見的有runtime.newobject、runtime.convT2Eslice、runtime.panicindex、runtime.morestack_noctxt等runtime系列函數。
3.修改golang類型指針偏移(當對象轉換成interface{}時候,需要一個類型指針),指向加載者相同的類型。
4.修改指向字符串,全局變量,自定義函數的偏移(一般都是相對偏移)。還有其他的一些信息,這個可以做實驗發現。
5.寫入mmap,並執行。
整體思路是,通過修改偏移,復用加載者所用到的函數、golang內部函數、golang類型信息等。
缺點:
1.可以自定義類型,但是不能將這些類型的對象賦值到interface{}(加載者已定義的類型可以),比如使用fmt.Println打印這些對象(但是可以打印這些對象的成員)。因為golang內部的一些全局變量(比如golang類型)可能存在指針,而且開始就初始化了。
2.不能在函數外初始化全局變量。(可能的解決方法:定義一個入口函數,在里面初始化,或者讀取main.init函數,取出初始化代碼。)
優點:
仍然使用golang和golang編譯工具。
速度極快,體積極小。相當於復用了golang內部的調度器、內存分配器、類型系統等。
可以自定義。golang函數內的匯編足夠簡單,可以開發自己的工具來實現上面的思路。
用途:
一些過濾,路由,緩存,前端邏輯。有很多邏輯直接在前端處理更好。
直接處理,不想放到后端,不想重啟程序的服務,類似openresty。
