2017年5月22日 星期一

[Python] "From ... Import "和 Import的差別

Python內的"From ... Import"和import都是使用module的方式。Python內的module指的就是檔案,只不過這個檔案內有許多的宣告和實作。每個module都有自己的function,他們都存在獨立的symbol table內。

不過import方式有許多種,底下就用官方的例子來說明"From ... Import"和 "Import"的不同,底下的code實作兩個function,並存在 fibo.py內

# Fibonacci numbers module

def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while b < n:
        print b,
        a, b = b, a+b

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result


  • 如果使用"Import"來使用module時,會保留獨立的symbol table,所以就不用擔心function name衝突的問題。但碼農們就必須清楚告訴python 直譯器是使用哪一個function

>>> import fibo
>>> fibo.fib(1000) 

此時可以使用dir() 來查看模組名稱
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'fibo']

繼續查詢fibo,可以發現裡面有 fib和 fib2這兩個function
>>> dir(fibo)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'fib', 'fib2']
  • 如果使用"from fibo import fib"來使用module時,會將被匯入的function (fib)加入目前module的symbol table,所以就可能造成名稱衝突的問題,請小心使用。但是好處就是打的字比較少
>>> from fibo import fib, fib2
>>> fib(500)

此時可以使用dir() 來查看模組名稱,可以清楚看到沒有fibo這個名稱
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'fib', 'fib2']


參考資料:https://docs.python.org/2/tutorial/modules.html







[Neural Networks] Keras有內建MNIST資料嗎?

剛安裝完Keras後,很快就會發覺沒有一個sample code附上data set,而安裝資料夾("lib/python2.7/site-packages/keras/")內的檔案大小都只有十幾KB。

仔細看完code後可以猜出來MNIST data set是從網路硬碟下載來的


原始example code:
https://github.com/fchollet/keras/blob/master/examples/mnist_mlp.py



from keras.datasets import mnist
from keras.models import Sequential

繼續往下追可以知道 "lib/python2.7/site-packages/keras/datasets/mnist.py"裡面是從AWS S3網路硬碟下載來的

path = get_file(path, origin='https://s3.amazonaws.com/img-datasets/mnist.npz')


下載完後,馬上將training data和test data取出
    f = np.load(path)
    x_train = f['x_train']
    y_train = f['y_train']
    x_test = f['x_test']

    y_test = f['y_test']

2016年4月22日 星期五

Compound Page, Huge Page, 和Transparent Huge Page(THP)


Compound Page:
指的是記憶體的集合,包含兩個以上的連續記憶體。它會使用meta data來記錄這些記憶體,使用的flag是 __GFP_COMP。目前有一些driver會使用,另外還提供給系統核心使用,比較著名的是 hugetlbfs 和Transparent Huge Pages (THP)。
詳情可以參考 An introduction to compound pages


Huge Page [詳細介紹]:
這是20年前就開始使用的技術 (Pentium Pro),而ARM則等到2004年後在ARMv7才支援。這項技術就是將page size從4KB加大到1MB以上。[wiki]
    1. 處理大檔案時,減少 page fault 為 1/512 倍 。
    2. 同樣大小的TLB (Translate Lookaside Buffer) 可以映射更多的記憶體。Intel 分別設立2組 TLB (Pentium Pro in PAE mode),一組專門紀錄4KB page,另一組專門紀錄2MB page。
    1. 當同一page內的資料過於分散,需要花更多的搜尋時間 
    2. page fault時,載入時間比較久
    3. 因為有兩組不同的 TLB,如果要使用這項硬體功能時,是偷懶地交由user自行決定是否要使用大page,使用的機制就是 hugetlbfs。 
    4. 不能將page swap到硬碟 !!!  (不支援) [lwn]
    5. 最好在boot階段就配置好
    6. VMA (Virtual Memory Area) 也要分成 4KB 和 2MB兩組 !!!

 hugetlbfs:
使用user space介面去配置大塊連續的memory !!! 接著要自己做mmap(),不過簡單的方式是使用libhugetlbfs的HUGETLB_MORECORE=yes變數[lwn]。
  • 壞處:
    1. 在Kernel Virtual Machine底下,如果使用動態配置,可能出錯
    2. 性能不好,所以加入許多功能,搞的像是第二個 tmpfs

替代方案:
目前有兩個方案,但2016年的會議仍無法決定誰能進入Linux Mainline

  • Transparent Huge Page (THP):
每個 process都配置2MB page !!!  但當記憶體不足時會自動分裂成4KB pages
    • 好處:
      1. 會自動將2MB page分裂成 4KB pages
      2. user space programmer不用煩惱不同pages間的差異

    • 壞處:
    1. 還在孵,在2016/04/20時,還沒有實際整合到 Linux mainline
THP的競爭對手
    • 好處:
      1.  已經被google server採用將近一年 (截至2016/04),有實戰經驗


2016年4月19日 星期二

使用man查詢 /etc/networks 檔案的定義

Linux檔案系統的命名方式常常讓人搞不清楚是做什麼用,除了上網查詢外,也可以用內建的man指令查詢

打入底下的指令
$ man 5 networks

就可以查詢到定義,其中數字5代表的是查詢檔案格式,如果還想看其他數字代表的意思,可以打底下的指令查詢
$ man man

不過不要對內建的說明抱持太大的期待,當你看完/etc/networks的定義後,可能還是要乖乖地去網路上看其他資料。


2016年1月6日 星期三

高效率C語言設計

這篇文章只著重在C語言反組譯後的效能,並不探討架構。這是個冷門知識,除了無線通訊的核心程式、高速介面的核心程式外,很少有人會在乎。探討這類的書也不多,通常也很古老,最慘的是部分守則在GCC的進步下,已經沒有用了。本篇文章就一一檢視這些守則。書本用的是"ARM System Developer's Guide - Designing and Optimizing System Software"




  • 局部變數

    • 過時守則 : 32bit CPU上,不要使用char當作區域變數,要改用unsigned int 

int checksum_v1 (int *data) {
        char i;
        int sum = 0;
        for (i=0; i<64; i++) {
                sum +=data[i];
        }   

        return sum;
}

int checksum_v2 (int *data) {
        unsigned int i;
        int sum = 0;
        for (i=0; i<64; i++) {
                sum +=data[i];
        }   

        return sum;
}


理由:
     register寬度為32bit。如果變數是char,編譯器反而要register跟 0xFF 做mask,這樣才能保證不會溢位。且unsigned可以熱

實驗結果: 
      checksum_v1()和checksum_v2() 並沒差,不需要改變。

    • 守則 : 不能把 int加法的值存在short內,即便知道 short不會overflow

short checksum_v3 (int *data) {
        unsigned int i;
        short sum = 0;
        for (i=0; i<64; i++) {
                sum = (short) ( sum +data[i]);
        }   

        return sum;
}

short checksum_v4 (short *data) {
        unsigned int i;
        int sum = 0;
        for (i=0; i <64; i++) {
                sum += *(data ++);
        }
        return (short) sum;
}

理由 :
    compiler會多花幾個arm 指令把值塞入 short內

實驗結果: 
     在checksum_v3() 的迴圈內會多花一個指令 (uxth - zero extend halfword. Extend 16-bit value to 32-bit value)去把short先變成 int。離開迴圈後,再用另一個指令 (sxth - sign extend halfword) 去把 int變為short。

    • 守則 : 使用除法時,盡量使用無符號數

int average_v1 ( int a, int b) {
        return (a+b) /2
}

理由: 
    對負數除二時,需要先加一然後再往右shift一位。ex:  -3/2 = -1 然而 -3 >> 1 = -2,所以需要先加一然後再移位。

實驗結果:
     依然有效

  • C迴圈結構
    • 過時守則 : For Loop內要使用index時,要用遞減方式

int checksum_v5 (int *data) {
        unsigned int i;
        int sum = 0;
        for ( i = 0; i< 64;i++) {
                sum += *(data++);
        }   
        return sum;
}


int checksum_v6 (int *data) {
        unsigned int i;
        int sum = 0;
        for( i=64; i!=0; i--) {
                sum+=*(data++);
        }   
        return sum;
}

理由 : 
    當時的GCC會多花一個指令處理遞增For Loop的index 。

實驗結果 :
    不需使用遞減方式,現在的GCC已經最佳化了,不需要考慮這個。

    • 守則 : 使用 do {} while ()替代 for loop

int checksum_v7 (int *data, unsigned int N) {
        int sum;
        for(; N!=0; N--) {
                sum += *(data++);
        }
        return sum;
}

int checksum_v8 (int *data, unsigned int N) {
        int sum;
        do {
                sum += *(data++);
        } while( --N !=0);
        return sum;
}

理由 : 
    For Loop在第一次迴圈時,會檢查index N是否為 0,但是通常N 不為 0,所以會多花一個arm 指令。 

實驗結果 :
    仍然有效

  • 迴圈展開
    • 守則 : 把迴圈內的內容展開,如此可以減少判斷終止的次數

int checksum_v9 (int *data, unsigned int N ) { 
    int sum = 0;
    do {
        sum += *(data++);
        sum += *(data++);
        sum += *(data++);
        sum += *(data++);
        N -= 4;
    } while (N != 0); 
    return sum;
}


理由 :
     當迴圈的指令數很少時,判斷中止條件的指令就佔很大的比例。將迴圈的內容展開,可以稀釋中止條件所佔的比例。

實驗結果 :
    仍然有效





2015年12月24日 星期四

如何解讀 dynamic section


.dynamic section 提供動態鏈結 (dynamic linking)需要的資訊,其中包含shared library的路徑。
本篇文章參考 oracle dynamic section文件,有興趣的人可以看看。

我會用底下的例子來說明,可以從這下載所需要的.c和.h檔。這些檔案的內容如下

  1. Lib_b.c : 會呼叫 libc內的 printf()
  2. Lib_a.c : 會呼叫 Lib_b.c內的function
  3. main.c : 會呼叫Lib_a.c的function,本身會產生執行檔

  • Overview

先編譯並反組譯這些檔案 (實驗的環境是 arm cortex-a7 32bits、gcc 4.6.3)。首先產生 Lib_a.o 和 Lib_b.o

$ gcc -g -shared -fPIC Lib_a.c -o Lib_a.o
$ gcc -g -shared -fPIC Lib_b.c -o Lib_b.o

接著產生執行檔

$ gcc -g main.c ./Lib_a.o ./Lib_b.o

然後反組譯 Lib_a.o、Lib_b.o、a.out,來產生 objdump.txt

$ objdump -sSdD a.out > objdump.txt

在解讀前,先偷看解答,看看.dynamic 和 .dynstr 會產生什麼結果,先打底下的指令

$ readelf -d a.out > dynamic_section.txt 


 dynamic_section.txt裡頭的東西長得就是這樣。除了前面第4行~第6行的shared library (Lib_a.o、Lib_b.o、libc.so.6)還看得懂外,其他的都不知道。不過沒關係,就一步步的看。
  1 
  2 Dynamic section at offset 0x5ec contains 27 entries:
  3   Tag        Type                         Name/Value
  4  0x00000001 (NEEDED)                     Shared library: [./Lib_a.o]
  5  0x00000001 (NEEDED)                     Shared library: [./Lib_b.o]
  6  0x00000001 (NEEDED)                     Shared library: [libc.so.6]
  7  0x0000000c (INIT)                       0x8444
  8  0x0000000d (FINI)                       0x85c8
  9  0x00000019 (INIT_ARRAY)                 0x105e0
 10  0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 11  0x0000001a (FINI_ARRAY)                 0x105e4
 12  0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 13  0x00000004 (HASH)                       0x8194
 14  0x6ffffef5 (GNU_HASH)                   0x81e4
 15  0x00000005 (STRTAB)                     0x8330
 16  0x00000006 (SYMTAB)                     0x8240
 17  0x0000000a (STRSZ)                      174 (bytes)
 18  0x0000000b (SYMENT)                     16 (bytes)
 19  0x00000015 (DEBUG)                      0x0
 20  0x00000003 (PLTGOT)                     0x106ec
 21  0x00000002 (PLTRELSZ)                   32 (bytes)
 22  0x00000014 (PLTREL)                     REL
 23  0x00000017 (JMPREL)                     0x8424
 24  0x00000011 (REL)                        0x841c
 25  0x00000012 (RELSZ)                      8 (bytes)
 26  0x00000013 (RELENT)                     8 (bytes)
 27  0x6ffffffe (VERNEED)                    0x83fc
 28  0x6fffffff (VERNEEDNUM)                 1
 29  0x6ffffff0 (VERSYM)                     0x83de

 30  0x00000000 (NULL)                       0x0

  • 如何解讀 .dynamic 反組譯內容

.dynamic section 反組譯後的內容如下,這其實是一個 structure table,存放的是 struct Elf32_Dyn,大小是8 bytes。注意,如果cpu是64bits的話,這個struct就是Elf64_Dyn,而大小則變為16bytes。

$ vim objdump.txt
  1 
  2 a.out:     file format elf32-littlearm
...
109 Contents of section .dynamic:
110  105ec 01000000 01000000 01000000 3e000000  ............>...
111  105fc 01000000 48000000 0c000000 44840000  ....H.......D...

112  1060c 0d000000 c8850000 19000000 e0050100  ................


這個struct Elf32_Dyn 可以從 /usr/include/link.h內找到

typedef struct {
        Elf32_Sword d_tag;
        union {
                Elf32_Word      d_val;
                Elf32_Addr      d_ptr;
                Elf32_Off       d_off;
        } d_un;
} Elf32_Dyn;

其中
  • d_tag : 表達編碼過的類型,而類型的定義可以考這個文件,以上面第110行的內容為例。
110  105ec 01000000 01000000
0x01表示的類型是 DT_NEEDED,就是需要載入的shared library。
  • d_du: 依照d_tag的值來決定union是 d_val, d_ptr, 或是 d_off
    • d_val : 表示是值
    • d_ptr : 表示地址
第110行的內容為例,因為d_tag是0x1。從文件的定義。可以知道 d_un現在表示的是d_val。
Name
Value
d_un
Executable
Shared Object
DT_NEEDED
1
d_val
Optional
Optional
所以0x105f0標示的 0x00000001代表的就是 d_val。
整合上面的內容可以把第 110行的內容轉換成下面的 struct
{
    d_tag = 0x1 /* (DT_NEEDED) */
    d_val = 0x1 /* (offset of DT_STRTAB) */
}

  • Type: NEEDED, STRTAB, and STRSZ
這些值可以幫助我們從反組譯中產生底下的內容
  2 Dynamic section at offset 0x5ec contains 27 entries:
  3   Tag        Type                         Name/Value
  4  0x00000001 (NEEDED)                     Shared library: [./Lib_a.o]

讓我們先看看這些的定義
    • NEEDED: 存放的是DT_STRTAB 表格內的 offset

先看反組譯的內容,可以發現 d_val為1,代表在 DT_STRTAB的offset為 1
110  105ec 01000000 01000000


    • STRTAB: 存放字串表格的地址。

從"readelf -r"產生的結果去看DT_STRTAB的值如下
 15  0x00000005 (STRTAB)                     0x8330

反組譯去中可以知道這指到 .dynstr section。裡面存放一堆字串,而且已0x00當作字串結尾。
 43 Contents of section .dynstr:
 44  8330 002e2f4c 69625f61 2e6f005f 5f676d6f  ../Lib_a.o.__gmo
 45  8340 6e5f7374 6172745f 5f005f4a 765f5265  n_start__._Jv_Re
 46  8350 67697374 6572436c 61737365 7300666f  gisterClasses.fo
 47  8360 6f005f69 6e697400 5f66696e 69002e2f  o._init._fini../
 48  8370 4c69625f 622e6f00 6c696263 2e736f2e  Lib_b.o.libc.so.
 49  8380 36006162 6f727400 5f5f6c69 62635f73  6.abort.__libc_s


    • STRSZ : 是STRTAB表格的大小


有了上面的資訊,可以直接去看 .dynamic section的實際內容了

109 Contents of section .dynamic:
110  105ec 01000000 01000000 01000000 3e000000  ............>...

因為這是32位元的機器,可以把0x105ec這一行共4個words解讀成

第一個struct
{
    d_tag = 0x1 (DT_NEEDED)
    d_val = 0x1 (offset of DT_STRTAB)
}

第二個struct
{
    d_tag = 0x1 (DT_NEEDED)
    d_val = 0x33 (offset of DT_STRTAB)
}

先看第一個 struct的 d_val,這個值是 0x1,代表在 .dynstr section的offset 為 0x1。透過查詢可以發現這個字串是"./Lib_a.o" ,請注意尾端是用NULL  (0x00) 當字串結尾。
 43 Contents of section .dynstr:
 44  8330 002e2f4c 69625f61 2e6f005f 5f676d6f  ../Lib_a.o.__gmo
 45  8340 6e5f7374 6172745f 5f005f4a 765f5265  n_start__._Jv_Re
 46  8350 67697374 6572436c 61737365 7300666f  gisterClasses.fo
 47  8360 6f005f69 6e697400 5f66696e 69002e2f  o._init._fini../
 48  8370 4c69625f 622e6f00 6c696263 2e736f2e  Lib_b.o.libc.so.
 49  8380 36006162 6f727400 5f5f6c69 62635f73  6.abort.__libc_s


同樣的,第二個 struct的字串代表的是"./Lib_b.o"
 43 Contents of section .dynstr:
 44  8330 002e2f4c 69625f61 2e6f005f 5f676d6f  ../Lib_a.o.__gmo
 45  8340 6e5f7374 6172745f 5f005f4a 765f5265  n_start__._Jv_Re
 46  8350 67697374 6572436c 61737365 7300666f  gisterClasses.fo
 47  8360 6f005f69 6e697400 5f66696e 69002e2f  o._init._fini../
 48  8370 4c69625f 622e6f00 6c696263 2e736f2e  Lib_b.o.libc.so.
 49  8380 36006162 6f727400 5f5f6c69 62635f73  6.abort.__libc_s


有興趣的人可以參考英文的定義





DT_NEEDED
The DT_STRTAB string table offset of a null-terminated string, giving the name of a needed dependency. The dynamic array can contain multiple entries of this type. The relative order of these entries is significant, though their relation to entries of other types is not. See Shared Object Dependencies.
DT_STRTAB
The address of the string table. Symbol names, dependency names, and other strings required by the runtime linker reside in this table. See String Table Section.
DT_STRSZ
The total size, in bytes, of the DT_STRTAB string table.

  • Type: INIT 和  FINI
  • INIT : 存放 init function
  • FINI : 存放 termination function
當process載入或是移除"每個" shared object時,就會呼叫這兩個function。

再回頭看 .dynamic section的值,可以知道init function和termination地址分別是 0x8444和0x85c8
109 Contents of section .dynamic:
110  105ec 01000000 01000000 01000000 3e000000  ............>...
111  105fc 01000000 48000000 0c000000 44840000  ....H.......D...
112  1060c 0d000000 c8850000 19000000 e0050100  ................

再次透過反組譯確認是否是function
424 Disassembly of section .init:
425 
426 00008444 <_init>:
427     8444:   e92d4008    push    {r3, lr}
428     8448:   eb000020    bl  84d0 <call_gmon_start>
429     844c:   e8bd8008    pop {r3, pc}

560 Disassembly of section .fini:
561 
562 000085c8 <_fini>:
563     85c8:   e92d4008    push    {r3, lr}
564     85cc:   e8bd8008    pop {r3, pc}


有興趣的人可以參考英文的定義

Name
Value
d_un
Executable
Shared Object
DT_INIT
12
d_ptr
Optional
Optional
DT_FINI
13
d_ptr
Optional
Optional





DT_INIT
The address of an initialization function. See Initialization and Termination Sections.
DT_FINI
The address of a termination function. See Initialization and Termination Sections.


  • Type: INIT_ARRAY, INIT_ARRAYSZ, FINI_ARRAY, and FINI_ARRAYSZ
 這四個參數是標記當shared object載入或是卸載時,需要呼叫哪些function。

    • DT_INIT_ARRAY : 紀錄 initialization function表格的地址
    • DT_INIT_ARRAYSZ : 標注這些function地址一共佔據幾個byte
    • DF_FINI_ARRAY : 紀錄termination function表格的地址
    • DT_FINI_ARRAYSZ : 紀錄這些地址一共佔據幾個byte
    • 範例

用下面的例子來看這些數值,其中 __attribute__((constructor)) 的作用是在shared object載入前先呼叫的一的function。反之,__attribute((destructor))是在shared object卸載前先呼叫對應的function。

$ vim main.c

#include    <stdio.h>
                                                                                static __attribute__((constructor)) void foo_1()
{
        (void) printf("initializing: foo_1()\n");
}

static __attribute__((constructor)) void foo_2()
{
        (void) printf("finalizing: foo_2()\n");
}

static __attribute__((destructor)) void bar_1()
{
        (void) printf("finalizing: bar_1()\n");
}

static __attribute__((destructor)) void bar_2()
{
        (void) printf("finalizing: bar_2()\n");
}

void main()
{
        (void) printf("main()\n");
}

接著編譯還有產生反組譯
$ gcc -o main main.c 
$ objdump -sSdD main > objdump.txt

這個程式的執行結果如下
$ ./main
initializing: foo_1()
finalizing: foo_2()
main()
finalizing: bar_2()
finalizing: bar_1()

先看 .dynamic section
$ readelf -d main 

Dynamic section at offset 0x534 contains 25 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x82cc
 0x0000000d (FINI)                       0x84a4
 0x00000019 (INIT_ARRAY)                 0x10518
 0x0000001b (INIT_ARRAYSZ)               12 (bytes)
 0x0000001a (FINI_ARRAY)                 0x10524
 0x0000001c (FINI_ARRAYSZ)               12 (bytes)

然後看看 INIT_ARRAY和FINI_ARRAY的內容
 91 Contents of section .init_array:
 92  10518 98830000 c8830000 e0830000           ............    
 93 Contents of section .fini_array:
 94  10524 7c830000 f8830000 10840000           |...........    

其中 INIT_ARRAY中對應到的 function分別為 frame_dummy(), foo_1()和foo_2()。而佔的記憶體大小為12 bytes,和INIT_ARRAYSZ所標示的一樣

340 00008398 <frame_dummy>:
...
354 000083c8 <foo_1>:
...
362 000083e0 <foo_2>:

相同的FINI_ARRAY記載的function為

331 0000837c <__do_global_dtors_aux>:
...
370 000083f8 <bar_1>:
...
378 00008410 <bar_2>:

可以發現,function是照main.c的位置,依序放入array,如果是destructor,就高位元組先執行。

有興趣的人可看英文定義

Name
Value
d_un
Executable
Shared Object
DT_INIT_ARRAY
25
d_ptr
Optional
Optional
DT_FINI_ARRAY
26
d_ptr
Optional
Optional
DT_INIT_ARRAYSZ
27
d_val
Optional
Optional
DT_FINI_ARRAYSZ
28
d_val
Optional
Optional






DT_INIT_ARRAY
The address of an array of pointers to initialization functions. This element requires that a DT_INIT_ARRAYSZ element also be present. See Initialization and Termination Sections.
DT_FINI_ARRAY
The address of an array of pointers to termination functions. This element requires that a DT_FINI_ARRAYSZ element also be present. See Initialization and Termination Sections.
DT_INIT_ARRAYSZ
The total size, in bytes, of the DT_INIT_ARRAY array.
DT_FINI_ARRAYSZ
The total size, in bytes, of the DT_FINI_ARRAY array.

  • Type: SYMTAB and SYMENT
這些參數是Symbol table的重要值。之後symbol table會提供 .hash section用

    • SYMTAB : 指向 .dynsym section
裡面存放著 struct Elf32_Sym,定義可在 /usr/include/elf.h找到,把struct列出來看看

typedef struct {
        Elf32_Word      st_name;
        Elf32_Addr      st_value;
        Elf32_Word      st_size;
        unsigned char   st_info;
        unsigned char   st_other;
        Elf32_Half      st_shndx;
} Elf32_Sym;

      • st_name : 這是 .dynstr 的 byte offset
      • st_value : 在執行檔或是shared object內表示虛擬地址。詳細內容參考這
      • st_size : symbol size
      • st_info : 標示是否是 strong symbol, weak symbol, ... 。所有的型態可以參考這。另外,關於強弱符號的介紹可以參考這篇中文文章
      • st_other : symbol's visibility 
      • st_shndx : section header table index 



    • SYMENT : 每個 entry的大小
    • 範例
先看這些符號的值

 16  0x00000006 (SYMTAB)                     0x8240
...
 18  0x0000000b (SYMENT)                     16 (bytes)

可以看出 symbol table的地址是0x8240,每個entry 大小是 16 bytes,先列出幾行看看
 27 Contents of section .dynsym:
 28  8240 00000000 00000000 00000000 00000000  ................
 29  8250 0b000000 00000000 00000000 20000000  ............ ...
 30  8260 1a000000 00000000 00000000 20000000  ............ ...

第28行指到 .dynstr的 null
第29行指到 .dynstr的 __gmon_start__,注意這以 0x00 做結尾。且st_info的值為0x2,表示為weak symbol (為初始化的全域變數)
 43 Contents of section .dynstr:
 44  8330 002e2f4c 69625f61 2e6f005f 5f676d6f  ../Lib_a.o.__gmo
 45  8340 6e5f7374 6172745f 5f005f4a 765f5265  n_start__._Jv_Re
 46  8350 67697374 6572436c 61737365 7300666f  gisterClasses.fo
 47  8360 6f005f69 6e697400 5f66696e 69002e2f  o._init._fini../
 48  8370 4c69625f 622e6f00 6c696263 2e736f2e  Lib_b.o.libc.so.
 49  8380 36006162 6f727400 5f5f6c69 62635f73  6.abort.__libc_s
 50  8390 74617274 5f6d6169 6e005f65 64617461  tart_main._edata
 51  83a0 005f5f62 73735f73 74617274 005f5f62  .__bss_start.__b
 52  83b0 73735f73 74617274 5f5f005f 5f627373  ss_start__.__bss
 53  83c0 5f656e64 5f5f005f 5f656e64 5f5f005f  _end__.__end__._
 54  83d0 656e6400 474c4942 435f322e 3400      end.GLIBC_2.4.  

另外可以透過底下指令看symbol table

$ readelf -s a.out 

Symbol table '.dynsym' contains 15 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     3: 00010718     0 NOTYPE  GLOBAL DEFAULT   25 _bss_end__
     4: 00010714     0 NOTYPE  GLOBAL DEFAULT   24 _edata
     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND foo
     6: 00010718     0 NOTYPE  GLOBAL DEFAULT   25 _end
     7: 00010718     0 NOTYPE  GLOBAL DEFAULT   25 __bss_end__
     8: 00010714     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start
     9: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.4 (2)
    10: 00008444     0 FUNC    GLOBAL DEFAULT   12 _init
    11: 00010714     0 NOTYPE  GLOBAL DEFAULT   25 __bss_start__
    12: 000085c8     0 FUNC    GLOBAL DEFAULT   15 _fini
    13: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.4 (2)

    14: 00010718     0 NOTYPE  GLOBAL DEFAULT   25 __end__

定義




DT_SYMTAB
The address of the symbol table. See Symbol Table Section.




DT_SYMENT
            The size, in bytes, of the DT_SYMTAB symbol entry. 


  • Type: HASH and GNU_HASH
HASH指向 .hash section
GNU_HASH指向 .gnu.hash section

 13  0x00000004 (HASH)                       0x8194
 14  0x6ffffef5 (GNU_HASH)                   0x81e4




DT_HASH
The address of the symbol hash table. This table refers to the symbol table indicated by the DT_SYMTAB element. See Hash Table Section.
  • Type: PLTGOT and PLTRELSZ
PLTGOT就是紀錄 GOT的地址。在GCC內,PLT就是程式碼,不是table。而PLTRELSZ記錄著GOT的大小

直接看"readelf -d"的結果,GOT table一共占32 bytes,和反組譯的結果相符

 20  0x00000003 (PLTGOT)                     0x106ec
 21  0x00000002 (PLTRELSZ)                   32 (bytes)

反組譯結果
126 Contents of section .got:
127  106ec ec050100 00000000 00000000 50840000  ............P...
128  106fc 50840000 50840000 50840000 00000000  P...P...P.......


PLTGOT的定義
    An address associated with the procedure linkage table or the global offset table.

PLTRELSZ :
    The total size, in bytes, of the relocation entries associated with the procedure linkage table.
  • Type: PLTREL, JMPREL, REL, RELA, RELSZ, and RELENT
這幾個符號是用來描述GOT內的內容。


    • PLTREL : 用來標示 relocation 方式
    • JMPREL : 存放 .rel.plt section地址。而這個section存放GOT內function的定義
    • REL : 存放 .rel.dyn section地址。而這個section存放GOT內data的定義
    • RELSZ : REL的大小
    • RELENT: REL entry的大小


    • 範例

先從 dynamic section看這些符號的內容。
 22  0x00000014 (PLTREL)                     REL
 23  0x00000017 (JMPREL)                     0x8424
 24  0x00000011 (REL)                        0x841c
 25  0x00000012 (RELSZ)                      8 (bytes)
 26  0x00000013 (RELENT)                     8 (bytes)

      • 解讀 JMPREL

先看 .rel.plt 內第64行的前兩個words
 63 Contents of section .rel.plt:
 64  8424 f8060100 160d0000 fc060100 16010000  ................
 65  8434 00070100 16050000 04070100 16090000  ................


        • 第一個word "0x106f8"代表的是需要relocate 的地址,這個地址為在GOT內,關於GOT的介紹在此
        • 第二個word "0x0d16"中的"16"代表 type R_ARM_JUMP_SLOT。而 "0d"代表symbol table的index。至於 "0d"對應到的值是什麼? ,可以透過下面的指令查詢到為 "__libc_start_main"

$ readelf -s a.out 

Symbol table for image:
  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
...
    2   0: 00000000     0 NOTYPE  WEAK   DEFAULT UND _Jv_RegisterClasses
    5   0: 00000000     0 FUNC    GLOBAL DEFAULT UND foo
    1   0: 00000000     0 NOTYPE  WEAK   DEFAULT UND __gmon_start__
...
   13   1: 00000000     0 FUNC    GLOBAL DEFAULT UND __libc_start_main
...
    3   2: 00010718     0 NOTYPE  GLOBAL DEFAULT  25 _bss_end__

      • 解讀 REL : 使用JMPREL的同樣方式可以解讀為__gmon_start__
 61 Contents of section .rel.dyn:
 62  841c 08070100 15010000 
      • 花了這麼多力氣,其實可以從透過底下的指令看結果
$ readelf -r a.out 

Relocation section '.rel.dyn' at offset 0x41c contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00010708  00000115 R_ARM_GLOB_DAT    00000000   __gmon_start__

Relocation section '.rel.plt' at offset 0x424 contains 4 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
000106f8  00000d16 R_ARM_JUMP_SLOT   00000000   __libc_start_main
000106fc  00000116 R_ARM_JUMP_SLOT   00000000   __gmon_start__
00010700  00000516 R_ARM_JUMP_SLOT   00000000   foo
00010704  00000916 R_ARM_JUMP_SLOT   00000000   abort



定義 :
PTREL :
    Indicates the type of relocation entry to which the procedure linkage table refers, either DT_REL or DT_RELA. All relocations in a procedure linkage table must use the same relocation. This element requires a DT_JMPREL element also be present.

RELA :
    The address of a relocation table.
    An object file can have multiple relocation sections. When creating the relocation table for an executable or shared object file, the link-editor catenates those sections to form a single table. Although the sections can remain independent in the object file, the runtime linker sees a single table. When the runtime linker creates the process image for an executable file or adds a shared object to the process image, the runtime linker reads the relocation table and performs the associated actions.
    This element requires the DT_RELASZ and DT_RELAENT elements also be present. When relocation is mandatory for a file, either DT_RELA or DT_RELcan occur.
REL :
    Similar to DT_RELA, except its table has implicit addends. This element requires that the DT_RELSZ and DT_RELENT elements also be present.

RELSZ :
    The total size, in bytes, of the DT_REL relocation table.

RELENT :
    The size, in bytes, of the DT_REL relocation entry.

JMPREL :
    The address of relocation entries that are associated solely with the procedure linkage table. The separation of these relocation entries enables the runtime linker to ignore these entries when the object is loaded with lazy binding enabled. This element requires the DT_PLTRELSZ and DT_PLTREL elements also be present.