<!-- $FreeBSD$ -->
<!-- The FreeBSD Documentation Project -->
<!-- Translate into Chinese by jtjang@gcn.net.tw -->
<!-- English Version: 1.11 -->

  <sect>
    <heading>給有心深入研究的 FreeBSD hacker <label id="hackers"></heading>

    <sect1>
      <heading>SNAPs 和 RELEASEs 是什麼？</heading>

      <p>目前有三個活躍/半活躍的分支在 FreeBSD 的
      <url url="http://www.FreeBSD.org/cgi/cvsweb.cgi" name="CVS Repository">:

      <itemize>
        <item><bf/RELENG_2_2/   即 <bf/2.2-stable/ 即 <bf/"2.2 branch"/
        <item><bf/RELENG_3/     即 <bf/3.x-stable/ 即 <bf/"3.0 branch"/
        <item><bf/HEAD/         即 <bf/-current/ 即 <bf/4.0-current/
      </itemize>

      <p>如同其他兩個，<bf/HEAD/ 並不是真正的 branch tag，它只是一個符號
      常數，指向 <em/"current：尚未分支的發展中版本"/ ，簡記為
      <bf/-current/。

      <p>以現在來說，<bf/-current/ 朝向 4.0 發展，而 <bf/3.0-stable/ 這
      個分支，也就是 <bf/RELENG_3/，在 1999 年 1 月從 <bf/-current/ 分出
      來。

      <p><bf/2.2-stable/ 這個分支，也就是 <bf/RELENG_2_2/，是在 1996 年
      11 月從 -current 分出來。

      <p><bf/2.1-stable/ 這個分支，也就是 <bf/RELENG_2_1_0/，則是在
      1994 年 9 月從 -current 分支出來，這個分支已經完全退休了。

    <sect1>
      <heading>
        我要怎麼作出自己的 release？<label id="custrel">
      </heading>

      <p>做 release 包括下面這三個步驟：首先，做出有 <htmlurl 
      url="http://www.FreeBSD.org/cgi/man.cgi?vn" name="vn"> 這個驅動程
      式的可用 kernel。把下面這一行加到 kernel 設定檔，然後做出新的
      kernel 重新開機：

      <verb>
        pseudo-device vn         #Vnode driver (turns a file into a device)
      </verb>

      <p>接著，你手上要有整個 CVS repository。可以參考 <url
      url="../../handbook/synching.html#CVSUP" name="CVSUP"> 這篇文章，但在
      supfile 中把 release 名稱設成 cvs，再刪掉所有 tag 或 date 的欄位，如
      下：

      <verb>
        *default prefix=/home/ncvs
        *default base=/a
        *default host=cvsup.FreeBSD.org
        *default release=cvs
        *default delete compress use-rel-suffix

        ## Main Source Tree
        src-all
        src-eBones
        src-secure

        # Other stuff
        ports-all
        www
        doc-all
      </verb>

      <p>然後執行 <tt/cvsup -g supfile/ 把所有東西都抓下來...

      <p>最後，硬碟要有相當大的空間來做 release。假設你想把它放在
      <tt>/some/big/filesystem</tt> 這裡，上面這個例子也把 CVS
      repository 放在 <tt>/home/ncvs</tt> 了，接著：

      <verb>
        setenv CVSROOT /home/ncvs        # or export CVSROOT=/home/ncvs
        cd /usr/src/release
        make release BUILDNAME=3.0-MY-SNAP CHROOTDIR=/some/big/filesystem/release
      </verb>

      <p>整個 release 會做在 <tt>/some/big/filesystem/release</tt>。結束
      時 <tt>/some/big/filesystem/release/R/ftp</tt> 這個目錄可以直接用
      來做為 FTP 安裝方式的來源。如果想做出 -current 以外分支的 SNAP，
      在上面 make release 這一行加 <tt/RELEASETAG=SOMETAG/。舉例來說，
      <tt/RELEASETAG=RELENG_2_2/ 這個參數會做個即時的 2.2-STABLE snapshot。

    <sect1>
      <heading>怎樣才能做出自己用的安裝磁片？</heading>

      <p>建立安裝磁片、還有做出 source/binary archive，都是由
      <tt>/usr/src/release/Makefile</tt> 裡面的各種 target 自動產生，這
      個檔案裡的資訊應該足以開始。但是這個過程牽涉到 make world，所以會
      用到相當多的時間和硬碟空間。

    <sect1>
      <heading>``make world'' 把原來裝的 binary 檔都換掉了。</heading>

      <p>沒錯，就是這樣子。如名字所示，``make world'' 會重新編譯系統內建
      的每個 binary 檔，這樣在結束時就可確定有個一致且乾淨的環境(所以要
      花上好一段時間)。

      <p>在執行 ``<tt/make world/'' 或 ``<tt/make install/'' 時，如果有
      設 <tt/DESTDIR/ 這個環境變數，新產生的 binary 將會裝在
      <tt>&dollar;&lcub;DESTDIR&rcub;</tt> 下的同樣目錄樹中。但在某些修
      改 shared library 和重建 binary 的無特定情況下，這樣做可能會使
      ``<tt/make world/'' 失敗。

    <sect1>
      <heading>
        在系統開機時，出現 ``(bus speed defaulted)''。
      </heading>

      <p>Adaptec 1542 SCSI 卡允許使用者用軟體調整匯流排的存取速度。早
      期的 1542 驅動程式試圖將它設成可用的最快速度，但後來發現在一些
      機器上不能用，所以現在要在 kernel 設定中加 ``<tt/TUNE&lowbar;1542/''
      這個選項來啟動這個功能。在支援的機器上用這個選項會使硬碟存取更
      快，但在不支援的機器上有可能會毀掉資料。

    <sect1>
      <heading>
        我的網路連線不快，那可以跟著 current 的發展嗎？<label id="ctm">
      </heading>

      <p>可以，藉著 <url url="../../handbook/synching.html#CTM" name="CTM"> 就
      可以不用下傳所有的原始碼目錄樹。

    <sect1>
      <heading>你是怎麼把發行版本中的檔案切成一個個 240k 的小檔案？</heading>

      <p>在以 BSD 為主的較新系統中，split 有個 ``<tt/-b/'' 選項，是用來
      把檔案以任意數目 byte 切開。

      <p>這裡是 <tt>/usr/src/Makefile</tt> 中的一個例子：

      <verb>
        bin-tarball:
        (cd $&lcub;DISTDIR&rcub;; \
        tar cf - . \
        gzip --no-name -9 -c | \
        split -b 240640 - \
        $&lcub;RELEASEDIR&rcub;/tarballs/bindist/bin_tgz.)
      </verb>

    <sect1>
      <heading>我在 kernel 裡加了些新功能，要把它寄給誰？</heading>

      <p>請看一下 <url url="../../handbook/contrib.html"
      name="Handbook 中加入程式碼的部份">。

      <p>同時也感謝你的費心！

    <sect1>
      <heading>ISA 的 Plug N Play 卡是怎麼偵測和初始化的？</heading>

      <p>由 <url url="mailto:uhclem@nemesis.lonestar.org"
      name="Frank Durda IV"> 所寫：

      <p>簡單的說，當主機發出是否有 PnP 卡的詢問訊號時，所有的 PnP
      會在幾個固定的 I/O port 作回應。所以當偵測 PnP 的程式開始時，它
      會先問有沒有 PnP 卡在，接著所有 PnP 卡會在它所讀的 port 以自己
      的型號 &num; 作回答，這樣偵測程式就會得到一個 wired-OR ``yes'' 
      的數字，其中至少會有一個 bit 是打開的。然後偵測程式會要求型號
      (由 Microsoft/Intel指定)小於 X 的卡``離線''，再去看是否還有卡回
      答同樣的詢問，如果得到 ``<tt/0/''，就表示沒有型號大於 X 的卡。
      現在程式會問是否有型號小於 X 的卡，如果有的話，程式再要型號大於
      X-(limit/4) 的卡離線，然後重覆上面的動作。用這種類似 binary
      search 的方法，在某範圍內找個幾次後，偵測程式最後會在機器中區分
      出所有的 PnP 卡，搜尋次數也遠低於一個個找的 2^64 次。

      <p>一張卡的 ID 由兩個 32-bit(所以上面是 2&circ;64) + 8bit 偵錯
      碼組成，第一個 32 bits 是用來區分各家廠商的。這些廠商沒有出來澄
      清過，但看來應假設同一家出的不同種類的卡的廠商 ID 有可能不同。
      用 32 bits 只來表示不同廠商的想法實在有點過頭了。

      <p>第二個 32 bits 則是型號 &num、乙太網路位址、或一些使這張卡獨
      特的資料。除非第一個 32 bits 不同，否則廠商不可能作出第二個 32
      bit 相同的兩張卡。所以在一台機器中可以有同樣的好幾張卡，然而他們
      整個 64 bits 還是會都不一樣。

      <p>這兩個 32 bit 永遠都不可能為零，這使得最開始 binary search 中
      的 wired-OR 會得到一個非零數字。

      <p>一旦系統區分出所有卡的 ID，接著會經由同樣的 port 一個個重新啟
      動每張卡，接著找出已知介面卡所需的資源、有那些可以選的 interrupt
      等等。所有卡都會被掃描一次，來收集這些資料。

      <p>這些資訊接著和硬碟上的 ECU 檔案、或 MLB BIOS 裡的資料結合在一
      起，通常是綜合 ECU 和 MLB 裡的 BIOS PnP 資料，這些週邊並不支援真
      正的 PnP，然而偵測程式在檢查 BIOS 和 ECU 資料後，它可以避免 PnP
      週邊和那些偵測不到的相衝突。

      <p>接著再度拜訪這些 PnP 週邊，這次會把可用的 I/O、DMA、IRQ 和記
      憶體映射的位址都指定給它們。這些週邊就會出現在所指定的地方，直到
      下一次重新開機為止，不過也沒有人說不能把它們隨時移來移去。

      <p>上面有相當多的簡化，但你應該已經了解大致的過程。

      <p>Microsoft 把表示印表機狀態的幾個主要 port 拿來作 PnP，他們的
      邏輯是沒有一張卡會在這些地方解碼作相反的 I/O cycles。但是我找到
      一款早期仍在評估 PnP 提案時的 IBM 原廠 printer board，它的確去解
      對這些狀態 port 的寫入資料，但是 MS ``說了就算''。所以它們的確有
      對印表機狀態 port 寫入，還有讀取該位址 + <tt/0x800/、和另一個在
      <tt/0x200/ 及 <tt/0x3ff/ 之間的 port。

    <sect1>
      <heading>FreeBSD 是否有支援 x86 以外的機器架構？</heading>

      <p>有幾群人士已經表示對發展多平台 FreeBSD 的興趣，其中
      FreeBSD/AXP (ALPHA) 即是其中相當成功的例子，可以在 <url
      url="ftp://ftp.FreeBSD.org/pub/FreeBSD/alpha/"
      name="ftp://ftp.FreeBSD.org/pub/FreeBSD/alpha"> 這裡取得它的
      3.0 SNAPshot 版本。ALPHA 版的 FreeBSD 在越來越多的 ALPHA 機器上
      使用，其中包括了 AlphaStation、AXPpci、PC164、Miata 和 Multia
      這幾種。要等到系統安裝工具完全、能用光碟安裝、以及有足夠能用的
      port/package 之後，我們才打算把它當作完整的版本，所以現在應該把
      FreeBSD/AXP 當作 BETA 品質的軟體。若要相關狀況的消息，請加入
      <tt>&lt;freebsd-alpha@FreeBSD.org&gt;</tt><ref id="mailing"
      name="mailing list">.

      也有人表示過將 FreeBSD SPARC 版本的興趣，如果你想參加這個計畫，
      請加入 <tt>&lt;freebsd-sparc@FreeBSD.org&gt;
      </tt><ref id="mailing" name="mailing list">。如果想要知道關於
      新平台的討論，請加入 <tt>&lt;freebsd-platforms@FreeBSD.org&gt;</tt>
      <ref id="mailing" name="mailing list">。

    <sect1>
      <heading>我剛剛寫了某個設備的驅動程式，能不能給它一個 major number？
      </heading>

      <p>這要看你是否打算將這個驅動程式公開使用，如果是的話，請把它的原始
         碼送一份給我們，還有 <tt>files.i386</tt> 修改的部份、kernel 設定
         檔樣本、以及用來產生設備檔的 <htmlurl
         url="http://www.FreeBSD.org/cgi/man.cgi?MAKEDEV" name="MAKEDEV">。
         如果你不打算、或因為版權問題而不能公開的話，我們有特地保留
         character major number 32 和 block major number 8 給這方面的使用，
         直接用這兩個就好了。不論如何，我們都會很感激你能在
         <tt>&lt;freebsd-hackers@FreeBSD.org&gt;</tt> 發表驅動程式的消息。

    <sect1>
      <heading>關於放置目錄和檔案 inode 作法上的相異</heading>

      <p>在回答有關目錄放置方式不同的問題上，我在 1983 年寫好目前的作法
         後就沒有再改變過，這種方式是針對原先的 FFS 檔案系統，後來也沒
         有對它作任何更動。它在避免 cylinder group 被填滿這方面做得相當
         成功，但是就像有些人已經注意到，它和 `find' 就配合得不大好。大
         部份的檔案系統是由那些用 depth first search(aka ftw) 產生的
         archive 製造出來，解出來的目錄 inode 會橫跨好幾個 cylinder
         group，如果以後要做 depth first search 的話，這是最糟糕的情況之
         一。如果我們知道總共會產生多少目錄的話，解法是在做任何存取/寫
         入動作之前，在每個 cylinder group 上先造出(所有目錄數/cylinder
         greoup 的數目)這麼多的目錄。很明顯的，我們必須要有根據地去猜這
         個數字，就算一個像 10 的很小固定數目也會使效率以級數成長。區分
         restore (即解開上述的 archive) 和一般檔案操作的方法可以是(現在
         用的演算法可能要更敏感)：如果一些目錄(最多 10 個)都在 10 秒內產
         生的話，那麼就把這些目錄聚集在同一個 cylinder group。不管怎樣，
         我的經驗指出這是一個已經充份實驗過的部份。</p>

      <p>Kirk McKusick, September 1998</p>

    <sect1>
      <heading>如何從 kernel panic 得到最多資訊？</heading>

      <p>      
      <em>[這節是從 <url url="mailto:wpaul@FreeBSD.org" name="Bill Paul">
      在 freebsd-current <ref id="mailing" name="mailing list"> 上發表
      的信中節錄，<url url="mailto:des@FreeBSD.org"
      name="Dag-Erling Co&iuml;dan Sm&oslash;rgrav"> 修正了打字錯誤、再
      加上括弧裡的注解。]</em>

      <p>
      <verb>
From: Bill Paul <wpaul@skynet.ctr.columbia.edu>
Subject: Re: the fs fun never stops
To: ben@rosengart.com
Date: Sun, 20 Sep 1998 15:22:50 -0400 (EDT)
Cc: current@FreeBSD.org
      </verb>

      <p>
      <em>[&lt;ben@rosengart.com&gt; 發表了下面的 panic 訊息]</em>
      <verb>
> Fatal trap 12: page fault while in kernel mode
> fault virtual address   = 0x40
> fault code              = supervisor read, page not present
> instruction pointer     = 0x8:0xf014a7e5
                                ^^^^^^^^^^
> stack pointer           = 0x10:0xf4ed6f24
> frame pointer           = 0x10:0xf4ed6f28
> code segment            = base 0x0, limit 0xfffff, type 0x1b
>                         = DPL 0, pres 1, def32 1, gran 1
> processor eflags        = interrupt enabled, resume, IOPL = 0
> current process         = 80 (mount)
> interrupt mask          =
> trap number             = 12
> panic: page fault
      </verb>
      
      <p>當你看到像這樣的訊息時，只把它拷一份送上來是不夠的。我在上
      面特地標明的 instruction pointer 值相當重要，不幸的是它會因設
      定而不同。換句話說，這個值會跟你用的 kernel image 檔而變動。如
      果是用某個 snapshot 版本的 GENERIC kernel，也許其他人可以追蹤
      到出問題的函式，但如果你是用自訂的 kernel，那麼只有<em/你/才能
      告訴我們問題出在那裡。

      <p>要做的事包括這些：

      <itemize>
        <item>把 instruction pointer 的值記下來。注意在前面的 <tt/0x8:/
        在這個情況中並不重要，我們要的是 <tt/0xf0xxxxxx/。

	<item>當系統重新開機後，執行這道命令：
	  <verb>
% nm /(造成 panic 的 kernel 檔案) | grep f0xxxxxx
          </verb>	  
     其中 <tt/f0xxxxxx/ 就是記下來的 instruction pointer 值。有可能
     不會剛好找到完整的這個字串，這是因為 kernel symbol table 裡的各
     個 symbol 只是函式的進入點，但 instruction pointer 所指的位址有
     可能是在函式內的某一處，而不一定在開頭。所以如果找不到整個字串，
     那麼把 instruction pointer 值的最後一個數字拿掉，再試一次：

	  <verb>
% nm /kernel.that.caused.the.panic | grep f0xxxxx
	  </verb>
     如果這樣也找不到，那就把另一個數字去掉再找，一直重複到找到為止，
     結果是一串可能造成 panic 的函式列表。這樣比直接找到出問題的函式
     來得差，但至少好過什麼都沒有。
      </itemize>

      <p>我常常看到人們顯示一大片 panic 訊息，但很少看到有人花一點時間
      把 instruction pointer 和 kernel symbol table 中的函式比較一下。

      <p>要追蹤出造成 panic 原因的最好方法是先做出 crash dump，然後用
      <tt/gdb(1)/ 在上面做 stack trace。當然，這要靠 -current 中的
      <tt/gdb(1)/ 能運作正常，然而我無法保證這一點。(記得有人說 ELF 的
      <tt/gdb(1)/ 在 kernel 的 crash dump 上不能正常操作，在 3.0 脫離
      BETA 階段時最好有人先檢查這方面的狀況，免得很多人在收到 3.0 光碟
      後脹紅了臉。)

      <p>
      不管是那一種，我通常是用這個方法：

      <itemize>
        <item>寫好 kernel 設定檔。如果你需要用 kernel debugger，在設定
        檔中加上 `options DDB' 這個選項。(當我懷疑有出現無窮迴圈時，通
        常會用這個來設定中斷點。)
        <item>用 <tt/config -g KERNELCONFIG/ 做出用來編譯的目錄
        <item><tt>cd /sys/compile/KERNELCONFIG; make</tt>
        <item>等 kernel 編譯出來
        <item><tt/cp kernel kernel.debug/
        <item><tt/strip -d kernel/
        <item><tt/mv /kernel /kernel.orig/
        <item><tt>cp kernel /</tt>
        <item>重新開機
      </itemize>

      <p> <em>[注意：現在 FreeBSD 3.x kernel 內定是 ELF 格式，所以應該
      用 <tt/strip -g/ 而不是 <tt/strip -d/。如果你的 kernel 因某種原
      因仍是 a.out 格式的話，則用 <tt/strip -aout -d/。]</em>

      <p>注意你<em/不會/真的用包括所有 debug symbol 的 kernel 來開機，
      用 <tt/-g/ 編譯出來的 kernel 大小很容易就超過 10MB。不需要用這麼
      大的 kernel 開機，晚一點 <tt/gdb(1)/ 才會需要它(<tt/gdb(1)/ 會用
      到裡面的 symbol table)。所以我們才會把完整的 kernel 複製一份，接
      著用 <tt/strip -d/ 刪掉 debug symbol，做出第二個 kernel，這個才真
      正拿來開機。

      <p>要確定能抓到 crash dump，先編輯 <tt>/etc/rc.conf</tt>，將
      <tt/dumpdev/ 指到 swap 分割區。這樣 <tt/rc(8)/ 會用 <tt/dumpon(8)/
      來啟動 crash dump，你也可以手動用 <tt/dumpon(8)/。在 panic 之後，
      crash dump 可以用 <tt/savecore(8)/ 存起來；如果 <tt>/etc/rc.conf</tt>
      裡有設 <tt/dumpdev/，那麼重新開機後 <tt/rc(8)/ 會自動執行
      <tt/savecore(8)/ 把 crash dump 存在 <tt>/var/crash</tt>。

      <p>注意：FreeBSD 的 crash dump 通常和機器裡的實際記憶體一樣大，
      就像如果有 64MB 記憶體，crash dump 大小就是 64MB。所以要確定
      <tt>/var/crash</tt> 下有足夠的空間，或是可以手動執行 <tt/savecore(8)/
      把 crash dump 放到另一個空間較夠的目錄下。另一種也許可以限制 crash
      dump 的方法，是在 kernel 設定檔中用 <tt/options MAXMEM=(foo)/，
      將 kernel 可用的記憶體限制在合理的大小。舉例來說，如果你有 128MB
      的記憶體，但是可以限制 kernel 只能用 16MB 的記憶體，這樣 crash
      dump 就是 16MB 而不是 128MB 了。

      <p>一旦發現有了 crash dump，就可以用 <tt/gdb(1)/ 來做 stack trace
      ，如下所示：

      <p>
      <verb>
% gdb -k /sys/compile/KERNELCONFIG/kernel.debug /var/crash/vmcore.0
(gdb) where
      </verb>

      <p>要注意可能會出現好幾個螢幕的可用資訊，你可以用 <tt/script(1)/
      把所有輸出都存起來。用包括所有 debug symbol 的 kernel 來除錯，
      這樣應該可以直接顯示 panic 是發生在那一行。通常是由下往上讀 stack
      strace，這樣才能一個個追蹤出有那些動作引到 crash。也可以用
      <tt/gdb(1)/ 把各種變數或結構的內容印出來，以檢查系統 crash 時的
      實際狀態。

      <p>好啦，如果你有第二台電腦而且有夠瘋狂，可以將 <tt/gdb(1)/ 設定
      成遠端除錯。這樣你可以在一台機器中用 <tt/gdb(1)/ 去除錯另一台裡的
      kernel，可以執行的包括設定中斷點、在 kernel 原始碼中一步步執行等
      等，就像在一般使用者程式上除錯一樣。由於沒有什麼機會為除錯而設置
      兩台並鄰電腦，所以我還沒有這樣玩過。

      <p><em>[Bill 附注：我忘了提到一點：如果你有啟動 DDB 而 kernel 也
      已經進入除錯器，可以在 DDB 命令列下打 `panic'，強迫產生 panic(還
      有 crash dump)。也有可能在 panic 階段時再進入除錯器，如果這樣的話
      ，輸入 `continue'，接著它就會完成 crash dump。 -ed]</em>

    <sect1>
      <heading>dlsym() 對 ELF 執行檔不能用!</heading>

      <p>在 ELF 一系列的工具中，內定是不會讓 dynamic linker 看到執行檔
      裡定義了那些 symbol。所以 <tt>dlsym()</tt> 沒有辦法用藉由呼叫
      <tt>dlopen(NULL, flags)</tt> 取得的 handle，用它去搜尋有那些
      symbol 一定會失敗。

      <p>如果你想要用 <tt>dlsym()</tt> 找出某個 process 的主執行檔中
      有那些 symbol，則要在 link 時對
      <htmlurl url="http://www.FreeBSD.org/cgi/man.cgi?ld"
      name="ELF linker"> 加上 <tt>-export-dynamic</tt> 這個參數。

    <sect1>
      <heading>增加或減少 kernel 能定址的空間</heading>

      <p>系統的內定是，FreeBSD 3.x kernel 能定址到 256 MB，4.x 則是
      1 GB。如果是網路負荷相當重的伺服器(就像大型 FTP 或 HTTP 伺服器)，
      256 MB 可能會不大夠。

      <p>要怎麼增加定址空間呢? 要從兩方面著手。首先告訴 kernel 本身要
      保留較大空間; 其次，既然是在定址空間的最上面載入 kernel，所以還
      要調低載入的位址，否則就會超過定址範圍。

      <p>增加 <tt>src/sys/i386/include/pmap.h</tt> 裡的 <tt/NKPDE/ 值
      便可達成第一個目標。1 GB 的定址空間會像這樣：

      <verb>
#ifndef NKPDE
#ifdef SMP
#define NKPDE                   254     /* addressable number of page tables/pde's */
#else
#define NKPDE                   255     /* addressable number of page tables/pde's */
#endif  /* SMP */
#endif
      </verb>

      <p>要算出 <tt/NKPDE/ 的正確值，將想要的空間大小(以 megabyte 為單
      位)除以 4，接著單 CPU 機器減 1，雙 CPU 則是減 2。

      <p>要解決第二個問題，必須自行算出 kernel 被載入的位址：求出
      0x100100000 減掉定址空間大小的值(以 byte 為單位)，如 1 GB 大小就
      是 0xc0100000。把 <tt>src/sys/i386/conf/Makefile.i386</tt> 裡的
      <tt/LOAD_ADDRESS/ 設成這個值，接著在
      <tt>src/sys/i386/conf/kernel.script</tt> 中，將 section 列表最前面的
      location counter 設成相同的值，如下：

      <verb>
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(btext)
SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/obj/elf/home/src/tmp/usr/i386-unknown-freebsdelf/lib);
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  . = 0xc0100000 + SIZEOF_HEADERS;
  .interp     : { *(.interp)    }
      </verb>

      <p>然後重新 config 和做出新的 kernel。在執行像 <tt/ps(1)/、
      <tt/top(1)/ 這類程式時可能會碰到問題，做一次 <tt/make world/
      應該就可以解決(或把改過的 <tt/pmap.h/ 拷到 <tt>/usr/include/vm/</tt>
      下，再手動編譯 <tt/libkvm/、<tt/ps/ 和 <tt/top/)。

      <p>注意：kernel 所能定址的空間大小必須是 4 megabytes 的倍數。

      <p>[<url url="mailto:dg@FreeBSD.org" name="David Greenman"> 加上
      這一段：<em>我認為 kernel 定址空間大小應該是 2 的乘冪，但不大確
      定這一點。舊的啟動程式會動到 high order address bits，記得它假設
      至少有 256 MB。]</em>

  </sect>

