2015/07/22

Battery

1. Introduction


電量 (Power Capacity) 指的是電池裡儲存了多少能量, 單位通常是瓦特小時 (Watt-hour, Wh)

  • Voltage * Amp * hour = Wh

舉例來說, 3V的額定電池 (nominal battery), 輸出1A的電, 使用了1小時, 所以它的電量是 3 V * 1 A * 1 h = 3 Wh

通常同一種電池類型裡, 電壓是固定的, 所以規格裡的單位只會標示安培小時 (Amps-hour, Ah)
注意 V x Ah = Wh, 所以兩個單位可以乘以或除以電壓來互換

前個例子裡, 3V額定電池有 1Ah 的電量, 所以它也可以 0.1 A 使用 10 h, 或是 10 A 使用 0.1 h

但是通常 1 Ah 的電池實際上無法使用到這麼多, 當輸出的電流越大, 能使用的時間就非線性地越短, 也就是說, 1 Ah 的電池如果輸出 10 A, 那麼它使用的時間會小於 0.1h

以勁量電池 (Energizer Battery) 的 AAA電池 (型號E92) 來說, 可以看到同個電壓裡, 當輸出電流越大 (線條往右走), 使用的時間並不是線性減少, 而是稍微往下偏移


另外, 電池電量跟電池的額定容量 (nominal capacity) 是不一樣的, 一般電池如果內裝是 3.6V 或 3.7V, 為了升壓到 5V 做一般性的使用會造成能量消耗, 原本的電量也會減少

電量密度 (power density) 是指單位重量可提供的電量, 密度越高, 代表輕便, 也通常代表價錢越貴

2. Lead Acid Battery

鉛蓄電池的特點是便宜, 可充電, 隨處可得, 通常用在需要大量電源, 並且重量不是問題的地方
它通常是以 2V 的核心組成, 所以可以看到 2V, 6V, 12V, 24V 的鉛蓄電池

3. Alkaline Battery


鹼性電池是常見的電池, 它的 power density 比鎳鎘電池高, 也比鎳氫電池略高, 電量低, 但在低電量的用途有較長的使用時間, 以一般性的使用來說很方便, 但不能充電

它以 1.5V 的核心組成, 大小則從 AAAA ~ D 不等

6V 的電池是用大一些的鹼性電池做成, 電量大, 所以使用起來也很方便

9V 的電池則是由 6 個 1.5V 電組成, 電量低, 體積又省不到多少

4. Ni-Cad Battery


鎳鎘電池是舊型的充電電池, 大小有 AA, AAA, C, 但現在較少看到, 因為鎳氫電池有較高的 power density

另外一方面, 它放電速度比鎳氫電池慢, 也比鎳氫電池便宜, 所以仍可以看到它用在一些價錢考量的裝置上

它以 1.2V 的核心組成, 常看到它以 3 個組成 3.6 V的電池

5. Ni-HM Battery


鎳氫電池是充電電池, 電量比鹼性電池高, 但是放電速度比鎳鎘電池高, 生命周期也較低

它以 1.25V 的核心組成

6. Li-Ion (Lithium-Ion) and LiPoly (Lithium Polymer)


鋰離子電池和鋰聚合物電池是充電電池, 它很輕, 低放電, 高power density, 但它需要一些迴路保護它爆炸

它通常以 3.6V 核心組成, 所以可以看到 3.6V 或 7.2V 的規格

7. Lithium Battery and Coin Cells


鋰電池通常做成鈕扣形狀, 用在需要電池體積小的情況, 不可充電, 安全性高, 生命周期長, 也不貴

常見的規格是 CR2032, (直徑 20mm x 厚 3.2mm), 提供 3V 200mAh






2015/06/16

cmake

1. Introduction


CMake 是跨平台自動化建構系統, 但用來管理Makefile也很方便
只要安裝 cmake 套件就可以使用

sudo apt-get install cmake


2. 簡易範例


假如有底下這些檔案
  • myproj/
    • main.c
    • prime.c
    • prime.h
    • CMakeLists.txt
其中 CMakeLists.txt 的內容為

add_executable(myproj main.c prime.c)

CMake 預設會去找 CMakeLists.txt, 並解譯裡面的內容
add_executable() 裡面, 第一個參數是 executable target, 名字可以自定, 這個 executable target 會由後面的 source file 所編譯成。
CMakeLists.txt 可以放很多個 add_executable(), 代表同個 Makefile 可以生出不同的 executable target

接著我們移到 myproj 的目錄底下, 並下這個 command

$ cmake .

後面的點代表當前目錄
執行後就會生出 Makefile, 要注意的是這個 Makefile 無法在沒有 CMake 的環境下單獨使用。如果將整包檔案換環境, 就應該重下cmake command

接著可以操作 Makefile 的一些 command, 像是 make, 或 make clean

3. 簡易專案


假如有底下這些檔案
  • myproj/
    • build/
    • src/
      • CMakeLists.txt
      • main.c
      • prime.c
      • prime.h

那麼移動到 build目錄底下:
$ cmake ../src

這樣就可以將 source code 跟 target file 分開

4. optimization


cmake 提供幾組 compile config, 預設值是None

  • None: default
  • Debug: 產生 debug information
  • Release: 速度最佳化
  • RelWithDebInfo: 速度最加化, 但包含 debug flag
  • MinSizeRel: 程式碼最小化

假如現在我們有這些檔案
  • myproj/
    • build/
    • release/
    • src/
      • CMakeLists.txt
      • main.c
      • prime.c
      • prime.h
然後移動到 release 目錄底下, 這樣 release 的 make file 就會包含速度最佳化

$ cmake -DCMAKE_BUILD_TYPE=Release ../src


5. my project


底下是我的 project euler 使用的 example, 裡面的檔案有:

  • pe/
    • bin/
    • debug/
      • CMakeLists.txt
    • src/
      • main.cpp
      • prime.cpp
      • CMakeLists.txt
    • release/
      • CMakeLists.txt
    • inc/
      • prime.h
其中 pe/debug/CMakeLists.txt 的內容和 pe/release/CMakeLists.txt 的內容一樣
cmake_minimum_required(VERSION 2.6)

project(pe)
subdirs(../src)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../bin)


然後 pe/src/CMakeLists.txt 的內容
include_directories(../inc)
add_executable(pe main.cpp prime.cpp)

要建 debug 的 makefile 就轉到 pe/debug/ 目錄底下打這個 command
$ cmake -DCMAKE_BUILD_TYPE=Debug

要建 release 的 makefile 就轉到 pe/release/ 目錄底下打這個 command
$ cmake -DCMAKE_BUILD_TYPE=Release


2015/04/30

OpenMP in linux

1. Introduction


寫 project euler 的時候, 有些問題想不到速度快的解法, 只好用平行運算來加速
平常程式都以一條 thread 在執行, 要開多條 thread 來跑, 在管理上就很麻煩
OpenMP 讓人可以平行處理, 又可以減少管理的麻煩

2. 對 for 做 parallel


底下是範例程式

#include <stdio.h>
#include <omp.h>

int main() {

 #pragma omp parallel for
 for (int i=0; i<6; i++) {
  printf("%d ", i);
 }
 printf("\r\n");

 return 0;
}

然後compile它, 這邊使用c99來compile:

c99 -o openmp_example_01 openmp_example_01.c -fopenmp

幾個重點
  • 在 header 檔要加入 omp.h
  • 在 for 迴圈前加上 #pragma omp, 這個是用來使用omp的功能, 後面是控制敘述
  • compile 的時候加上 -fopenmp
結果可以看到
4 0 1 5 2 3

每次執行的結果都不一樣

3. 語法


它的語法大改長這樣

#pragma omp <directive> [clause[[,] clause] ...]

前個例子裡, 其實 parallel 和 for 都是 directive 的敘述, 所以它可以拆成兩個
#pragma omp parallel
{
 #pragma omp for
 for (int i=0; i<6; i++) {
  printf("%d ", i);
 }
}

可以看 wiki 上面的指令清單
OpenMP in wiki

4. thread information


如果要知道目前的 thread number, 可以用 omp_get_thread_num()
#include <stdio.h>
#include <omp.h>

int main() {

 #pragma omp parallel for
 for (int i=0; i<6; i++) {
  printf("T:%d i:%d\r\n", omp_get_thread_num(), i );
 }

 return 0;
}

執行結果
T:2 i:4
T:1 i:2
T:1 i:3
T:0 i:0
T:0 i:1
T:3 i:5

可以看到我的電腦上, 它跑了4條thread, 其中 thread 0處理 i 是 0 & 1的情況, thread 1 處理 i 是 2 & 3 的情況....

5. 控制 thread number


可以用 num_threads(n) 來控制要開多少 thread
#include <stdio.h>
#include <omp.h>

int main() {

 #pragma omp parallel for num_threads(2)
 for (int i=0; i<6; i++) {
  printf("T:%d i:%d\r\n", omp_get_thread_num(), i );
 }

 return 0;
}

執行結果可以看到只開2條thread了
T:1 i:3
T:1 i:4
T:1 i:5
T:0 i:0
T:0 i:1
T:0 i:2

6. 手動切割 section 做平行處理


#include <stdio.h>
#include <omp.h>

int main() {

 #pragma omp parallel sections
 {
  #pragma omp section
  {
   printf("T:%d section 0\r\n", omp_get_thread_num());
  }
  #pragma omp section
  {
   printf("T:%d section 1\r\n", omp_get_thread_num());
  }
  #pragma omp section
  {
   printf("T:%d section 2\r\n", omp_get_thread_num());
  }
 }

 return 0;
}

執行結果
T:2 section 0
T:3 section 1
T:0 section 2

要注意的是 section 之間不可以有相依性, 不然要額外做處理

7. for 裡面處理共同變數


要注意的是 parallel for 只會將 section 裡的變數做平行處理,
section 外的變數則會當成是所有thread 共用的變數,
要讓 openmp知道 section 裡的變數是每個thread自己有一份的話, 則要加上private

#include <stdio.h>
#include <omp.h>

int main() {

 int i, j;

 #pragma omp parallel for private ( j )
 for (i=0; i<2; i++) {
  for (j=0; j<2; j++) {
   printf("(%d,%d)\r\n", i, j);
  }
 }

 return 0;
}

如果不加上private, 那麼當外面的 thread 在做處理時,
就有可能不小心把別的 thread 的變數 j 加了 1

如果希望 section 裡面每個 thread 共用一份變數,
並且希望它不會有處理先後順序造成的問題, 可以使用 atomic

#include <stdio.h>
#include <omp.h>

int main() {

 int count = 0;
 #pragma omp parallel for
 for (int i=0; i<10; i++) {
  #pragma omp atomic
  count++;
 }
 printf("count: %d\r\n", count);

 return 0;
}

如果沒有加上 atomic 的話, count 最後的結果就有可能不是10







2015/01/05

FreeRTOS Low Power Support

1. Introduction


在 FreeRTOS 裡, Power Saving 相關的工作通常會放在 idle task 裡面作, 它的作法是限制periodic tick interrupt, 這樣可以減少 period task的耗電

實際作法是, 它停止 periodic tick interrupt, 然後在 restart periodic tick interrupt實調整 RTOS tick count

在 tick interrupt 停止的時候, microcontroller 就可以進 deep power saving state, 直到有interrupt發生

2. Idle Task


idle task 放在 task.c
static portTASK_FUNCTION( prvIdleTask, pvParameters )

裡面有 infinite for loop 不停地執行一些工作, 包括:

  1. 檢查有沒有 task 已經被刪除
  2. 檢查 preemption 相關的工作
  3. 呼叫 idle hook funtion
  4. Power Saving Task

其中有兩個 hook function 會因為 power saving 的機制受到影響

  • vApplicationIdleHook : 在 idle task 裡每次呼叫
  • vApplicationTickHook : 在 idle task 裡, 並且只有在 sytem tick 增加時才呼叫
這兩個 hook function 都是直接 hard code 實作, 預設是空的, 為了避免影響其它task, 這兩個hook function 裡面不可以擺可能會 blocking 的 task

3. Power Saving


底下是 idle task 裡的 power saving code:
/* This conditional compilation should use inequality to 0, not equality
to 1.  This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when
user defined low power mode implementations require
configUSE_TICKLESS_IDLE to be set to a value other than 1. */
#if ( configUSE_TICKLESS_IDLE != 0 )
{
TickType_t xExpectedIdleTime;

 /* It is not desirable to suspend then resume the scheduler on
 each iteration of the idle task.  Therefore, a preliminary
 test of the expected idle time is performed without the
 scheduler suspended.  The result here is not necessarily
 valid. */
 xExpectedIdleTime = prvGetExpectedIdleTime();

 if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
 {
  vTaskSuspendAll();
  {
   /* Now the scheduler is suspended, the expected idle
   time can be sampled again, and this time its value can
   be used. */
   configASSERT( xNextTaskUnblockTime >= xTickCount );
   xExpectedIdleTime = prvGetExpectedIdleTime();

   if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
   {
    traceLOW_POWER_IDLE_BEGIN();
    portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
    traceLOW_POWER_IDLE_END();
   }
   else
   {
    mtCOVERAGE_TEST_MARKER();
   }
  }
  ( void ) xTaskResumeAll();
 }
 else
 {
  mtCOVERAGE_TEST_MARKER();
 }
}
#endif /* configUSE_TICKLESS_IDLE */

它第一步先計算預計會idle的時間 xExpectedIdleTime, 如果這個時間大於 configEXPECTED_IDLE_TIME_BEFORE_SLEEP 才真的 sleep, 不然的話, idle時間太短又跑去sleep會造成額外的 effort

接著 vTaskSuspendAll(), 這裡將 uxSchedulerSuspended 加 1, 於是task scheduler就被停止

然後重新計算 idle time, 接著進入sleep
traceLOW_POWER_IDLE_BEGIN();
portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
traceLOW_POWER_IDLE_END();

最後 xTaskResumeAll(), 將 uxSchedulerSuspended 減 1, 將所有應該要切入 ready state 的 task 喚醒, 最後將 system tick 加回來

4. portSUPPRESS_TICKS_AND_SLEEP


portSUPPRESS_TICKS_AND_SLEEP() 這個macro根據不同的MCU而有不同的實作, 在GCC, IAR, Keil都有default的實作

通常 SysTick 跟 Cortex-M Microcontrollers Clock 是一樣的 frequency, 這種情況下, 這兩個值是一樣的, configSYSTICK_CLOCK_HZ 以及 configCPU_CLOCK_HZ

如果 SysTick 比 Core Clock還慢的話, 就要定義 configSYSTICK_CLOCK_HZ 這個值

其中sleep的機制會呼叫到Cortex-M的 function call
/* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can
set its parameter to 0 to indicate that its implementation contains
its own wait for interrupt or wait for event instruction, and so wfi
should not be executed again.  However, the original expected idle
time variable must remain unmodified, so a copy is taken. */
xModifiableIdleTime = xExpectedIdleTime;
configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
if( xModifiableIdleTime > 0 )
{
 __DSB();
 __WFI();
 __ISB();
}
configPOST_SLEEP_PROCESSING( xExpectedIdleTime );

其中會有個 pre & post processing, 中間sleep的部份取決於不同MCU而有不同的code

DSP 表示 Data Synchronization Barrier, 它將 processor目前做到一半的工作做完
WFI 表示 Wait For Interrupt, 它讓 processor 進 suspend, 直到底下其中一個發生:

  • non-masked interrupt 發生
  • 被 PRIMASK mask 的 interrupt 被 pending
  • Debug Entery 的 request

ISB 表示 Instruction Synchronization Barrier, 它將 processor 的pipe line清空, 這樣才不會拿到之前未執行的 code 來執行




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