本節是以 Golang 上游 1a708bcf1d17171056a42ec1597ca8848c854d2a 為基準做的實驗。
本日的內容資料很多,但或許值得參考的資訊並不多。本來筆者考慮將這個部分放置到附錄之中,但這麼一來又會有時間上的不連續感,最後還是作罷。也當作是個紀錄吧!Hoddarla 專案所需的工具鏈並非理所當然,而是經過這些不太優雅的手段之後,才稍微成形的。如果能讓讀者諸君理解這一點,那今天這篇的意義也就達到了。
予焦啦!讓我們接著處理工具鏈的問題。回顧昨日最後的編譯狀況,錯誤訊息如下:
$ GOOS=opensbi GOARCH=riscv64 go build ethanol/ethanol.go
# syscall
syscall/syscall.go:50:16: undefined: EINVAL
syscall/syscall.go:81:11: undefined: Timespec
syscall/syscall.go:86:11: undefined: Timeval
syscall/syscall.go:91:11: undefined: Timespec
syscall/syscall.go:96:11: undefined: Timeval
# runtime
runtime/alg.go:341:2: undefined: getRandomData
runtime/alg.go:351:2: undefined: getRandomData
runtime/proc.go:142:17: undefined: sigset
runtime/runtime2.go:519:16: undefined: gsignalStack
runtime/runtime2.go:520:16: undefined: sigset
runtime/runtime2.go:597:2: undefined: mOS
runtime/sigqueue.go:54:15: undefined: _NSIG
runtime/sigqueue.go:55:15: undefined: _NSIG
runtime/sigqueue.go:56:15: undefined: _NSIG
runtime/sigqueue.go:57:15: undefined: _NSIG
runtime/alg.go:351:2: too many errors
現在的目標是,將所有未定義的符號(無論是常數、變數、函式或是物件)一個一個加上去直到定義完全,就算只是空殼也都先讓它能夠通過編譯,製造出一個產物為止。當然,能夠編譯出成品不代表那個檔案真的可以執行在 OpenSBI 系統之上。我們在後續章節會再回顧這些部份。
這些冒出來的未定義符號顯然不可能出現在其它已經建立好的系統組合當中,也就是說,搜尋這些符號的時候,我們可以留意在其它系統組合之中的個別的定義。以下數小節就開始我們的搜尋之旅。
在 syscall
組件(package)之中,僅有 3 個符號未定義,以下小節逐個分析。
在原始碼中搜尋這個符號,會發現很多地方都只是使用到這個值,比方說像是位在 ./src/syscall/syscall_linux_riscv64.go
檔案中的 Pipe
函式:
152 func Pipe(p []int) (err error) {
153 if len(p) != 2 {
154 return EINVAL
155 }
...
Pipe
預期輸入參數應該剛好包含兩個整數,所以在除此之外的狀況回傳無效狀況的錯誤。
然而對於我們來說,更重要的是找到這個值如何定義。於是我們又看到很多 z 開頭的生成程式碼檔案:
...
./src/syscall/zerrors_linux_ppc64le.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_freebsd_386.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_openbsd_amd64.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_linux_ppc64.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_linux_386.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_linux_mips.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_solaris_amd64.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_linux_arm64.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_dragonfly_amd64.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_linux_riscv64.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_darwin_amd64.go: EINVAL = Errno(0x16)
./src/syscall/zerrors_linux_mipsle.go: EINVAL = Errno(0x16)
...
為了打造 opensbi/riscv64
系統組合的 zerror
檔,我們眼前有兩個選擇。一個是參考其它系統組合的正統作法,另外一個則是直接創個 zerrors_opensbi_riscv64.go
再將缺失的符號定義於其中。以結論而言,筆者後來採取的是後者。前者的作法適用於這些大部份的 Unix-like 作業系統,許多符號與定義都來自 C 語言世界的系統標頭檔案,如 /usr/include
或是 /include/sys
底下的內容,並且使用 Golang 的自動生成框架。筆者這裡選擇後者,以 src/syscall/tables_js.go
作為藍本,僅修改需要的部份如下:
js/wasm
比較特殊,是 Javascript 語言和 Web Assembly 的組合。某種程度來說,opensbi/riscv64
系統組合可以考慮借用該組合的一些簡潔性,將自己加入到已經頗為成熟的 Golang 之中。
7,8c7,8
< //go:build amd64 && openbsd
< // +build amd64,openbsd
---
> //go:build riscv64 && opensbi
> // +build riscv64,opensbi
為什麼看起來這兩行的資訊類似,卻需要同時存在呢?因為 Golang 社群打算在 1.17 版轉換這個編譯器指令的格式,目前是前後版本並存的狀態。
修改之後的錯誤變成:
$ GOOS=opensbi GOARCH=riscv64 go build ethanol/ethanol.go
# runtime
src/runtime/alg.go:341:2: undefined: getRandomData
src/runtime/alg.go:351:2: undefined: getRandomData
src/runtime/proc.go:142:17: undefined: sigset
src/runtime/runtime2.go:519:16: undefined: gsignalStack
src/runtime/runtime2.go:520:16: undefined: sigset
src/runtime/runtime2.go:597:2: undefined: mOS
src/runtime/sigqueue.go:54:15: undefined: _NSIG
src/runtime/sigqueue.go:55:15: undefined: _NSIG
src/runtime/sigqueue.go:56:15: undefined: _NSIG
src/runtime/sigqueue.go:57:15: undefined: _NSIG
src/runtime/alg.go:351:2: too many errors
...
看來 syscall
組件已經完成所有符號的定義了!我們一樣繼續這個搜尋-替換-重編譯的循環解決所有符號未定義問題。然後,我們將各個組件解決了的問題濃縮如下:
src/runtime/os_js.go
為 src/runtime/os_opensbi.go
opensbi
自 src/runtime/cputicks.go
的編譯相依性中除去opensbi
自 src/runtime/stubs2.go
的編譯相依性中除去由於 runtime
組件並非工具鏈的一環,我們不需要重編工具鏈。直接重新編譯之後,錯誤變為:
$ GOOS=opensbi GOARCH=riscv64 go build ethanol/ethanol.go
# runtime
go/src/runtime/chan.go:200:2: undefined: lock
go/src/runtime/chan.go:203:3: undefined: unlock
go/src/runtime/chan.go:210:28: undefined: unlock
go/src/runtime/chan.go:226:3: undefined: unlock
go/src/runtime/chan.go:231:3: undefined: unlock
go/src/runtime/chan.go:360:2: undefined: lock
go/src/runtime/chan.go:362:3: undefined: unlock
go/src/runtime/chan.go:416:2: undefined: unlock
go/src/runtime/chan.go:508:2: undefined: lock
go/src/runtime/chan.go:514:3: undefined: unlock
go/src/runtime/chan.go:514:3: too many errors
src/runtime/lock_js.go
為 src/runtime/lock_opensbi.go
$ GOOS=opensbi GOARCH=riscv64 go build ethanol/ethanol.go
# runtime
go/src/runtime/debuglog.go:75:18: undefined: sysAlloc
go/src/runtime/debuglog.go:717:12: undefined: sysAlloc
go/src/runtime/extern.go:236:7: undefined: gogetenv
go/src/runtime/heapdump.go:713:3: undefined: sysFree
go/src/runtime/heapdump.go:731:4: undefined: sysFree
go/src/runtime/heapdump.go:734:8: undefined: sysAlloc
go/src/runtime/malloc.go:570:19: undefined: sysReserve
go/src/runtime/malloc.go:656:8: undefined: sysReserve
go/src/runtime/malloc.go:674:4: undefined: sysFree
go/src/runtime/malloc.go:799:15: undefined: sysReserve
go/src/runtime/malloc.go:799:15: too many errors
src/runtime/mem_js.go
為 src/runtime/mem_opensbi.go
# runtime
go/src/runtime/extern.go:236:7: undefined: gogetenv
go/src/runtime/mgcpacer.go:840:7: undefined: gogetenv
go/src/runtime/proc.go:222:7: undefined: _cgo_setenv
go/src/runtime/proc.go:225:7: undefined: _cgo_unsetenv
go/src/runtime/proc.go:714:21: undefined: gogetenv
go/src/runtime/proc.go:1245:5: undefined: netpollinited
go/src/runtime/proc.go:1246:11: undefined: netpoll
go/src/runtime/proc.go:1706:5: undefined: netpollinited
go/src/runtime/proc.go:1707:3: undefined: netpollBreak
go/src/runtime/proc.go:2753:5: undefined: netpollinited
go/src/runtime/proc.go:2753:5: too many errors
src/runtime/netpoll_stub.go
以支援 opensbisrc/runtime/env_posix.go
以支援 opensbi$ GOOS=opensbi GOARCH=riscv64 go build ethanol/ethanol.go
# internal/poll
src/internal/poll/fd_mutex.go:201:11: undefined: FD
src/internal/poll/fd_mutex.go:211:11: undefined: FD
src/internal/poll/fd_mutex.go:220:11: undefined: FD
src/internal/poll/fd_mutex.go:230:11: undefined: FD
src/internal/poll/fd_mutex.go:238:11: undefined: FD
src/internal/poll/fd_mutex.go:248:11: undefined: FD
# syscall
src/syscall/syscall.go:81:11: undefined: Timespec
src/syscall/syscall.go:86:11: undefined: Timeval
src/syscall/syscall.go:91:11: undefined: Timespec
src/syscall/syscall.go:96:11: undefined: Timeval
src/syscall/tables_opensbi.go:107:18: undefined: Errno
src/syscall/tables_opensbi.go:108:18: undefined: Errno
src/syscall/tables_opensbi.go:109:18: undefined: Errno
src/syscall/tables_opensbi.go:110:18: undefined: Errno
src/syscall/tables_opensbi.go:111:18: undefined: Errno
src/syscall/tables_opensbi.go:112:18: undefined: Errno
src/syscall/tables_opensbi.go:112:18: too many errors
syscall 組件的部份又出現了,先排除這個部份。
src/syscall/syscall_js.go
為 src/runtime/syscall_opensbi.go
# internal/poll
src/internal/poll/fd_mutex.go:201:11: undefined: FD
src/internal/poll/fd_mutex.go:211:11: undefined: FD
src/internal/poll/fd_mutex.go:220:11: undefined: FD
src/internal/poll/fd_mutex.go:230:11: undefined: FD
src/internal/poll/fd_mutex.go:238:11: undefined: FD
src/internal/poll/fd_mutex.go:248:11: undefined: FD
# syscall
src/syscall/syscall_opensbi.go:29:9: undefined: readInt
src/syscall/syscall_opensbi.go:285:12: undefined: Getcwd
src/syscall/syscall_opensbi.go:293:9: undefined: jsProcess
src/syscall/syscall_opensbi.go:297:9: undefined: jsProcess
src/syscall/syscall_opensbi.go:301:9: undefined: jsProcess
src/syscall/syscall_opensbi.go:305:9: undefined: jsProcess
src/syscall/syscall_opensbi.go:309:8: undefined: recoverErr
src/syscall/syscall_opensbi.go:310:11: undefined: jsProcess
src/syscall/syscall_opensbi.go:319:9: undefined: jsProcess
src/syscall/syscall_opensbi.go:323:9: undefined: jsProcess
src/syscall/syscall_opensbi.go:323:9: too many errors
src/syscall/dirent.go
以支援 opensbisrc/syscall/syscall_opensbi.go
裡面的 Getwd
函式、recoverErr
函式以及所有用到 jsProcess
函式的部份,反正這些都不需要# internal/poll
src/internal/poll/fd_mutex.go:201:11: undefined: FD
src/internal/poll/fd_mutex.go:211:11: undefined: FD
src/internal/poll/fd_mutex.go:220:11: undefined: FD
src/internal/poll/fd_mutex.go:230:11: undefined: FD
src/internal/poll/fd_mutex.go:238:11: undefined: FD
src/internal/poll/fd_mutex.go:248:11: undefined: FD
# time
src/time/zoneinfo.go:92:16: undefined: initLocal
src/time/zoneinfo.go:653:13: undefined: syscall.Getenv
src/time/zoneinfo.go:667:34: undefined: zoneSources
src/time/zoneinfo_read.go:402:13: undefined: open
src/time/zoneinfo_read.go:406:8: undefined: closefd
src/time/zoneinfo_read.go:418:12: undefined: preadn
src/time/zoneinfo_read.go:426:12: undefined: preadn
src/time/zoneinfo_read.go:489:13: undefined: preadn
src/time/zoneinfo_read.go:499:13: undefined: preadn
src/time/zoneinfo_read.go:563:12: undefined: open
src/time/zoneinfo_read.go:563:12: too many errors
src/internal/poll/fd_plan9.go
作為src/internal/poll/fd_opensbi.go
,並將函數都清為空函數之所以選擇 plan9 作業系統的 FD 實作,只是因為該作業系統在 Golang 裡面的份量比較小,之後如果需要調整的話會比較容易。
# time
src/time/zoneinfo.go:92:16: undefined: initLocal
src/time/zoneinfo.go:653:13: undefined: syscall.Getenv
src/time/zoneinfo.go:667:34: undefined: zoneSources
src/time/zoneinfo_read.go:402:13: undefined: open
src/time/zoneinfo_read.go:406:8: undefined: closefd
src/time/zoneinfo_read.go:418:12: undefined: preadn
src/time/zoneinfo_read.go:426:12: undefined: preadn
src/time/zoneinfo_read.go:489:13: undefined: preadn
src/time/zoneinfo_read.go:499:13: undefined: preadn
src/time/zoneinfo_read.go:563:12: undefined: open
src/time/zoneinfo_read.go:563:12: too many errors
還記得上一章結尾前的
okgoos
嗎?若是沒有加入,就會節外生枝的發現,新增了這個檔案之後即使能夠解消FD
未定義的問題,但若是重新編譯工具鏈,反而會出現問題:
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_poll_runtime.go:132:15: (*FD).SetDeadline redeclared in this block /home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_opensbi.go:32:6: previous declaration /home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_poll_runtime.go:137:15: (*FD).SetReadDeadline redeclared in this block
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_opensbi.go:36:6: previous declaration
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_poll_runtime.go:142:15: (*FD).SetWriteDeadline redeclared in this block
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_opensbi.go:40:6: previous declaration
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_poll_runtime.go:146:6: setDeadlineImpl redeclared in this block
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_opensbi.go:44:53: previous declaration
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_posix.go:57:15: (*FD).RawControl redeclared in this block
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_opensbi.go:48:6: previous declaration
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_unix.go:18:6: FD redeclared in this block
/home/noner/FOSS/hoddarla/ithome/go/src/internal/poll/fd_opensbi.go:11:6: previous declaration
會變成有一堆重複定義的問題!剛才才新增的
fd_opensbi.go
中的部份函數與許多其它的fd_poll_runtime.go
、fd_unix.go
、fd_posix.go
重複定義了。這都是因為opensbi
不被認識為正式的 Golang 作業系統,不具有排他性,因此無法將其它三者排除在外。
src/time/zoneinfo_plan9.go
作為src/time/zoneinfo_opensbi.go
src/time/sys_plan9.go
作為src/time/sys_opensbi.go
# time
src/time/sys_opensbi.go:21:13: undefined: syscall.Open
src/time/sys_opensbi.go:29:9: undefined: syscall.Read
src/time/sys_opensbi.go:33:2: undefined: syscall.Close
src/time/sys_opensbi.go:41:15: undefined: syscall.Seek
src/time/sys_opensbi.go:45:13: undefined: syscall.Read
src/time/zoneinfo.go:653:13: undefined: syscall.Getenv
src/time/zoneinfo_opensbi.go:126:11: undefined: syscall.Getenv
src/syscall/syscall_plan9.go
,修改 src/syscall/syscall_opensbi.go
,新增 Read
、Open
、Close
、Seek
等空函數src/syscall/env_windows.go
作為 src/syscall/env_opensbi.go
,並將其中的函數都改為空函數# os
src/os/exec.go:128:28: undefined: ProcessState
src/os/exec.go:139:10: undefined: ProcessState
src/os/exec.go:144:10: undefined: ProcessState
src/os/exec.go:149:10: undefined: ProcessState
src/os/exec.go:155:10: undefined: ProcessState
src/os/exec.go:162:10: undefined: ProcessState
src/os/exec.go:171:10: undefined: ProcessState
src/os/file.go:66:11: undefined: NewFile
src/os/file.go:67:11: undefined: NewFile
src/os/types.go:17:2: undefined: file
src/os/file.go:67:11: too many errors
src/os/file_plan9.go
作為 src/os/file_opensbi.go
,並將其中函數清空src/os/exec_plan9.go
作為 src/os/exec_opensbi.go
,並將其中函數清空src/os/rawconn.go
,使之除了 plan9
之外也不會編譯到 opensbi
# os
src/os/dir.go:41:23: f.readdir undefined (type *File has no field or method readdir, but does have Readdir)
src/os/dir.go:70:22: f.readdir undefined (type *File has no field or method readdir, but does have Readdir)
src/os/dir.go:98:25: f.readdir undefined (type *File has no field or method readdir, but does have Readdir)
src/os/executable.go:19:9: undefined: executable
src/os/file.go:268:10: undefined: syscall.Mkdir
src/os/file.go:300:10: undefined: syscall.Chdir
src/os/getwd.go:29:14: undefined: statNolog
src/os/getwd.go:35:13: undefined: statNolog
src/os/getwd.go:62:13: undefined: statNolog
src/os/getwd.go:70:15: undefined: statNolog
src/os/getwd.go:70:15: too many errors
src/os/executable_plan9.go
作為 src/os/executable_opensbi.go
,並將函數清空。src/os/stat_plan9.go
作為 src/os/stat_opensbi.go
,並將函數清空。src/os/dir_plan9.go
作為 src/os/dir_opensbi.go
,並將函數清空。src/syscall/dir_plan9.go
作為 src/syscall/dir_opensbi.go
,並將函數清空,雖然是在處理 os
組件,但是會需要用到 syscall
組件,因此補上。# os
src/os/file.go:268:10: undefined: syscall.Mkdir
src/os/file.go:300:10: undefined: syscall.Chdir
src/os/path.go:30:15: undefined: IsPathSeparator
src/os/path.go:35:16: undefined: IsPathSeparator
src/os/path.go:41:18: undefined: fixRootDirectory
src/os/path.go:75:51: undefined: IsPathSeparator
src/os/removeall_noat.go:70:37: undefined: PathSeparator
src/os/sys.go:9:9: undefined: hostname
src/os/tempfile.go:49:64: undefined: PathSeparator
src/os/tempfile.go:61:6: undefined: IsPathSeparator
src/os/tempfile.go:61:6: too many errors
src/syscall/fs_js.go
作為 src/syscall/fs_opensbi.go
,並將函數清空。src/os/path_plan9.go
作為 src/os/path_opensbi.go
,並將函數清空。src/os/sys_plan9.go
作為 src/os/sys_opensbi.go
,並將函數清空。# command-line-arguments
2021/05/30 11:42:15 unknown thread-local storage offset for 8
在 os
組件的第三階段解決了符號定義問題之後,出現的新問題並非其他組件的未定義符號,而是工具鏈本身的警告訊息。稍加檢索之後,可以發現該訊息位於 src/cmd/link/internal/ld/sym.go
之中的 computeTLSOffset
函數,它僅由一個 switch-case 結構構成。而我們先前所見的警告訊息,是進入 default
選項之後造成的。在這裏新增 opensbi
進來即可。
由於改動在工具鏈中,需要重新編譯。編譯完之後的結果,仍然有問題
# command-line-arguments
/home/noner/FOSS/hoddarla/ithome/go/pkg/tool/linux_amd64/link: unknown -H option: 8
循線追查的結果,這個在 cmd/link/internal/riscv64/obj.go
之中的 archinit
函數。原本 riscv64
架構在這裡只接受 linux
作業系統,因此我們正在加入的 opensbi
會在 defult
選項中受到警告。將之加入,則
# command-line-arguments
2021/05/30 15:53:26 duplicated definition of symbol runtime.cputicks, from runtime and runtime
看來又回到 runtime
組件的問題了。這次是 cputicks
,我們除了在原本的 src/runtime/os_opensbi.go
裡面存有定義之外,在 src/runtime/asm_riscv64.go
之中也有實作,因此我們可以將前者的實作改為僅有標頭的宣告。
# command-line-arguments
runtime.notetsleepg: relocation target runtime.nanotime1 not defined
runtime.notetsleepg: relocation target runtime.scheduleTimeoutEvent not defined
runtime.notetsleepg: relocation target runtime.clearTimeoutEvent not defined
runtime.checkTimeouts: relocation target runtime.nanotime1 not defined
runtime.beforeIdle: relocation target runtime.clearTimeoutEvent not defined
runtime.beforeIdle: relocation target runtime.scheduleTimeoutEvent not defined
...
package runtime
src/runtime/os_plan9.go
為靈感,編輯 src/runtime/os_opensbi.go
,補足沒有定義的部分。src/runtime/stubs.go
,使之不會為了 opensbi
編成 nanotime1
函數。src/runtime/timestubs2.go
。src/runtime/mem_opensbi.go
。# command-line-arguments
panic: unknown platform
goroutine 1 [running]:
cmd/link/internal/ld.asmb2(0xc00018e000)
/home/noner/FOSS/hoddarla/ithome/go/src/cmd/link/internal/ld/asmb.go:92 +0x325
cmd/link/internal/ld.Main(_, {0x8, 0x20, 0x1, 0x2, 0x1, 0x0, {0x0, 0x0}, {0x66fb63, ...}, ...})
/home/noner/FOSS/hoddarla/ithome/go/src/cmd/link/internal/ld/main.go:355 +0x13a5
main.main()
/home/noner/FOSS/hoddarla/ithome/go/src/cmd/link/main.go:69 +0xe1b
在我們解決完這一輪的 runtime
組件問題之後,又有工具鏈問題浮現!這次是 unknown platform
?一樣我們直接搜尋資料夾,發現是在 src/cmd/link/internal/ld/asmb.go
裡面的 asmb2
函數,要決定輸出的二進位檔格式。這裏我們就加入 linux
的陣營,一樣使用廣為使用的 ELF 檔吧!別忘了重新編譯工具鏈:
# command-line-arguments
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4fa6ad]
goroutine 1 [running]:
cmd/link/internal/loader.(*Loader).SymType(0x57e936, 0xc0001420c0)
/home/noner/FOSS/hoddarla/ithome/go/src/cmd/link/internal/loader/loader.go:818 +0x4d
cmd/link/internal/ld.Entryvalue(0xc000120000)
/home/noner/FOSS/hoddarla/ithome/go/src/cmd/link/internal/ld/lib.go:2444 +0x97
...
這裡的關鍵字是 Entryvalue
。今天的目標是產出可執行檔,但 opensbi/riscv64
系統組合的可執行檔的進入點在哪裡呢?
關於 Golang 編成的 ELF 檔的解析,可以參考拙作。
linux/riscv64
系統組合為例先為這個系統組合編成可執行檔,
$ GOOS=linux GOARCH=riscv64 go build ethanol/ethanol.go
再以 readelf
工具觀察之
$ riscv64-buildroot-linux-musl-readelf -h hw [4/1889]
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: RISC-V
Version: 0x1
Entry point address: 0x77898
...
可知,是在 0x77898
的某個函數。再以 objdump
工具考察,
0000000000077898 <_rt0_riscv64_linux>:
77898: 00013503 ld a0,0(sp)
7789c: 00810593 addi a1,sp,8
778a0: 00000f97 auipc t6,0x0
778a4: 008f8067 jr 8(t6) # 778a8 <main>
檢索 src/runtime
資料夾下可發現,這位在 rt0_linux_riscv64.s
檔案中。我們將之複製為 rt0_opensbi_riscv64.s
,並更換內部函數名稱,再行編譯的話:
$ GOOS=opensbi GOARCH=riscv64 go build ethanol/ethanol.go
成功了!確實已經成功產出一個 ELF 檔,當然其中很多函數都是拼湊出來的空殼,但至少我們已經有一個工具鏈環境足堪使用。
予焦啦!本篇其實沒有什麼紮實功夫,不過就是創造一些空殼好讓編譯能夠順利進行而已。筆者本來也在考慮要不要乾脆將之列為附錄章節,但這麼一來,整個 Hoddarla 專案的順序就不明顯了。但如果說從中沒有什麼東西可以學習的話,也不正確。至少筆者曾試著從我們解決的未定義符號當中看出意義,或者就以之為關鍵字,理解 Golang 怎麼樣兼容不同的系統組合,相關的函數又通常用以解決甚麼問題等等。除此之外,也能夠建立一些與 Golang 本身相關的開發技巧。
在 github 上,至本日為止的狀態已經更新。
無論如何,這樣逞著匹夫之勇的筆者修修補補之後,搞定了一個可以服務於 opensbi/riscv64
的 Golang 工具鏈。讓我們開始以這個基本裝備繼續 Hoddarla 之旅吧!各位讀者,我們明天再會!