<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                鏈接(Linking)就是通過符號將各個模塊組合成一個獨立的程序的過程。 鏈接的主要內容就是把各個模塊之間的相互引用部分處理好,使得各個模塊能夠正確地銜接。鏈接器所做的主要工作跟前面提到的“人工調整地址”本質上沒有什么兩樣,只不過現代的高級語言擁有諸多的特性,使得編譯器和鏈接器更為復雜,功能更為強大,但從原理上來講,無非是找到符號的地址,或者把指令中使用到的地址加以修正。這個過程稱為符號決議(Symbol Resolution)或者重定位(Relocation)。 對于簡單的C語言程序,鏈接過程如下圖所示。每個模塊的源文件(.c 和 .h)先被編譯成目標文件,再和系統庫一起鏈接成可執行文件。庫(Library)其實是一組目標文件的包,是將一些最常用的代碼編譯成目標文件后打包存放。 ![](https://box.kancloud.cn/03fd4f6cfb99c064cc538ef697ebe827_427x346.png) 系統庫這個概念比較模糊,專業一點應該叫做運行時庫(Runtime Library)。“運行時”就是程序運行期間,“運行時庫”包含了程序運行期間所需要的基本函數,是程序運行不可或缺的,例如輸入輸出函數 printf()、scanf(),內存管理函數 malloc()、free() 等。 鏈接過程并沒有想象中的復雜,它還是一個比較容易理解的概念。 假設一個程序有兩個模塊 main.c 和 module.c,我們在 module.c 中定義了函數 func(),并在 main.c 中進行了多次調用,當所有模塊被編譯成一個可執行文件后,每一處對 func() 函數的調用都會被替換為一個絕對地址。但由于每個模塊都是單獨編譯的,編譯器在處理 main.c 時并不知道 func() 的地址,所以需要把這些調用 func() 的指令的目標地址擱置,等到最后鏈接的時候再由鏈接器將這些地址修正。 如果沒有鏈接器,我們必須手工修正 func() 的地址。當 module.c 被修改并重新編譯時,func() 的地址極有可能改變,那么在 main.c 中所有使用到 func() 函數的地方,都要全部重新調整地址。這些繁瑣的工作將成為程序員的噩夢。 有了鏈接器,我們可以直接調用其他模塊中的函數而無需知道它們的地址,因為在鏈接的時候,鏈接器會根據符號 func 自動去 module.c 模塊查找 func 的地址,然后將 main.c 模塊中所有使用到 func 的指令重新修正,讓它們的目標地址成為真正的 func() 函數的地址。 這種在程序運行之前確定符號地址的過程叫做靜態鏈接(Static Linking);如果需要等到程序運行期間再確定符號地址,就叫做動態鏈接(Dynamic Linking)。 Windows 下的 .dll 或者 Linux 下的 .so 必須要嵌入到可執行程序、作為可執行程序的一部分運行,它們所包含的符號的地址就是在程序運行期間確定的,所以稱為動態鏈接庫(Dynamic Linking Library)。 變量和函數一樣,都是符號,都需要確定它的地址。例如在 a.c 中有一個 int 類型的全局變量 var,現在需要在 b.c 中對它賦值 42,對應的C語言代碼是: ~~~ var = 100; ~~~ 對應的匯編代碼為: ~~~ mov 0x2a, var ~~~ mov 用來將一份數據移動到一個存儲位置,這里表示將 0x2a 移動到 var 符號所代表的位置,也就是對 var 變量賦值。 當被編譯成目標文件后,得到如下的機器指令: ~~~ c705 ?00000000 ?0000002a ~~~ 由于在編譯時不知道變量 var 的地址,編譯器將這條 mov 指令的目標地址設置為 0,等到將目標文件 a.o 和 b.o 鏈接起來的時候,再由鏈接器對其進行修正。 假設生成可執行文件后變量 var 的地址為 0x1100,那么上面的機器指令就變為: ~~~ c705 ?00001100 ?0000002a ~~~ 這種地址修正的過程就是前面提到的重定位,每個需要被修正的地方叫做一個重定位入口(Relocation Entry)。重定位所做的工作就是給程序中每個這樣的絕對地址引用的位置“打補丁”,使它們指向正確的地址。 # 符號的概念 函數和變量在本質上是一樣的,都是地址的助記符,在鏈接過程中,它們被稱為符號(Symbol)。鏈接器的一個重要任務就是找到符號的地址,并對每個重定位入口進行修正。 我們可以將符號看做是鏈接中的粘合劑,整個鏈接過程正是基于符號才能正確完成。 目標文件被分成了多個部分,其中有一個叫做符號表(Symbol Value),它的段名是`.symtab`。符號表記錄了當前目標文件用到的所有符號,包括: 1. 全局符號,也就是函數和全局變量,它們可以被其他目標文件引用。 2. 外部符號(External Symbol),也就是在當前文件中使用到、卻沒有在當前文件中定義的全局符號。 3. 局部符號,也就是局部變量。它們只在函數內部可見,對鏈接過程沒有作用,所以鏈接器往往也忽略它們。 4. 段名,這種符號往往由編譯器產生,它的值就是該段的起始地址,比如`.text`、`.data`等。 對鏈接來說,最值得關注的是全局符號,也就是上面的第一類和第二類,其它符號都是次要的。 所有的符號都保存在符號表`.symtab`中,它一個結構體數組,每個數組元素都包含了一個符號的信息,包括符號名、符號在段中的偏移、符號大小(符號所占用的字節數)、符號類型等。 > 確切地說,真正的符號名字是保存在字符串表`.strtab`中的,符號表僅僅保存了當前符號在字符串表中的偏移。 # 符號決議(Symbol Resolution) 當要進行鏈接時,鏈接器首先掃描所有的目標文件,獲得各個段的長度、屬性、位置等信息,并將目標文件中的所有(符號表中的)符號收集起來,統一放到一個全局符號表。 在這一步中,鏈接器會將目標文件中的各個段合并到可執行文件,并計算出合并后的各個段的長度、位置、虛擬地址等。 在目標文件的符號表中,保存了各個符號在段內的偏移,生成可執行文件后,原來各個段(Section)起始位置的虛擬地址就確定了下來,這樣,使用起始地址加上偏移量就能夠得到符號的地址(在進程中的虛擬地址)。 這種計算符號地址的過程被稱為符號決議(Symbol Resolution)。 重定位表`.rel.text`和`.rel.data`中保存了需要重定位的全局符號以及重定位入口,完成了符號決議,鏈接器會根據重定位表調整代碼中的地址,使它指向正確的內存位置。 至此,可執行文件就生成了,鏈接器完成了它的使命 # 全局變量和局部變量 當程序被加載到內存后,全局變量要在數據區(全局數據區)分配內存,局部變量要在棧上分配內存 數據區在程序運行期間一直存在,全局變量的位置不會改變,地址也是固定的,所以在鏈接時就能夠計算出全局變量的地址。而棧區內存會隨著函數的調用不斷被分配和釋放,局部變量的地址不能預先計算,必須等到發生函數調用時才能確定,所以鏈接過程會忽略局部變量。 關于局部變量的定位,就是 ebp 加上偏移量,這在編譯階段就能給出計算公式(一條簡單的語句),程序運行后,只要執行這條語句,就能夠得到局部變量的地址。 總結起來,鏈接的一項重要任務就是確定函數和全局變量的地址,并對每一個重定位入口進行修正
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看