2010-09-27 62 views
8

我正在寫一些代碼,它將一些數據結構存儲在一個特殊的命名二進制節中。這些都是相同結構的實例,它們分散在許多C文件中,並且不在彼此的範圍內。通過將它們全部放在指定的部分中,我可以遍歷所有這些部分。如何獲取指向MSVC中二進制節的指針?

在GCC,我使用_ 屬性 _((節(...)),加上一些特別命名的extern指針,其是由鏈接神奇填寫下面是一個簡單的例子:

#include <stdio.h> 

extern int __start___mysection[]; 
extern int __stop___mysection[]; 

static int x __attribute__((section("__mysection"))) = 4; 
static int y __attribute__((section("__mysection"))) = 10; 
static int z __attribute__((section("__mysection"))) = 22; 

#define SECTION_SIZE(sect) \ 
    ((size_t)((__stop_##sect - __start_##sect))) 

int main(void) 
{ 
    size_t sz = SECTION_SIZE(__mysection); 
    int i; 

    printf("Section size is %u\n", sz); 

    for (i=0; i < sz; i++) { 
     printf("%d\n", __start___mysection[i]); 
    } 

    return 0; 
} 

我試圖找出如何在MSVC中做到這一點,但我畫了一個空白。我從編譯器文檔中看到,我可以使用__pragma(section(...))聲明該部分,並聲明數據在使用__declspec(allocate(...)),但我看不到如何在運行時獲得指向該部分的開始和結束的指針。

我在網絡上看到了一些與MSVC中的_ 屬性 _((構造函數))相關的例子,但是它似乎像是針對CRT的黑客攻擊,而不是獲取指向開頭/結尾的指針的一般方法部分。有人有主意嗎?

+0

請問爲什麼要控制二進制文件命名呢? – Reinderien 2010-09-27 21:42:45

+0

這是一個高性能的儀器框架。想象一下printf(format,args ...)調用,其中所有格式字符串都存儲在二進制部分中,唯一記錄的是參數和查找值。參數替換髮生在後處理中。 – 2010-09-27 21:57:00

+0

這是一個更好的例子,它允許您通過重新鏈接而不是重新編譯(並可能重新生成一些代碼)來添加模塊。如果你可以把整個部分當作一個結構體的數組,那麼你可以迭代它並對每個入口執行一些操作,比如調用'cur_entry [i] - > init(&cur_entry)'。您也可以使用關於內存使用模式的特殊知識來優化分頁和緩存局部性。通常不是Windows相關的(我知道的),但這對於哈佛架構處理器也是必需的。 – nategoose 2010-09-27 23:16:15

回答

4

還有一種方法可以使用程序集文件進行此操作。

#pragma section(".init$a") 
#pragma section(".init$u") 
#pragma section(".init$z") 

__declspec(allocate(".init$a")) int InitSectionStart = 0; 
__declspec(allocate(".init$z")) int InitSectionEnd = 0; 

__declspec(allocate(".init$u")) int token1 = 0xdeadbeef; 
__declspec(allocate(".init$u")) int token2 = 0xdeadc0de; 

第3行定義段。這些定義了各個部分並取代了彙編文件。與data_seg pragma不同,section pragma只創建該部分。 __declspec(allocate())行指示編譯器將該項放入該段。

來自微軟網頁: 這裏的順序很重要。節名稱必須是8個字符或更少。 $之前的同名部分合併爲一個部分。它們合併的順序是通過對$之後的字符進行排序來確定的。

另一個要記住的重點是部分是0填充到256字節。 START和END指針不會像您期望的那樣直接出現在前後。

如果你設置你的表是函數指針或其它沒有NULL值,它應該很容易前後臺後跳過NULL項,由於部分填充

this msdn page更多細節

3

首先,你需要創建一個包含所有你感興趣的章節的ASM-文件(當然,section.asm):

.686 
.model flat 

PUBLIC C __InitSectionStart 
PUBLIC C __InitSectionEnd 

INIT$A SEGMENT DWORD PUBLIC FLAT alias(".init$a") 
     __InitSectionStart EQU $ 
INIT$A ENDS 

INIT$Z SEGMENT DWORD PUBLIC FLAT alias(".init$z") 
     __InitSectionEnd EQU $ 
INIT$Z ENDS 

END 

接下來,在你的代碼,你可以使用以下:

#pragma data_seg(".init$u") 
int token1 = 0xdeadbeef; 
int token2 = 0xdeadc0de; 
#pragma data_seg() 

這給出了這樣一個映射文件:

Start   Length  Name     Class 
0003:00000000 00000000H .init$a     DATA 
0003:00000000 00000008H .init$u     DATA 
0003:00000008 00000000H .init$z     DATA 

    Address   Publics by Value    Rva+Base  Lib:Object 
0003:00000000  [email protected]@3HA    10005000  dllmain.obj 
0003:00000000  ___InitSectionStart  10005000  section.obj 
0003:00000004  [email protected]@3HA    10005004  dllmain.obj 
0003:00000008  ___InitSectionEnd   10005008  section.obj 

所以,你可以看到它,段與南e .init$u被放置在.init$a.init$z之間,並且這使您能夠通過__InitSectionStart符號獲得指向數據開始的指針並且通過__InitSectionEnd符號獲得數據的末尾。

0

我在這裏試驗了一下,試圖在沒有裝配文件的情況下實現這個版本,但是在部分之間填充字節的隨機數目掙扎着,這使得幾乎不可能找到.init $ u部分的開始部分如果內容不只是指針或其他可以檢查NULL或其他已知模式的簡單項目。 是否插入填充似乎與使用調試選項Zi相關。在給定的情況下,插入填充,而不是,所有部分完全按照人們想要的方式顯示。