2014/12/12

Symbols of object file

1. Symbol Table


在 ELF file 裡, 其中有些 section 它的 type 是 DYNSYM 或 SYMTAB, 裡面含有symbol table,
我們可以用readelf來dump這些symbols, 這邊以簡單的 hello.c 當範例, compile & link 之後
$ readelf --syms hello
Symbol table '.dynsym' contains 4 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__

Symbol table '.symtab' contains 65 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400238     0 SECTION LOCAL  DEFAULT    1 
     2: 0000000000400254     0 SECTION LOCAL  DEFAULT    2 
     3: 0000000000400274     0 SECTION LOCAL  DEFAULT    3 
     4: 0000000000400298     0 SECTION LOCAL  DEFAULT    4
....

會看到有兩個section有symbol table, 分別是 dynsym 以及 symtab

同樣的事情也可以用nm來做
$ nm hello
0000000000601040 B __bss_start
0000000000601040 b completed.6972
0000000000601030 D __data_start
0000000000601030 W data_start
......

會發現, 用 readelf 讀出來的 symbole item 比較多, 但是 readelf 裡面有不少是空的item, FILE type 的 item 也不會在 nm 裡出現

如果我們將檔案 strip 過, 會發現 symtab 被清掉了
$ strip hello

$ nm hello
nm: hello: no symbols

$ readelf --syms hello

Symbol table '.dynsym' contains 4 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__


2. Symbol type


用 nm dump 出來的 symbol 的 type , 如果是大寫表示global, 小寫表示local

text section

  • T : symbol 位於 text section
data section

  • D : symbol 位於已初始化的 data section
  • G : symbol 位於小物件使用的已初始化 data section. (NOTE: 有些環境用近距離symbol效率較好)
read-only data

  • R : symbol 位於read-only data section
BSS (non-initialize data)

  • B : symbol 位於未初始化 data section (BSS)
  • S : symbol 位於小物件使用的未初始化 data section
Weak  object

  • V : symbol 是 weak object
Weak symbol

  • W : symbol 是未解析 weak object 的 weak symbol
Shared

  • C : common symbol, 未初始化資料
Debug purpose

  • N : debug purpose symbol
  • - : symbol 是 a.out object file 的 stabs symbol (for debug information)
Absolute value

  • A : symbol 是absolute value, linking的時候不會改變
Undefined

  • U : undefined symbol, instance應該在其它object file或是shared library
Indirect reference

  • I : Indirect reference symbol, 是a.out 的 GNU extension feature
Unknown

  • ? : 






2014/12/08

NFC NXP 203 Tag content example

1. Plain text example


底下是用一般的 nfc tag writter 寫進ndef plain text "123456789"的tag content

page 0     0469b055 c22d3580 5a480000 e1101200
page 4     0103a010 440310d1 010c5402 656e3132
page 8     33343536 373839fe 00000000 00000000
page 12    00000000 00000000 00000000 00000000
page 16    00000000 00000000 00000000 00000000
page 20    00000000 00000000 00000000 00000000
page 24    00000000 00000000 00000000 00000000
page 28    00000000 00000000 00000000 00000000
page 32    00000000 00000000 00000000 00000000
page 36    00000000 00000000 00000000 00000000
page 40    00000000 00000000

可以看到它總共有42個page, 根據 NFX 203F 的 format, user data 存在 page 4~39
而page 0~3, 40~41 用於紀錄 ID, checksum, read only, counter

1.1 TLV Blocks


Tag 內容格式為 TLV (Tag, Length, Value)
Tag 的值為底下幾種

  • NULL TLV (0x00) : 用於對齊 memory
  • Lock Control TLV (0x01) : 對 lock bit 做進一步描述
  • Memory Control TLV (0x02) : 定義剩下的memory
  • NDEF Message TLV (0x03)
  • Proprietary TLV (0xFD)
  • Terminator TLV (0xFE) : 最後一個TLV
上面的例子裡, 可以看到3個TLV
TLV 1: (Lock Control)
01 03 a01044

TLV 2: (NDEF Message)
03 10 d1010c5402656e313233343536373839

TLV 3: (Terminator)
fe


1.2 NDEF Message


在 NDEF TLV 的 VALUE 為 NDEF message
d1010c5402656e313233343536373839

第一個byte 0xd1為 NDEF Flag + TNF

  • MB = 1
  • ME = 1
  • CF = 0
  • SR = 1
  • IL = 0
  • TNF = 1

接下來的 1 個byte是 Type Length
Type Length = 0x01

因為 SR (Short Record) 為 1, 所以接下來的Payload length 長度為1個byte
Payload Length = 0x0c

因為 IL (ID Length is present) 為0, 所以沒有 ID 相關的欄位

接著是1個 byte 的 Payload Type
Payload Type = 0x54 (WELL-KNOWN: urn:nfc:wkt:T (Text))

最後是NDEF Text content:
02 65 6e 31 32 33 34 35 36 37 38 39
   E  N  1  2  3  4  5  6  7  8  9

第一個byte 0x02為Status

  • bit 7 : 0表示UTF-8 encoded, 1表示UTF16 encoded
  • bit 6 : RFU (must be set to zero)
  • bit 5..0 : IANA language code的長度
可以看到它用UTF-8, IANA language code的長度為2
所以接著2個byte為 0x65, 0x6e 為 "EN" 是它的 IANA language code
接著是我們的message "123456789"













2014/11/25

objdump

1. Use objdump dump ELF binary


如果想 dump 每個 section 的 binary, 可以用objdump

$ objdump --full-contents /bin/ls
Contents of section .interp:
 400238 2f6c6962 36342f6c 642d6c69 6e75782d  /lib64/ld-linux-
 400248 7838362d 36342e73 6f2e3200           x86-64.so.2.
Contents of section .note.ABI-tag:
 400254 04000000 10000000 01000000 474e5500  ............GNU.
 400264 00000000 02000000 06000000 18000000  ................
Contents of section .note.gnu.build-id:
 400274 04000000 14000000 03000000 474e5500  ............GNU.
 400284 64d095bc 6589dd4b fbf1c6d6 2ae98538  d...e..K....*..8
 400294 5965461b                             YeF.
Contents of section .gnu.hash:
 400298 03000000 72000000 02000000 07000000  ....r...........
 4002a8 a201400c 12010c3f 28440003 a8040000  ..@....?(D......
 4002b8 72000000 75000000 7e000000 281d8c1c  r...u...~...(...
 4002c8 4245d5ec bbe3927c bc50769e 86f0967c  BE.....|.Pv....|
 4002d8 96a08997 3cad390d d871581c ce2c6372  ....<.9..qX..,cr
 4002e8 e46241f5 b88df10e 39f28b1c 32c4f712  .bA.....9...2...
 4002f8 ead3ef0e b3a2f712                    ........

但示通常會印出一堆, 所以通常會看特定的section
可以用 objdump 找 section
$ objdump --section-headers /bin/ls

/bin/ls:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000400238  0000000000400238  00000238  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  0000000000400254  0000000000400254  00000254  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000400274  0000000000400274  00000274  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .gnu.hash     00000068  0000000000400298  0000000000400298  00000298  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynsym       00000c18  0000000000400300  0000000000400300  00000300  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynstr       00000593  0000000000400f18  0000000000400f18  00000f18  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version  00000102  00000000004014ac  00000000004014ac  000014ac  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version_r 00000090  00000000004015b0  00000000004015b0  000015b0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rela.dyn     000000a8  0000000000401640  0000000000401640  00001640  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.plt     00000a80  00000000004016e8  00000000004016e8  000016e8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .init         0000001a  0000000000402168  0000000000402168  00002168  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .plt          00000710  0000000000402190  0000000000402190  00002190  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .text         0000f65a  00000000004028a0  00000000004028a0  000028a0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
......

找到想要印的section
$ objdump --full-contents --section .interp /bin/ls

/bin/ls:     file format elf64-x86-64

Contents of section .interp:
 400238 2f6c6962 36342f6c 642d6c69 6e75782d  /lib64/ld-linux-
 400248 7838362d 36342e73 6f2e3200           x86-64.so.2.

也可以指定範圍, 比如說 .interp 從 0x400238 到 0x400253
$ objdump --full-contents --start-address=0x400238 --stop-address=0x400253 /bin/ls

/bin/ls:     file format elf64-x86-64

Contents of section .interp:
 400238 2f6c6962 36342f6c 642d6c69 6e75782d  /lib64/ld-linux-
 400248 7838362d 36342e73 6f2e32             x86-64.so.2

如果指定的範圍超過 section 邊界的話, 它會將跨過的 section 名字也印出來
$ objdump --full-contents --start-address=0x400238 --stop-address=0x400280 /bin/ls

/bin/ls:     file format elf64-x86-64

Contents of section .interp:
 400238 2f6c6962 36342f6c 642d6c69 6e75782d  /lib64/ld-linux-
 400248 7838362d 36342e73 6f2e3200           x86-64.so.2.
Contents of section .note.ABI-tag:
 400254 04000000 10000000 01000000 474e5500  ............GNU.
 400264 00000000 02000000 06000000 18000000  ................
Contents of section .note.gnu.build-id:
 400274 04000000 14000000 03000000           ............

如果只想把 ELF 檔案當 binary 來印, 或是想印某些純 binary 檔
$ objdump --full-contents -b binary /bin/ls


2. Use objdump to disassemble file


假如有個 "hello.c", 內容如下:
// hello.c
#include <stdio.h>

int main() {
        printf("Hello World\n");
        return 0;
}

然後 compile : "gcc -c -g hello.c", "gcc -o hello hello.o"

可以用 objdump 反組譯
$ objdump -d hello.o

hello.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   bf 00 00 00 00          mov    $0x0,%edi
   9:   e8 00 00 00 00          callq  e <main+0xe>
   e:   b8 00 00 00 00          mov    $0x0,%eax
  13:   5d                      pop    %rbp
  14:   c3                      retq

使用 -d (--disassemble) 反組譯, 通常只會包含執行碼的 section (.text之類的)
如果想反組譯所有 section, 可以使用 -D ( --disassemble-all ), 但是會把不是程式碼的部份也當成程式碼反組譯

反組譯的輸出格式是
address <symbol>
    address:    code byte sequence        disassemble result

如果不想看到 code byte sequence
$ objdump -d --no-show-raw-insn hello.o

hello.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:   push   %rbp
   1:   mov    %rsp,%rbp
   4:   mov    $0x0,%edi
   9:   callq  e <main+0xe>
   e:   mov    $0x0,%eax
  13:   pop    %rbp
  14:   retq


如果想看到逐行加上 symbol 的位置
$ objdump -d --prefix-address hello.o

hello.o:     file format elf64-x86-64


Disassembly of section .text:
0000000000000000 <main> push   %rbp
0000000000000001 <main+0x1> mov    %rsp,%rbp
0000000000000004 <main+0x4> mov    $0x0,%edi
0000000000000009 <main+0x9> callq  000000000000000e <main+0xe>
000000000000000e <main+0xe> mov    $0x0,%eax
0000000000000013 <main+0x13> pop    %rbp
0000000000000014 <main+0x14> retq

此時 code byte sequence 就會被忽略, 如果想加回code byte sequence
$ objdump -d --prefix-address --show-raw-insn hello.o

hello.o:     file format elf64-x86-64


Disassembly of section .text:
0000000000000000 <main> 55                      push   %rbp
0000000000000001 <main+0x1> 48 89 e5                    mov    %rsp,%rbp
0000000000000004 <main+0x4> bf 00 00 00 00              mov    $0x0,%edi
0000000000000009 <main+0x9> e8 00 00 00 00              callq  000000000000000e <main+0xe>
000000000000000e <main+0xe> b8 00 00 00 00              mov    $0x0,%eax
0000000000000013 <main+0x13> 5d                         pop    %rbp
0000000000000014 <main+0x14> c3                         retq


如果只想反組譯特定的 section
$ objdump -d --section .init hello

hello:     file format elf64-x86-64


Disassembly of section .init:

00000000004003e0 <_init>:
  4003e0:       48 83 ec 08             sub    $0x8,%rsp
  4003e4:       48 8b 05 0d 0c 20 00    mov    0x200c0d(%rip),%rax        # 600ff8 <_DYNAMIC+0x1d0>
  4003eb:       48 85 c0                test   %rax,%rax
  4003ee:       74 05                   je     4003f5 <_init+0x15>
  4003f0:       e8 3b 00 00 00          callq  400430 <__gmon_start__@plt>
  4003f5:       48 83 c4 08             add    $0x8,%rsp
  4003f9:       c3                      retq

反組譯的部份跟之前一樣, 也可以設定 --start-address 以及 --stop-address

如果 object file 包含 debug information, 加上 -l (--line-numbers) 可以把指令與原始碼行號對應資訊印出來, 但如果 object file 沒有 debug information 就沒有任何效果
$ objdump -d -l hello.o

hello.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
main():
/home/william/temp/hello.c:3
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
/home/william/temp/hello.c:4
   4:   bf 00 00 00 00          mov    $0x0,%edi
   9:   e8 00 00 00 00          callq  e <main+0xe>
/home/william/temp/hello.c:5
   e:   b8 00 00 00 00          mov    $0x0,%eax
/home/william/temp/hello.c:6
  13:   5d                      pop    %rbp
  14:   c3                      retq


如果加上 -S (--source), 如果找的到原始檔就會顯示對應的原始碼, 但如果 object file 沒有 debug information 就沒有作用

$ objdump -d -S hello.o

hello.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
#include <stdio.h>

int main() {
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
        printf("Hello World\n");
   4:   bf 00 00 00 00          mov    $0x0,%edi
   9:   e8 00 00 00 00          callq  e <main+0xe>
        return 0;
   e:   b8 00 00 00 00          mov    $0x0,%eax
}
  13:   5d                      pop    %rbp
  14:   c3                      retq

-S 與 -l 可以同時使用
其中可以注意的是, 在 printf 下面的 address 4, bf 00 00 00 00, 後面的位置都是00, 是因為檔案還沒 reallocate, 在 linking 之後才會嵌入位置
$ objdump -S hello

......
000000000040052d <main>:
  40052d:       55                      push   %rbp
  40052e:       48 89 e5                mov    %rsp,%rbp
  400531:       bf d4 05 40 00          mov    $0x4005d4,%edi
  400536:       e8 d5 fe ff ff          callq  400410 <puts@plt>
  40053b:       b8 00 00 00 00          mov    $0x0,%eax
  400540:       5d                      pop    %rbp
  400541:       c3                      retq
  400542:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  400549:       00 00 00
  40054c:       0f 1f 40 00             nopl   0x0(%rax)
......

可以看到執行檔裡已經嵌入位置 bf d4 05 40 00



















FreeRTOS Interrupt Management

1. Introduction


關於處理event, 有幾個要考慮的點

  • 要怎麼偵測event? 通常會用 interrupt, 但也可以用polling的方式
  • 如果使用 interrupt, 要在 ISR (Interrupt service routine) 裡面處裡多少事情? 相對地要在ISR外面處理多少事情? 是否 ISR 裡面的事情要愈少愈好?
  • ISR 裡的 code 要如何跟外面的 code 溝通? 尤其是 asynchronous 的code
FreeRTOS 沒有規定 event handle要怎麼實作, 但提供了一些interrupt API
在這些API裡, 只有 FromISR 結尾的 function 才可以在 ISR 裡呼叫, 以及以 FROM_ISR 結尾的 macro 可以在ISR裡使用

2. Binary Semaphores used for Synchronization


整個概念如下圖, 當 interrupt 發生時, ISR 解開 semaphore 並讓 handler task 變成 ready state, 然後 ISR return, 這時 handler task 有最高的 priority 並執行, 當它執行完, 等下一個 semaphore, 進入block state, 並讓低 priority 的 task 執行



在使用 semaphore 時, 分成 take & give, 實作上, 它就像是長度為1的queue, 它一樣要設定 block time, 相關的限制也相同

2.1 Create Semaphore


void vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore );

xSemaphoreHandle 是 semaphore 的型態, 在使用它之前必需先 create

2.2 Take Semaphore


portBASE_TYPE xSemaphoreTake( xSemaphoreHandle xSemaphore, portTickType xTicksToWait );

如果把 xTicksToWait 設成 portMAX_DELAY, 表示要永遠等下去

xSemaphoreTake 不能在 ISR 裡面使用

可能的回傳值 :
  • pdPASS : 成功拿到 semaphore, 如果 block time > 0, 表示在 timeout 前拿到 semaphore
  • pdFALSE : 沒能在 block time timeout 前拿到 semaphore

2.3 Give Semaphore in ISR


portBASE_TYPE xSemaphoreGiveFromISR( xSemaphoreHandle xSemaphore,
                                     portBASE_TYPE *pxHigherPriorityTaskWoken
                                     );

如果有 high priority task 在等這個 semaphore, 然後呼叫 xSemaphoreGiveFromISR 造成目前的 task 被 scheduler switch out, 那麼會把 pxHigherPriorityTaskWoken 這個 pointer 裡面的值設成 pdTRUE, 表示要處裡目前要被 switch out 的 task 的 context switch

可能的回傳值 :

  • pdPASS : 成功
  • pdFALSE : 如果目前的 semaphore 已經被 give 了, 會回傳失敗

2.4 sample code


static void vPeriodicTask( void *pvParameters )
{
    for( ;; )
    {
        /* 等 500ms 準備送出 sofeware interrupt */
        vTaskDelay( 500 / portTICK_RATE_MS );

        /* 送出 interrupt, 前後夾 log */
        vPrintString( "Periodic task - About to generate an interrupt.\r\n" );
        __asm{ int 0x82 } /* This line generates the interrupt. */
        vPrintString( "Periodic task - Interrupt generated.\r\n\r\n\r\n" );
    }
}

static void vHandlerTask( void *pvParameters )
{
    /* As per most tasks, this task is implemented within an infinite loop. */
    for( ;; )
    {
        /* take semaphore */
        xSemaphoreTake( xBinarySemaphore, portMAX_DELAY );
        vPrintString( "Handler task - Processing event.\r\n" );
    }
}

static void __interrupt __far vExampleInterruptHandler( void )
{
    static portBASE_TYPE xHigherPriorityTaskWoken;

 /* 先設成 false, 這樣如果有 task 被switch out時會設成ture, 才能分辨 */
    xHigherPriorityTaskWoken = pdFALSE;

    /* give semaphore */
    xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );
    if( xHigherPriorityTaskWoken == pdTRUE )
    {
        /* 做 context switch, 其中 portSWITCH_CONTEXT() 是 Open Watcom Dos 的port,
           其它平台可能有不同的 port */
        portSWITCH_CONTEXT();
    }
}

int main( void )
{
    /* 建 semaphore */
    vSemaphoreCreateBinary( xBinarySemaphore );

    /* 設定 interrupt handler */
    _dos_setvect( 0x82, vExampleInterruptHandler );

    if( xBinarySemaphore != NULL )
    {
        /* 建立要take semaphore 的 task, 它的 priority 是 3 */
        xTaskCreate( vHandlerTask, "Handler", 1000, NULL, 3, NULL );

        /* 建立定期發出 software interrupt 的 task */
        xTaskCreate( vPeriodicTask, "Periodic", 1000, NULL, 1, NULL );

        vTaskStartScheduler();
    }
    /* it should never reach here */
    for( ;; );
}

3. Counting semaphores


使用 binary semaphore 的缺點是, 在 ISR unblock binary semaphore之後, 並且讓handle task執行時, 如果這時又再有一個 interrupt, 這時候 handle task也只會做最初的那次, counting semaphore 可以解決這種情況

可以將 counting semaphore 想成是長度為n的queue, 只是我們不在乎裡面的data

通常 counting semaphore 用在兩個用途: 記錄次數, 與管理資源

底下是它的 api

xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount,
                                           unsigned portBASE_TYPE uxInitialCount );

其中 uxMaxCount 是最大值, uxInitialCount是初始值, 成功的話回傳 semaphore handle


4. Use queue in ISR


在 ISR 裡使用 queue, 有相對應的 function:

portBASE_TYPE xQueueSendToFrontFromISR( xQueueHandle xQueue,
                                        void *pvItemToQueue
                                        portBASE_TYPE *pxHigherPriorityTaskWoken
                                        );

portBASE_TYPE xQueueSendToBackFromISR( xQueueHandle xQueue,
                                       void *pvItemToQueue
                                       portBASE_TYPE *pxHigherPriorityTaskWoken
                                       );

BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,
                                 void *pvBuffer,
                                 BaseType_t *pxHigherPriorityTaskWoken
                                 );

這些其實就是 xQueueSendToFront(), xQueueSendToBack() 以及 xQueueReceive()

4.1 Sample code


static void vIntegerGenerator( void *pvParameters )
{
    portTickType xLastExecutionTime;
    unsigned portLONG ulValueToSend = 0;
    int i;

    /* Initialize the variable used by the call to vTaskDelayUntil(). */
    xLastExecutionTime = xTaskGetTickCount();
    for( ;; )
    {
        /* 每 200ms block */
        vTaskDelayUntil( &xLastExecutionTime, 200 / portTICK_RATE_MS );

        /* 寫入queue 5次, 這些值將從ISR讀出 */
        for( i = 0; i < 5; i++ )
        {
            xQueueSendToBack( xIntegerQueue, &ulValueToSend, 0 );
            ulValueToSend++;
        }
        /* 觸發 software interrupt 0x82 */
        vPrintString( "Generator task - About to generate an interrupt.\r\n" );
        __asm{ int 0x82 } /* This line generates the interrupt. */
        vPrintString( "Generator task - Interrupt generated.\r\n\r\n\r\n" );
    }
}

static void __interrupt __far vExampleInterruptHandler( void )
{
    static portBASE_TYPE xHigherPriorityTaskWoken;
    static unsigned long ulReceivedNumber;

    /* 宣告成 static const 確保它們不是 ISR 產生的 */
    static const char *pcStrings[] = {"St0\r\n", "St1\r\n", "St2\r\n", "St3\r\n"};
    xHigherPriorityTaskWoken = pdFALSE;

    /* Loop until the queue is empty. */
    while( xQueueReceiveFromISR( xIntegerQueue, &ulReceivedNumber, &xHigherPriorityTaskWoken ) != errQUEUE_EMPTY )
    {
        /* Mask最後兩個bit, 然後把對應的String放進queue裡 */
        ulReceivedNumber &= 0x03;
        xQueueSendToBackFromISR( xStringQueue, &pcStrings[ ulReceivedNumber ], &xHigherPriorityTaskWoken );
    }

    /* 如果寫入 queue 造成 high priority task 變成 ready, 就要做context switch */
    if( xHigherPriorityTaskWoken == pdTRUE )
    {
        /* NOTE: 這個context switch 是給 Open Watcom DOS port 使用, 其它平台有不同的port */
        portSWITCH_CONTEXT();
    }
}

static void vStringPrinter( void *pvParameters )
{
    char *pcString;
    for( ;; )
    {
        /* Block on the queue to wait for data to arrive. */
        xQueueReceive( xStringQueue, &pcString, portMAX_DELAY );
        vPrintString( pcString );
    }
}

int main( void )
{
    /* 建queue */
    xIntegerQueue = xQueueCreate( 10, sizeof( unsigned long ) );
    xStringQueue = xQueueCreate( 10, sizeof( char * ) );

    /* 設定 interrupt handler. */
    _dos_setvect( 0x82, vExampleInterruptHandler );

    /* low priority period task */
    xTaskCreate( vIntegerGenerator, "IntGen", 1000, NULL, 1, NULL );

    /* High priority interrupt handler */
    xTaskCreate( vStringPrinter, "String", 1000, NULL, 2, NULL );

    vTaskStartScheduler();
    for( ;; );
}







I2S Introduction

1. Introduction


I2S (Inter-IC Sound), 用於IC之間傳遞數位音源, 它由三條單方向線組成

  1. SCK (Serial Clock): serial transmission的clock line
  2. SD (Serial Data): serial transmission的data line
  3. WS (Word Select): 即 udio channel

在傳輸時, 有一方是Master, 取決於誰輸出SCK

Example 1: 這張圖中, Transmitter 輸出SCK, 所以 Transmitter 是 master


Example 2: 另種情況是, 雖然 receiver 從 tranmitter 的 SD 收到data, 但 receiver 輸出 SCK, 所以 receiver 是 master

Example 3: 有時候設計上會多出一個 controller, 它負責控制 SCK 以及WS, 這種情況下 controller 是 master

有時候在設計上, 為了達到master與slave角色互換, 可以利用修改pin function

在邏輯電壓準位上, 使用 TTL standard, Output Level 在 0V~0.8V為Lo, 2.4~5.0V為Hi

2 line description


2.1 Serial Data


傳輸格式為 two's complement, 並且先傳 MSB (Most Significant Bit)
使用MSB是因為如果 transmitter 與 receiver 兩邊的 word length 不同, 那麼就不需要考慮到調整word length的問題
例如, transmitter word length = 20 bits receiver word length = 24 bits, 那麼 transmitter 傳完之後, receiver 自動補上 4 個 0 就可以
另個例子是transmitter word length = 20 bits receiver word length = 16 bits, 那麼 receiver收完 16 bits 之後, 自動捨棄後面的 4 個 bits 即可


transmitter 在傳 SD 時, 它的 clock signal 在做synchronization可以是 trailing edge (high-to-low) 或者是 leading edge (low-to-high), 不管是哪一種, receiver 在 leading edge 時都得在 SD 上加 latch

2.2 Word Select


WS 輸出 0 表示 Channel 1 (Left), 輸出1表示 Channel 2 (Right)
WS在轉變0或1時, leading edge的SD是WS未轉變之前的資訊, 確定WS轉變後, 下一個leading edge 的 SD才是 WS 指定的 channel info
參考上一張圖, WS 原本值是 1 (Right Channel), 改成0之後, SD的data在1個clock period之間仍然是 Right Channel, 等到下個clock leading edge才變成 Left Channel






2014/10/20

static library and shared library

1. Static library


static library 把許多 object 檔包成同一個檔案, 用 ar 指令打包
在 link 的時候, 會在 library 裡找到對應的 object 副本, 再作 link 的動作
它以 object 檔為單位, link 的時候會放一份副本
也因此如果有多個執行檔都用到同個 static library, 每個執行檔都會有一份副本
使用的記憶體也因為每個執行檔都有一份, 所以也使用較多的記憶體
在更新 static library 時, 也得要將所有用到這份 static library 的執行檔都重新 link

2. Shared library


shared library 透過 mmap 的機制讓多個 process 共享記憶體, 並參考其中內容
一樣是將多個 object 打包, OS 會參考 memory mapping table , 等到實際取用的時候才去讀取資料

因為多個執行檔只要一份, 所以較省空間與記憶體
更新 shared library 也只需要更新一次

製作 shared library:
$ gcc -c foo.c
$ gcc -c bar.c
$ gcc -shared -W1,-soname,libfoo.so.o -o libfoo.so foo.o bar.o
-shared 表示要建立 shared object
-soname 表示 shared object soname, (別名)

使用的方式:
$ gcc -o baz baz.o -lfoo

3. Dependency of shared library


ELF 的 dynamic section 的 NEEDED 裡會儲存檔案用到哪些 shared library, 可以用objdump來看
$ objdump -p /bin/ls

/bin/ls:     file format elf32-littlearm

Program Header:
......

Dynamic Section:
  NEEDED               libselinux.so.1
  NEEDED               librt.so.1
  NEEDED               libacl.so.1
  NEEDED               libgcc_s.so.1
  NEEDED               libc.so.6
  NEEDED               ld-linux-armhf.so.3
  INIT                 0x00009bb8

或是用readelf
$ readelf -d /bin/ls

Dynamic section at offset 0x16ee8 contains 30 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libselinux.so.1]
 0x00000001 (NEEDED)                     Shared library: [librt.so.1]
 0x00000001 (NEEDED)                     Shared library: [libacl.so.1]
 0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x00000001 (NEEDED)                     Shared library: [ld-linux-armhf.so.3]
 0x0000000c (INIT)                       0x9bb8

NEEDED 記錄的是 soname, 為了要找到 soname 對應的實際的檔案, 底下是可能的找法

  • 它會先從 /usr/lib 找 soname的檔案
  • 如果環境變數 LD_LIBRARY_PAT 有設定, 就會在設定的路徑下找
  • 如果 /etc/ld.so.cache 有記錄相關資訊的話, 也會使用它的內容

一個一個找其實很麻煩, 通常會使用 ldd 來檢查
$ ldd /bin/ls
        /usr/lib/arm-linux-gnueabihf/libcofi_rpi.so (0xb6f10000)
        libselinux.so.1 => /lib/arm-linux-gnueabihf/libselinux.so.1 (0xb6edf000)
        librt.so.1 => /lib/arm-linux-gnueabihf/librt.so.1 (0xb6ed0000)
        libacl.so.1 => /lib/arm-linux-gnueabihf/libacl.so.1 (0xb6ec1000)
        libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xb6e99000)
        libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6d69000)
        /lib/ld-linux-armhf.so.3 (0xb6f1e000)
        libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0xb6d5e000)
        libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6d3f000)
        libattr.so.1 => /lib/arm-linux-gnueabihf/libattr.so.1 (0xb6d32000)

它會顯示出所有需要的 shared library, soname, 以及實際的位置











2014/10/13

ELF (Executable and Linking Format)

1. Abstract


ELF (Executable and Linking Format), 是 executable binary file 以及 object file 的檔案格式規範

ELF 的檔案開頭會有 (1) ELF header, (2) program header table, (3) section header table

ELF 相關的 define 在 "/usr/include/elf.h", 裡面有分 32-bit 及 64-bit, 以下都以32-bit當例子

2. ELF header


查看 ELF header 的內容, 可以用 readelf
$ readelf -h /bin/ls
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0xc3f0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          95220 (bytes into file)
  Flags:                             0x5000002, has entry point, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         28
  Section header string table index: 27

其實這只是把 /bin/ls 的 binary 的開頭部份以 Elf32_Ehdr 來 parse
Elf32_Ehdr 在 "/usr/include/elf.h" 的定義為
typedef struct
{
  unsigned char e_ident[EI_NIDENT];     /* Magic number and other info */
  Elf32_Half    e_type;                 /* Object file type */
  Elf32_Half    e_machine;              /* Architecture */
  Elf32_Word    e_version;              /* Object file version */
  Elf32_Addr    e_entry;                /* Entry point virtual address */
  Elf32_Off     e_phoff;                /* Program header table file offset */
  Elf32_Off     e_shoff;                /* Section header table file offset */
  Elf32_Word    e_flags;                /* Processor-specific flags */
  Elf32_Half    e_ehsize;               /* ELF header size in bytes */
  Elf32_Half    e_phentsize;            /* Program header table entry size */
  Elf32_Half    e_phnum;                /* Program header table entry count */
  Elf32_Half    e_shentsize;            /* Section header table entry size */
  Elf32_Half    e_shnum;                /* Section header table entry count */
  Elf32_Half    e_shstrndx;             /* Section header string table index */
} Elf32_Ehdr;

那麼把 /bin/ls 的開頭 dump 出來就可以對應到以上的欄位
$ hexdump -n 64 -C /bin/ls
00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 28 00 01 00 00 00  f0 c3 00 00 34 00 00 00  |..(.........4...|
00000020  f4 73 01 00 02 00 00 05  34 00 20 00 09 00 28 00  |.s......4. ...(.|
00000030  1c 00 1b 00 01 00 00 70  c0 60 01 00 c0 e0 01 00  |.......p.`......|

其中 Elf32_Ehdr 實際對應的值是
e_ident:     7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00
e_type:      02 00       = 0x0002
e_machine:   28 00       = 0x0028
e_version:   01 00 00 00 = 0x00000001
e_entry:     f0 c3 00 00 = 0x0000c3f0
e_phoff:     34 00 00 00 = 0x00000034 = 52 (bytes)
e_shoff:     f4 73 01 00 = 0x000173f4 = 95220 (bytes)
e_flags:     02 00 00 05
e_ehsize:    34 00       = 0x0034     = 52 (bytes)
e_phentsize: 20 00       = 0x0020     = 32 (bytes)
e_phnum:     09 00       = 0x0009
e_shentsize: 28 00       = 0x0028
e_shnum:     1c 00       = 0x001c
e_shstrndx:  1b 00       = 0x001b

其中 e_ident 包含 ELF magic number 和其它資訊, ELF 檔案的前 4 個byte一定是 "0x7f 45 4c 46"

e_type 則是底下幾種:

  • ET_REL (1) : relocatable file
  • ET_EXEC (2) : executable file
  • ET_DYN (3) : shared object file
  • ET_CORE (4) : core file

3. Program Header


program header table的位置從 ELF header 的 "Start of program headers" (e_phoff) 開始
program header 的大小則是由 "Size of program headers" (e_phentsize) 以及 "Number of program headers" (e_phnum) 決定

"/bin/ls" 的例子裡, program header 從offset 52 bytes 開始, header size是 32bytes, 共有 9 個header

用 readelf 來解讀:
$ readelf --program-headers /bin/ls

Elf file type is EXEC (Executable file)
Entry point 0xc3f0
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  EXIDX          0x0160c0 0x0001e0c0 0x0001e0c0 0x00028 0x00028 R   0x4
  PHDR           0x000034 0x00008034 0x00008034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x00008154 0x00008154 0x00019 0x00019 R   0x1
      [Requesting program interpreter: /lib/ld-linux-armhf.so.3]
  LOAD           0x000000 0x00008000 0x00008000 0x160ec 0x160ec R E 0x8000
  LOAD           0x016edc 0x00026edc 0x00026edc 0x003f8 0x0108c RW  0x8000
  DYNAMIC        0x016ee8 0x00026ee8 0x00026ee8 0x00118 0x00118 RW  0x4
  NOTE           0x000170 0x00008170 0x00008170 0x00044 0x00044 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4
  GNU_RELRO      0x016edc 0x00026edc 0x00026edc 0x00124 0x00124 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     .ARM.exidx
   01
   02     .interp
   03     .interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .ARM.exidx .eh_frame
   04     .init_array .fini_array .jcr .dynamic .got .data .bss
   05     .dynamic
   06     .note.ABI-tag .note.gnu.build-id
   07
   08     .init_array .fini_array .jcr .dynamic

其中 program header 對應到 "/usr/include/elf.h"
typedef struct
{
  Elf32_Word    p_type;                 /* Segment type */
  Elf32_Off     p_offset;               /* Segment file offset */
  Elf32_Addr    p_vaddr;                /* Segment virtual address */
  Elf32_Addr    p_paddr;                /* Segment physical address */
  Elf32_Word    p_filesz;               /* Segment size in file */
  Elf32_Word    p_memsz;                /* Segment size in memory */
  Elf32_Word    p_flags;                /* Segment flags */
  Elf32_Word    p_align;                /* Segment alignment */
} Elf32_Phdr;

一樣可以用 hexdump 來倒出對應的值, 其中參數 288 = 32 bytes * 9 header, offset 是 52 bytes
$ hexdump -n 288 -s 52 -C /bin/ls
00000034  01 00 00 70 c0 60 01 00  c0 e0 01 00 c0 e0 01 00  |...p.`..........|
00000044  28 00 00 00 28 00 00 00  04 00 00 00 04 00 00 00  |(...(...........|
00000054  06 00 00 00 34 00 00 00  34 80 00 00 34 80 00 00  |....4...4...4...|
00000064  20 01 00 00 20 01 00 00  05 00 00 00 04 00 00 00  | ... ...........|
00000074  03 00 00 00 54 01 00 00  54 81 00 00 54 81 00 00  |....T...T...T...|
00000084  19 00 00 00 19 00 00 00  04 00 00 00 01 00 00 00  |................|
00000094  01 00 00 00 00 00 00 00  00 80 00 00 00 80 00 00  |................|
000000a4  ec 60 01 00 ec 60 01 00  05 00 00 00 00 80 00 00  |.`...`..........|
000000b4  01 00 00 00 dc 6e 01 00  dc 6e 02 00 dc 6e 02 00  |.....n...n...n..|
000000c4  f8 03 00 00 8c 10 00 00  06 00 00 00 00 80 00 00  |................|
000000d4  02 00 00 00 e8 6e 01 00  e8 6e 02 00 e8 6e 02 00  |.....n...n...n..|
000000e4  18 01 00 00 18 01 00 00  06 00 00 00 04 00 00 00  |................|
000000f4  04 00 00 00 70 01 00 00  70 81 00 00 70 81 00 00  |....p...p...p...|
00000104  44 00 00 00 44 00 00 00  04 00 00 00 04 00 00 00  |D...D...........|
00000114  51 e5 74 64 00 00 00 00  00 00 00 00 00 00 00 00  |Q.td............|
00000124  00 00 00 00 00 00 00 00  06 00 00 00 04 00 00 00  |................|
00000134  52 e5 74 64 dc 6e 01 00  dc 6e 02 00 dc 6e 02 00  |R.td.n...n...n..|
00000144  24 01 00 00 24 01 00 00  04 00 00 00 01 00 00 00  |$...$...........|

以第一個 program header 來說, Elf32_Phdr 對應的值是:
p_type:   01 00 00 70 = 0x70000001
p_offset: c0 60 01 00 = 0x000160c0
p_vaddr:  c0 e0 01 00 = 0x0001e0c0
p_paddr:  c0 e0 01 00 = 0x0001e0c0
p_filesz: 28 00 00 00 = 0x00000028
p_memsz:  28 00 00 00 = 0x00000028
p_flags:  04 00 00 00 = 0x00000004
p_align:  04 00 00 00 = 0x00000004

其中 p_type 常見的值:

  • PT_LOAD (1) : 載入的 program section
  • PT_DYNAMIC (2) : 動態連結資訊
  • PT_INTERP (3) : program interpreter
  • PT_NOTE (4) : 輔助資訊
  • PT_PHDR (6) : program header本身
  • PT_TLS (7) : thread local storage

在 readelf 印出來的資料中, "Section to Segment mapping"底下的資料是每個program header對應的segment以及它包含的section list
比如說, 第1個 header 的 p_type 是 EXIDX, 它的 segment 是 index 00, 它包含的section是 ".ARM.exidx"
第4個 header的 p_type 是 LOAD, 它的 segment 是 index 03, 它包含的 section 是 ".interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .ARM.exidx .eh_frame"

4. Section header


section header table 從 "Start of section headers" (e_shoff) 的 offset 開始
它的大小為 "Size of section headers" (e_shentsize) 乘以 "Number of section headers" (e_shnum)

"/bin/ls" 的 offset 是 95220 bytes, 大小為 40 bytes * 28 items = 1120 bytes

用 readelf 解讀:
$ readelf --section-headers /bin/ls
There are 28 section headers, starting at offset 0x173f4:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        00008154 000154 000019 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            00008170 000170 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            00008190 000190 000024 00   A  0   0  4
  [ 4] .hash             HASH            000081b4 0001b4 000380 04   A  6   0  4
  [ 5] .gnu.hash         GNU_HASH        00008534 000534 0003f8 04   A  6   0  4
  [ 6] .dynsym           DYNSYM          0000892c 00092c 0007d0 10   A  7   1  4
  [ 7] .dynstr           STRTAB          000090fc 0010fc 000592 00   A  0   0  1
  [ 8] .gnu.version      VERSYM          0000968e 00168e 0000fa 02   A  6   0  2
  [ 9] .gnu.version_r    VERNEED         00009788 001788 0000b0 00   A  7   5  4
  [10] .rel.dyn          REL             00009838 001838 000030 08   A  6   0  4
  [11] .rel.plt          REL             00009868 001868 000350 08   A  6  13  4
  [12] .init             PROGBITS        00009bb8 001bb8 00000c 00  AX  0   0  4
  [13] .plt              PROGBITS        00009bc4 001bc4 00050c 04  AX  0   0  4
  [14] .text             PROGBITS        0000a0d0 0020d0 010bd0 00  AX  0   0  8
  [15] .fini             PROGBITS        0001aca0 012ca0 000008 00  AX  0   0  4
  [16] .rodata           PROGBITS        0001aca8 012ca8 003418 00   A  0   0  4
  [17] .ARM.exidx        ARM_EXIDX       0001e0c0 0160c0 000028 00  AL 14   0  4
  [18] .eh_frame         PROGBITS        0001e0e8 0160e8 000004 00   A  0   0  4
  [19] .init_array       INIT_ARRAY      00026edc 016edc 000004 00  WA  0   0  4
  [20] .fini_array       FINI_ARRAY      00026ee0 016ee0 000004 00  WA  0   0  4
  [21] .jcr              PROGBITS        00026ee4 016ee4 000004 00  WA  0   0  4
  [22] .dynamic          DYNAMIC         00026ee8 016ee8 000118 08  WA  7   0  4
  [23] .got              PROGBITS        00027000 017000 0001bc 04  WA  0   0  4
  [24] .data             PROGBITS        000271c0 0171c0 000114 00  WA  0   0  8
  [25] .bss              NOBITS          000272d8 0172d4 000c90 00  WA  0   0  8
  [26] .ARM.attributes   ARM_ATTRIBUTES  00000000 0172d4 00002f 00      0   0  1
  [27] .shstrtab         STRTAB          00000000 017303 0000f1 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

而 section header 的定義為:
typedef struct
{
  Elf32_Word    sh_name;                /* Section name (string tbl index) */
  Elf32_Word    sh_type;                /* Section type */
  Elf32_Word    sh_flags;               /* Section flags */
  Elf32_Addr    sh_addr;                /* Section virtual addr at execution */
  Elf32_Off     sh_offset;              /* Section file offset */
  Elf32_Word    sh_size;                /* Section size in bytes */
  Elf32_Word    sh_link;                /* Link to another section */
  Elf32_Word    sh_info;                /* Additional section information */
  Elf32_Word    sh_addralign;           /* Section alignment */
  Elf32_Word    sh_entsize;             /* Entry size if section holds table */
} Elf32_Shdr;


常見的 section type:

  • SHT_PROGBITS (1) : 程式資料
  • SHT_SYMTAB (2) : 符號表
  • SHT_STRTAB (3) : 字串表
  • SHT_RELA (4) : relocation addition item
  • SHT_HASH (5) : symbol hash table
  • SHT_DYNAMIC (6) : 動態連結資訊
  • SHT_NOTE (7)
  • SHT_NOBITS (8) : 檔案裡不是資料的部份 (.bss)
  • SHT_REL (9) : relocatable item
  • SHT_DYNSYM (11) : dynamic linker 所使用的 symbol table
  • SHT_INIT_ARRAY (14) : constructor array (.init)
  • SHT_FINI_ARRAY (15) : destructor array (.fini)

在 elf header 裡, e_shstrndx 標明字串表的 section index, 這個例子裡 "Section header string table index" (e_shstrndx) 是 27, section header 的 name (sh_name) 是從這個 section 對應而來

在index 27, 它的 offset = 0x017303, size = 0xf1 (241), 用 hexdump把它印出來
$ hexdump -n 241 -s 0x017303 -C /bin/ls
00017303  00 2e 73 68 73 74 72 74  61 62 00 2e 69 6e 74 65  |..shstrtab..inte|
00017313  72 70 00 2e 6e 6f 74 65  2e 41 42 49 2d 74 61 67  |rp..note.ABI-tag|
00017323  00 2e 6e 6f 74 65 2e 67  6e 75 2e 62 75 69 6c 64  |..note.gnu.build|
00017333  2d 69 64 00 2e 67 6e 75  2e 68 61 73 68 00 2e 64  |-id..gnu.hash..d|
00017343  79 6e 73 79 6d 00 2e 64  79 6e 73 74 72 00 2e 67  |ynsym..dynstr..g|
00017353  6e 75 2e 76 65 72 73 69  6f 6e 00 2e 67 6e 75 2e  |nu.version..gnu.|
00017363  76 65 72 73 69 6f 6e 5f  72 00 2e 72 65 6c 2e 64  |version_r..rel.d|
00017373  79 6e 00 2e 72 65 6c 2e  70 6c 74 00 2e 69 6e 69  |yn..rel.plt..ini|
00017383  74 00 2e 74 65 78 74 00  2e 66 69 6e 69 00 2e 72  |t..text..fini..r|
00017393  6f 64 61 74 61 00 2e 41  52 4d 2e 65 78 69 64 78  |odata..ARM.exidx|
000173a3  00 2e 65 68 5f 66 72 61  6d 65 00 2e 69 6e 69 74  |..eh_frame..init|
000173b3  5f 61 72 72 61 79 00 2e  66 69 6e 69 5f 61 72 72  |_array..fini_arr|
000173c3  61 79 00 2e 6a 63 72 00  2e 64 79 6e 61 6d 69 63  |ay..jcr..dynamic|
000173d3  00 2e 67 6f 74 00 2e 64  61 74 61 00 2e 62 73 73  |..got..data..bss|
000173e3  00 2e 41 52 4d 2e 61 74  74 72 69 62 75 74 65 73  |..ARM.attributes|

也可以用 readelf印出來
$ readelf -x27 /bin/ls

Hex dump of section '.shstrtab':
  0x00000000 002e7368 73747274 6162002e 696e7465 ..shstrtab..inte
  0x00000010 7270002e 6e6f7465 2e414249 2d746167 rp..note.ABI-tag
  0x00000020 002e6e6f 74652e67 6e752e62 75696c64 ..note.gnu.build
  0x00000030 2d696400 2e676e75 2e686173 68002e64 -id..gnu.hash..d
  0x00000040 796e7379 6d002e64 796e7374 72002e67 ynsym..dynstr..g
  0x00000050 6e752e76 65727369 6f6e002e 676e752e nu.version..gnu.
  0x00000060 76657273 696f6e5f 72002e72 656c2e64 version_r..rel.d
  0x00000070 796e002e 72656c2e 706c7400 2e696e69 yn..rel.plt..ini
  0x00000080 74002e74 65787400 2e66696e 69002e72 t..text..fini..r
  0x00000090 6f646174 61002e41 524d2e65 78696478 odata..ARM.exidx
  0x000000a0 002e6568 5f667261 6d65002e 696e6974 ..eh_frame..init
  0x000000b0 5f617272 6179002e 66696e69 5f617272 _array..fini_arr
  0x000000c0 6179002e 6a637200 2e64796e 616d6963 ay..jcr..dynamic
  0x000000d0 002e676f 74002e64 61746100 2e627373 ..got..data..bss
  0x000000e0 002e4152 4d2e6174 74726962 75746573 ..ARM.attributes
  0x000000f0 00                                  .

section header 的 sh_name 是字串表的 offset, 比如說 sh_name=1的話, name就是".shstrtab", 後面有字串結尾'\0', 如果 sh_name=0x0b的話, name 就是 ".interp"

5. Symbol table


如果檔案有strip過, 那麼只會看到 dynamic symbol table
"/bin/ls" 的 section header index 06 它的 type 是 DYNSYM, 所以 dynamic symbol table存在這
也可以先用 readelf來解讀
$ readelf --syms /bin/ls

Symbol table '.dynsym' contains 125 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr0@GCC_3.5 (2)
     2: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 00009c08     0 FUNC    GLOBAL DEFAULT  UND getpwnam@GLIBC_2.4 (3)
......

如果要用hexdump的話, section header index 06 的 offset是0x92c, 大小是0x7d0
$ hexdump -n 32 -s 0x92c -C /bin/ls
0000092c  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
0000093c  cd 00 00 00 00 00 00 00  00 00 00 00 12 00 00 00  |................|

而symbol item 的 struct :
typedef struct
{
  Elf32_Word    st_name;                /* Symbol name (string tbl index) */
  Elf32_Addr    st_value;               /* Symbol value */
  Elf32_Word    st_size;                /* Symbol size */
  unsigned char st_info;                /* Symbol type and binding */
  unsigned char st_other;               /* Symbol visibility */
  Elf32_Section st_shndx;               /* Section index */
} Elf32_Sym;

struct大小為16 bytes, 以第2個item來說, 它對應的值是
st_name:  cd 00 00 00 = 0x000000cd
st_value: 00 00 00 00
st_size:  00 00 00 00
st_info:  12          = STB_GLOBAL | STT_FUNC
st_other: 00
st_shndx: 00 00

st_name 雖然也是參考 string table, 但它參考的是 dynamic string table, 也就是 section header index 07 的表

st_info 分成高位 4 bit 及低位 4 bit
高位4 bit為:

  • STB_LOCAL (0) : local symbol
  • STB_GLOBAL (1) : global symbol
  • STB_WEAK (2) : weak linking symbol
低位4 bit為:

  • STT_OBJECT (1) : data object
  • STT_FUNC (2) : function
  • STT_SECTION (3) : section
  • STT_FILE (4) : source file name which related to object
  • STT_COMMON (5) : share data
  • STT_TLS (6) : thread local data