博客 / 詳情

返回

程序中嵌入任意文件二進制內容的幾個方法

程序多少都需要一點二進制資源,比如圖片、短音頻之類的。有的時候由於各種原因,可能就是需要把它直接塞到程序的可執行映像文件裏,而不是作為外部文件加載。經過一段時間的摸索,我發現有如下幾種方法,放在這裏以供參考:

轉成C源代碼

可以自己寫個程序或者腳本讀入資源文件,然後逐字節寫成C數組。這種方式的優點是跨平台性能最好,只要有C編譯器就行。缺點是它的執行代價比較大,首先你需要遍歷文件內容並且生成那麼長的源代碼,然後編譯器真的需要parse那麼長的源代碼。而且編譯器對單個源代碼的長度有限制,你不能轉換太大的資源文件。

Windows資源機制

就是rc文件和FindResourceLoadResourceLockResource那套Windows API。rc文件很容易用隨便什麼機制生成。唯一的缺點是它是Windows特有的。

GNU BinUtils

GNU的ObjDump可以將任意文件的內容轉換為可以鏈接的對象文件,並且暴露_binary_xxx_start和_binary_xxx_end兩個符號。實際使用ld調用這個功能可以不用聲明輸出格式,直接使用鏈接器的目標格式:

ld -r -b binary -o obj_file.o inputfile.png

此外,使用objcopy可以改符號名。比如我想把上面輸出文件的符號前綴改成_MyData

objcopy --redefine-sym _binary_inputfile_png_start=_MyData_start --redefine-sym _binary_inputfile_png_end=_MyData_end --redefine-sym _binary_inputfile_png_size=_MyData_size obj_file.o obj_file.o

這些命令都是工具鏈的一部分,很容易通過構建系統(比如CMake)調用。這個方式的限制是必須使用GNU的那一套。

彙編incbin

彙編語言的incbin可以包含任意文件,然後可以在包含前後創建符號,導出內容位置和尺寸的符號。

.const_data
.globl _MyData_BEGIN
.balign 16
_MyData_BEGIN:
.incbin "inputfile.png"
.globl _MyData_END
.balign 1
_MyData_END
.byte 1
.globl _MyData_SIZE
.balign 16
_MyData_SIZE
.long _MyData_END - _MyData_BEGIN
.text

這一段會導出_MyData_BEGIN_MyData_SIZE符號。可以把它作為內聯彙編塞在__asm__()裏面,塞給C編譯器。

彙編代碼的內容是固定的,很容易通過腳本生成。這個方法的限制在於你的C編譯器必須支持內聯彙編,我記得MSVC已經不支持了。

C23的#embed預處理指令

這個類似最前面那種方法,但是不需要真的把數組生成出來,只需要:

const char MyData[] = {
#embed "inputfile.png"
};

這個方法應當是最簡單的,限制是你的編譯器必須支持很新的語言標準。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.