My code works, I don’t know why.

國王的耳朵是驢耳朵

談談C語言的全域變數和 Linux Object 檔案的關係

| Comments

印象中以前學過C語言中跨檔案的全域變數是這樣宣告的。 檔案一:int g_var1; 檔案二:extern int g_val1;

然而,我從來沒有想過如果沒有extern的情況會發生什麼狀況。加上之前看過的objdump和nm後手癢,所以把可能的排列組合看看可能發生什麼事。

這邊要先知道common object表示object檔案已經紀錄下這個symbol,最後link的時候才會決定要怎麼處理。會放在common section 出處

由於結果和分析又臭又長,連我自己都不太想回去看。建議先看結論,有疑惑再回來看結果和分析吧。

目錄

測試環境

1
2
3
4
5
6
$ lsb_release -a
No LSB modules are available.
Distributor ID:   Ubuntu
Description:  Ubuntu 14.04.1 LTS
Release:  14.04
Codename: trusty
  • GNU objdump (GNU Binutils for Ubuntu) 2.24
  • GNU nm (GNU Binutils for Ubuntu) 2.24

測試程式

非常簡單,就是有兩個檔案存放全域變數,每次測試時先更改全域變數 * 是否有初始值 * 宣告方式是否有extern 全部的排列組合放在這邊

var1.c
1
int g_var1;
var2.c
1
int g_var1;
main.c
1
2
3
4
5
6
7
#include <stdio.h>
extern int g_var1;
int main()
{
    printf("Hello %d\n", g_var1);
    return 0;
}
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TARGET=glvar
SRCS=var_1.c var_2.c main.c
OBJS=$(patsubst %.c, %.o, $(SRCS))
CFLAGS=-g

$(TARGET): $(OBJS)
  $(CC) -o $@ $^

test: $(TARGET)
  objdump -t $(OBJS) $(TARGET)
  nm -A $(OBJS) $(TARGET)
  ./$(TARGET)

clean:
  rm -f $(TARGET) $(OBJS)

測試結果

var1.c 宣告方式 var2.c宣告方式 編譯結果 nm 和 objdump 分析
int g_var; int g_var; 成功 分析
int g_var = 2; int g_var; 成功 分析
int g_var; int g_var = 3; 成功 分析
int g_var = 2; int g_var = 3; 失敗 分析
var1.c 宣告方式 var2.c宣告方式 編譯結果 nm 和 objdump 分析
int g_var; extern
int g_var;
成功 分析
int g_var = 2; extern
int g_var;
成功 分析
int g_var; extern
int g_var = 3;
有警告 分析
int g_var = 2; extern
int g_var = 3;
失敗 分析
var1.c 宣告方式 var2.c宣告方式 編譯結果 nm 和 objdump 分析
extern
int g_var;
extern
int g_var;
link失敗 分析
extern
int g_var = 2;
extern
int g_var;
有警告 分析
extern
int g_var
extern
int g_var = 3;
有警告 分析
extern
int g_var = 2;
extern
int g_var = 3;
link失敗 分析

分析 var1.c: int g_var; var2.c: int g_var; 的結果

  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 0000000000000004 O COM 0000000000000004 g_var1
      • 這是一個object,放在commom section
    • var2.o: 0000000000000004 O COM 0000000000000004 g_var1
      • 這是一個object,放在commom section。
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
    • glvar: 0000000000601044 g O .bss 0000000000000004 g_var1
      • 這個symbol放在.bss,也就是未初始化的全域變數
      • 這是最後產生的executable object
  • nm -A 顯示object檔案的symbol:
    • var_1.o:0000000000000004 C g_var1
      • 這是一個common symbol。
    • var_2.o:0000000000000004 C g_var1
      • 這是一個common symbol。
    • main.o: U g_var1
      • 這是一個undefined symbol
    • glvar: glvar:0000000000601044 B g_var1
      • 這是最後產生的executable object
      • 這個symbol放在.bss,也就是未初始化的全域變數

分析 var1.c: int g_var = 2; var2.c: int g_var; 的結果

  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
    • var2.o: 0000000000000004 O COM 0000000000000004 g_var1
      • 這是一個object,放在commom section
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
    • glvar: 0000000000601040 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
      • 這是最後產生的executable object
  • nm -A 顯示object檔案的symbol:
    • var_1.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • var_2.o:0000000000000004 C g_var1
      • 這是一個common symbol。
    • main.o: U g_var1
      • 這是一個undefined symbol
    • glvar:0000000000601040 D g_var1
      • 這是最後產生的executable object
      • 這個symbol是全域變數,並且放在有初始值的資料section

分析 var1.c: int g_var2 = 2; var2.c: int g_var; 的結果

和上面的差別只有var1.o和var2.o的結果對調而已。

分析 var1.c: int g_var2 = 2; var2.c: int g_var = 3; 的結果

  • 錯誤訊息: g_var1重複定義
Makefile
1
2
3
4
5
6
7
8
9
$ make test
cc -g   -c -o var_1.o var_1.c
cc -g   -c -o var_2.o var_2.c
cc -g   -c -o main.o main.c
cc -o glvar var_1.o var_2.o main.o
var_2.o:(.data+0x0): multiple definition of `g_var1'
var_1.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [glvar] Error 1
  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
    • var2.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
  • nm -A 顯示object檔案的symbol:
    • var_1.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • var_2.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • main.o: U g_var1
      • 這是一個undefined symbol

分析 var1.c: int g_var; var2.c: extern int g_var; 的結果

  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 0000000000000004 O COM 0000000000000004 g_var1
      • 這是一個object,放在commom section
    • var2.o: 沒有g_var1這個entry
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
    • glvar: 0000000000601040 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
      • 這是最後產生的executable object
  • nm -A 顯示object檔案的symbol:
    • var_1.o:0000000000000004 C g_var1
      • 這是一個common symbol
    • var_2.o: 沒有symbol
    • main.o: U g_var1
      • 這是一個undefined symbol
    • glvar:0000000000601040 B g_var1
      • 這是最後產生的executable object
      • 這個symbol是全域變數,並且放在沒有初始值的資料section

分析 var1.c: int g_var = 2; var2.c: extern int g_var; 的結果

  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
    • var2.o: 沒有g_var1這個entry
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
    • glvar: 0000000000601040 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
      • 這是最後產生的executable object
  • nm -A 顯示object檔案的symbol:
    • var_1.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • var_2.o: 沒有symbol
    • main.o: U g_var1
      • 這是一個undefined symbol
    • glvar:0000000000601040 D g_var1
      • 這是最後產生的executable object
      • 這個symbol是全域變數,並且放在有初始值的資料section

分析 var1.c: int g_var; var2.c: extern int g_var = 3; 的結果

  • 得到警告和錯誤:
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
$ make test
rm -f glvar  var_1.o  var_2.o  main.o
cc -g   -c -o var_1.o var_1.c
cc -g   -c -o var_2.o var_2.c
var_2.c:1:12: warning: ‘g_var1’ initialized and declaredextern’ [enabled by default]
 extern int g_var1 = 3;
            ^
cc -g   -c -o main.o main.c
cc -o glvar var_1.o var_2.o main.o
var_2.o:(.data+0x0): multiple definition of `g_var1'
var_1.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [glvar] Error 1
  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 0000000000000004 O COM 0000000000000004 g_var1
      • 這是一個object,放在commom section
    • var2.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data sectio
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
    • glvar: 0000000000601040 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
      • 這是最後產生的executable object
  • nm -A 顯示object檔案的symbol:
    • var_1.o:0000000000000000 D g_var1
      • 這是一個common symbol
    • var_2.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • main.o: U g_var1
      • 這是一個undefined symbol
    • glvar:0000000000601040 D g_var1
      • 這是最後產生的executable object
      • 這個symbol是全域變數,並且放在有初始值的資料section

分析 var1.c: int g_var = 2; var2.c: extern int g_var = 3; 的結果

  • 得到警告和link錯誤:
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
$  make test
cc -g   -c -o var_1.o var_1.c
cc -g   -c -o var_2.o var_2.c
var_2.c:1:12: warning: ‘g_var1’ initialized and declaredextern’ [enabled by default]
 extern int g_var1 = 3;
            ^
cc -g   -c -o main.o main.c
cc -o glvar var_1.o var_2.o main.o
var_2.o:(.data+0x0): multiple definition of `g_var1'
var_1.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [glvar] Error 1
  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
    • var2.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
  • nm -A 顯示object檔案的symbol:
    • var_1.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • var_2.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • main.o: U g_var1
      • 這是一個undefined symbol

分析 var1.c: extern int g_var; var2.c: extern int g_var; 的結果

  • link時發生錯誤
Makefile
1
2
3
4
5
6
7
8
9
$ make test
cc -g   -c -o var_1.o var_1.c
cc -g   -c -o var_2.o var_2.c
cc -g   -c -o main.o main.c
cc -o glvar var_1.o var_2.o main.o
main.o: In function `main':
/home/wen/work/practice/Linux_Programming_Practice/16_global_var/main.c:5: undefined reference to `g_var1'
collect2: error: ld returned 1 exit status
make: *** [glvar] Error 1
  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 沒有g_var這個 entry
    • var2.o: 沒有g_var這個 entry
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
  • nm -A 顯示object檔案的symbol:
    • var_1.o: 沒有symbol
    • var_2.o: 沒有symbol
    • main.o: U g_var1
      • 這是一個undefined symbol

分析 var1.c: extern int g_var = 2; var2.c: extern int g_var; 的結果

  • 得到警告:
Makefile
1
var_1.c:1:12: warning: ‘g_var1’ initialized and declaredextern’ [enabled by default]
  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
    • var2.o: 沒有g_var這個 entry
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
  • nm -A 顯示object檔案的symbol:
    • var_1.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • var_2.o: 沒有symbol
    • main.o: U g_var1
      • 這是一個undefined symbol

分析 var1.c: extern int g_var2; var2.c: extern int g_var = 3; 的結果

和上面的差別只有var1.o和var2.o的結果對調而已。

分析 var1.c: extern int g_var2 = 2; var2.c: extern int g_var = 3; 的結果

  • 得到警告和錯誤:
Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ make clean ; make test
rm -f glvar  var_1.o  var_2.o  main.o
cc -g   -c -o var_1.o var_1.c
var_1.c:1:12: warning: ‘g_var1’ initialized and declaredextern’ [enabled by default]
 extern int g_var1 = 2;
            ^
cc -g   -c -o var_2.o var_2.c
var_2.c:1:12: warning: ‘g_var1’ initialized and declaredextern’ [enabled by default]
 extern int g_var1 = 3;
            ^
cc -g   -c -o main.o main.c
cc -o glvar var_1.o var_2.o main.o
var_2.o:(.data+0x0): multiple definition of `g_var1'
var_1.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [glvar] Error 1
  • objdump -t 顯示object檔案的symbol table欄位資料:
    • var1.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
    • var2.o: 0000000000000000 g O .data 0000000000000004 g_var1
      • 這是一個全域變數物件,放在有初始值的.data section
    • main.o: 0000000000000000 UND 0000000000000000 g_var1
      • 這是一個undefined symbol
  • nm -A 顯示object檔案的symbol:
    • var_1.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • var_2.o:0000000000000000 D g_var1
      • 這個symbol是全域變數,並且放在有初始值的資料section
    • main.o: U g_var1
      • 這是一個undefined symbol

結論

  • common symbol表示object檔案已經紀錄下這個symbol,先放在common section。最後link的時候才會決定要怎麼處理。 出處
  • gcc -c產生的object檔案中未初始化的全域變數會放在common section而不是.bss section,原因是link時期其他的object可能有相同名稱的symbol,而這些相同的symbol可能有初始化也可能沒有。而linker需要全盤了解後再做出決定要放在.bss還是.data section。
  • 最後link出來的executable object檔案不會有common symbol
  • 多個檔案宣告同樣名稱的全域變數時,編譯時會依下面的情況處理
    • 超過兩個檔案有設初始值,噴錯誤
    • 都沒設定初始值,產生的object檔案放common section。link時放.bss section。
    • 一個檔案有設定初始值,其他沒有設定初始值的情形
      • 有設定初始值那個檔案的object檔案變數放在.data
      • 其他沒有設定初始值有設定初始值那個檔案的object檔案變數放在common section
      • link時放.data section
  • 使用extern全域變數的時候
    • 程式碼沒用到該全域變數在未link前的object 檔案不會有該symbol
    • 程式碼用到該全域變數在未link前的object 檔案該symbol為undefined
    • 程式碼給初始值會產生警告,並且會被新增到該object 檔案的symbol table,以至於其他檔案宣告該全域變數會產生重複定義的錯誤。

參考資料

Nm 的欄位說明

| Comments

在閱讀ld手冊的時候看到symbol table突然想起來我nm只知道tU這兩個flag而已。想起來真是慚愧,趕快問一下男人nm顯示出來的欄位意義。一個典型的nm輸出為: 00000000004005ed T main

這些代表什麼呢,整理如下:

  • symbol存放的值,應該是位址或是offset我猜
  • symbol flags,一般來說大寫表示global而小寫表示local
  • symbol 名稱

symbol flags詳細意義為:

  • A: symbol的值是絕對值,之後的link全部就hardcode不會更動這個symbol的值
  • B: global symbol。該symbol存放在沒有初始化的data section (猜測是.bss)
  • b: local symbol。該symbol存放在沒有初始化的data section (猜測是.bss)
  • C: common symbol。一種未初始化的資料。不知道是三小,男人只有說link多個檔案可能會有不同的common symbol使用相同的名稱,結果還是看不懂。
  • D: global symbol。該symbol存放在有初始化的data section (猜測是.data)
  • d: local symbol。該symbol存放在有初始化的data section (猜測是.data)
  • G: global symbol。該symbol存放在是針對小量資料做最佳化、有初始化的data section(不是.data)
  • g: local symbol。該symbol存放在是針對小量資料做最佳化、有初始化的data section(不是.data)
  • I:表示這個symbol間接地reference 其他的symbol。不過我還是沒有搞懂是什麼意義
  • i: 在elf下面的話,表示這個symbol是一個間接的function。這表示當link的時候不會解析位址,而是在runtime才會去呼叫真實的位址function。以下是範例,不過我還是沒有搞懂是什麼意義
strcat.o
1
2
3
4
5
6
x86_64-linux-gnu/libc.a:strcat.o:0000000000000040 T __GI_strcat
x86_64-linux-gnu/libc.a:strcat.o:                 U __init_cpu_features
x86_64-linux-gnu/libc.a:strcat.o:0000000000000000 i strcat
x86_64-linux-gnu/libc.a:strcat.o:0000000000000040 T __strcat_sse2
x86_64-linux-gnu/libc.a:strcat.o:                 U __strcat_sse2_unaligned
x86_64-linux-gnu/libc.a:strcat.o:                 U __strcat_ssse3
  • N: debug symbol
  • p: symbol放在unwind stack section。 不知道這邊的stack unwind和nm的意義是不是一樣?
  • R: global symbol。symbol放在read only data section(猜測是.rodata)。
  • r: local symbol。symbol放在read only data section(猜測是.rodata)。
  • S: global symbol。該symbol存放在是針對小量資料做最佳化、沒有初始化的data section(不是.bss)
  • s: local symbol。該symbol存放在是針對小量資料做最佳化、沒有初始化的data section(不是.bss)
  • T: global symbol。symbol放在程式碼section(猜測是.text)。
  • t: local symbol。symbol放在程式碼section(猜測是.text)。
  • U: undefined symbol
  • u: unique symbol,GNU 用於ELF時的 symbol binding extenstion。我還是沒有搞懂是什麼意義
  • V: global symbol。weak object,當link時該symbol可以link到正確的symbol就和一般的symbol相同,然而如果link時無法resolve symbol也不會噴錯誤,而是將該symbol的值設成0。(用在什麼地方?)
  • v: local symbol。weak object,當link時該symbol可以link到正確的symbol就和一般的symbol相同,然而如果link時無法resolve symbol也不會噴錯誤,而是將該symbol的值設成0。(用在什麼地方?)
  • W: global symbol。weak defined symbol,weak object的特例。當link時該symbol可以link到正確的symbol就和一般的symbol相同,然而如果link時無法resolve symbol也不會噴錯誤,而是根據系統實作做適當的處理。(用在什麼地方?)
  • w: local symbol。weak defined symbol,weak object的特例。當link時該symbol可以link到正確的symbol就和一般的symbol相同,然而如果link時無法resolve symbol也不會噴錯誤,而是根據系統實作做適當的處理。(用在什麼地方?)
  • -: stabs symbol,用於a.out格式,看不懂又和elf無關故跳過
  • ?: 不明的symbol

待釐清

  • 名詞解釋
    • common symbol
    • indirect reference
    • indirect function
    • unwind stack section
    • unique symbol
  • 用法不明
    • weak object
    • weak defined symbol

參考資料

  • man nm

Python Mode for Vim簡介

| Comments

Python Mode是一個vim下面關於Python 的plugin,快速整理一下以前看的文件。

目錄

安裝

使用vundle

  • 在.vim 加入
1
Bundle 'klen/python-mode'
  • 在vim下打:BundleInstall *(不確定是否必要)離開vim 再重新啟動vim
  • 執行下面命令
1
:helptags ~/.vim/bundle/python-mode/doc

使用Pathogen管理下的安裝

  • 下載套件
1
2
$ cd ~/.vim/bundle
$ git clone git://github.com/klen/python-mode.git
  • 在.vimrc啟動,加入以下內容
1
2
3
4
5
6
7
8
" Pathogen load
filetype off

call pathogen#infect()
call pathogen#helptags()

filetype plugin indent on
syntax on

使用

  • 執行
    • \r
      • \<leader> key的預設值
  • 除錯
    • \b
      • 設定中斷點,python mode會自動偵測除錯器,我使用pdb。不過設中斷點後執行之後那些除錯命令要在那邊下還不清楚。
  • 查詢
    • 游標到要查詢的keyword按K
      • 查詢目前statement使用方式
  • 查詢PyDoc資料
    • :PymodeDoc 你要查詢的東西
      • ex: :PymodeDoc print
  • 區塊折疊/展開
    • zo
      • 展開區塊
    • zc
      • 折疊區塊
    • za
      • toggle區塊
    • 以上的操作o, ca換成O, C, A的話表示Apply到目前區塊下面所有的子區塊
  • 移動
    • [[
      • 移動到游標上方第一層的block,通常是在查詢目前trace到哪個function或是class
    • ]]
      • 移動到游標下方第一層的block,通常是用於切換下一個function或是class
    • [m
      • 移動到游標上方class 第一個method
    • ]m
      • 移動到游標下方class 第一個method
  • 移動到定義(函數、class)
    • CTRL + C 後按g
  • 區塊處理
    • 先定義處理方式x’, x’可代換複製(y),刪除(d),選取(v)等
    • x'aC
      • 處理整個class
    • x'iC
      • inner, 處理目前游標包含的那整個class
    • x'aM
      • 處理一個整個method
    • x'iM
      • inner, 處理目前游標包含的那整個method

.vimrc 設定更動

  • 預設使用Python 2,更改使用Python 3

    • let g:pymode_python = 'python3'
      • 注意!這招要確認你的vim已經支援Python3,確認方式
      • $ vim --version | grep python3 應該要有+python3,目前Ubuntu 14.04.1沒有看到設定語法檢查工具,放這邊主要是可以檢查是否已經裝了相關套件了。
  • let g:pymode_lint_checkers = [‘pyflakes’, ‘pep8’, ‘mccabe’]

    • Refactor,使用rope,我沒使用過rope,故跳過

Remarks

我安裝了下面兩個套件,用來讓Python Mode檢查語法。不過可以明顯感覺到用了速度變很慢,目前還沒打算去看是否設定有錯誤。

  • sudo apt-get install python-rope
  • sudo apt-get install pylint

參考資料

抓漏 - Gdb 和 Valgrind 合體技

| Comments

接續前一篇的話題。在查資料的時候看到valgrind可以和gdb合作,試了一下發現他的作法和我想的不一樣。原本以為是valgrind跑出來以後直接把漏掉的call stack和program counter傳給gdb,而gdb把他當成一種中斷點。結果完全不是,他的用法是:

  • 跑valgrind,參數指定要和gdb合體,還有你要抓漏的程式
  • 另外一邊執行gdb,載入你要抓漏的程式
  • gdb 連到valgrind
  • 從gdb中設懷疑漏掉的中斷點
  • 從gdb叫valgrind開始執行你要抓漏的程式,gdb將會中斷在你設置的中斷點
  • 在使用valgrind提供的指令,比對執行中斷點之前和之後是否有memory leak

測試程式碼和測試環境和前一篇一模一樣,所以就請自行看連結。

合體示範

  • 跑valgrind,參數指定要和gdb合體,還有你要抓漏的程式
    • valgrind --vgdb=yes --vgdb-error=0 ./你要抓漏的程式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ valgrind --vgdb=yes --vgdb-error=0 ./leak
==28450== Memcheck, a memory error detector
==28450== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==28450== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==28450== Command: ./leak
==28450== 
==28450== (action at startup) vgdb me ... 
==28450== 
==28450== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==28450==   /path/to/gdb ./leak
==28450== and then give GDB the following command
==28450==   target remote | /usr/lib/valgrind/../../bin/vgdb --pid=28450
==28450== --pid is optional if only one valgrind process is running
==28450== 
  • 另外一邊執行gdb,載入你要抓漏的程式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ gdb ./leak
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./leak...done.
(gdb) 
  • gdb 連到valgrind
    • target remote | vgdb
1
2
3
4
5
6
7
8
(gdb)  target remote | vgdb
Remote debugging using | vgdb
relaying data between gdb and process 28450
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug//lib/x86_64-linux-gnu/ld-2.19.so...done.
done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
0x00000000040012d0 in _start () from /lib64/ld-linux-x86-64.so.2
(gdb) 
  • 從gdb中設懷疑漏掉的中斷點
1
2
3
4
(gdb) b 47
Breakpoint 1 at 0x400703: file leak.c, line 47.
(gdb) b 48
Breakpoint 2 at 0x400708: file leak.c, line 48.

47, 48行內容是

1
2
47      leak1();
48        ch_ptr = leak2();
  • 從gdb叫valgrind開始執行你要抓漏的程式,gdb將會中斷在你設置的中斷點
1
2
3
4
5
6
(gdb) c
Continuing.

Breakpoint 1, main () at leak.c:47
47        leak1();
(gdb)
  • 在使用valgrind提供的指令,比對執行中斷點之前和之後是否有memory leak
    • 詳細指令可以在gdb中打help monitor查詢

執行中斷點前

1
2
3
(gdb) monitor leak_check full reachable any
==28450== All heap blocks were freed -- no leaks are possible
==28450==

執行下一步後再檢查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(gdb) n

Breakpoint 2, main () at leak.c:48
48        ch_ptr = leak2();
(gdb) monitor leak_check full reachable any
==28450== 100 bytes in 1 blocks are still reachable in loss record 1 of 1
==28450==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28450==    by 0x40065E: leak1 (leak.c:11)
==28450==    by 0x400707: main (leak.c:47)
==28450==
==28450== LEAK SUMMARY:
==28450==    definitely lost: 0 bytes in 0 blocks
==28450==    indirectly lost: 0 bytes in 0 blocks
==28450==      possibly lost: 0 bytes in 0 blocks
==28450==    still reachable: 100 bytes in 1 blocks
==28450==         suppressed: 0 bytes in 0 blocks

多執行幾步後再看看leak情況

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
(gdb) n
49        if(ch_ptr) {
(gdb) n
50            printf("%s", ch_ptr);
(gdb) n
53        lk_ptr = leak3();
(gdb) n
54        if(lk_ptr) {
(gdb) monitor leak_check full reachable any
==28450== 8 bytes in 1 blocks are still reachable in loss record 2 of 5
==28450==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28450==    by 0x4006C3: leak3 (leak.c:33)
==28450==    by 0x400732: main (leak.c:53)
==28450==
==28450== 14 bytes in 1 blocks are still reachable in loss record 3 of 5
==28450==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28450==    by 0x4EC02B9: strdup (strdup.c:42)
==28450==    by 0x400687: leak2 (leak.c:20)
==28450==    by 0x40070C: main (leak.c:48)
==28450==
==28450== 14 bytes in 1 blocks are still reachable in loss record 4 of 5
==28450==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28450==    by 0x4EC02B9: strdup (strdup.c:42)
==28450==    by 0x4006E2: leak3 (leak.c:37)
==28450==    by 0x400732: main (leak.c:53)
==28450==
==28450== 100 bytes in 1 blocks are definitely lost in loss record 5 of 5
==28450==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28450==    by 0x40065E: leak1 (leak.c:11)
==28450==    by 0x400707: main (leak.c:47)
==28450==
==28450== LEAK SUMMARY:
==28450==    definitely lost: 100 bytes in 1 blocks
==28450==    indirectly lost: 0 bytes in 0 blocks
==28450==      possibly lost: 0 bytes in 0 blocks
==28450==    still reachable: 36 bytes in 3 blocks
==28450==         suppressed: 0 bytes in 0 blocks
==28450==

參考資料

抓漏 - 使用valgrind檢查C語言memory Leak

| Comments

使用C 語言,memory leak的問題是最棘手的狀況之一,茫茫code海要一個一個比對簡直是大海撈針。幸好Linux下面有好的的工具可以救你一命。簡單介紹一下希望對大家有幫助。

目錄

測試環境

  • valgrind 在Ubuntu下面可以直接用sudo apt-get install valgrind安裝。
1
2
3
4
5
6
7
8
9
$ lsb_release -a
No LSB modules are available.
Distributor ID:   Ubuntu
Description:  Ubuntu 14.04.1 LTS
Release:  14.04
Codename: trusty

$ valgrind --version
valgrind-3.10.0.SVN

測試程式

這邊準備了幾個典型的memory leak程式,應該是很簡單看出哪邊有leak,所以我就不多做說明了。

leak.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct leak_example {
    char *ptr;
};

void leak1(void)
{
    int *ptr = (int *)malloc(100);

    if (!ptr) {
        printf("Oops, malloc fail!\n");
    }
}

char *leak2(void)
{
    char *ptr = strdup("Hey Hey Hey!\n");

    if (!ptr) {
        printf("Oops, strdup fail!\n");
        return 0;
    }
    return ptr;
}

struct leak_example *leak3(void)
{
    struct leak_example *ptr = 0;

    ptr = (struct leak_example *) malloc(sizeof(struct leak_example));
    if (!ptr) {
        printf("Oops, malloc fail!\n");
    }
    ptr->ptr = strdup("Hey Hey Hey!\n");

    return ptr;
}

int main()
{
    struct leak_example *lk_ptr;
    char * ch_ptr = 0;

    leak1();

    ch_ptr = leak2();
    if(ch_ptr) {
        printf("%s", ch_ptr);
    }

    lk_ptr = leak3();
    if(lk_ptr) {
        printf("%s", lk_ptr->ptr);
        free(lk_ptr);
    }

    return 0;
}

因為只是單一檔案,我懶得寫Makefile,編譯指令如下:

leak.c
1
$ CFLAGS="-g" make leak

這行指令可以去找Makefile中Implicit Rules得到解答。

執行方式

最簡單的方式就是用下面的方式

leak.c
1
$ valgrind 你的執行檔

然而這樣只會顯示出有漏掉多少的空間,因此要詳細地列出memory leak細節我會使用:

leak.c
1
$ valgrind --leak-check=full --show-leak-kinds=all --verbose 你的程式檔

這些選項應該是可以望文生義,所以就不解釋了。想知道細節可以問男人man valgrind

執行結果

  • 節錄重點,我們可以看到valgrind不但找出洩漏多少空間,也明確地列出未釋放的記憶體被分配時的callstack,真是佛心來者。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ valgrind --leak-check=full --show-leak-kinds=all --verbose
...
==26518== HEAP SUMMARY:
==26518==     in use at exit: 128 bytes in 3 blocks
==26518==   total heap usage: 4 allocs, 1 frees, 136 bytes allocated
==26518==
==26518== Searching for pointers to 3 not-freed blocks
==26518== Checked 78,136 bytes
==26518==
==26518== 14 bytes in 1 blocks are definitely lost in loss record 1 of 3
==26518==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26518==    by 0x4EC02B9: strdup (strdup.c:42)
==26518==    by 0x400687: leak2 (leak.c:20)
==26518==    by 0x40070C: main (leak.c:48)
==26518==
==26518== 14 bytes in 1 blocks are definitely lost in loss record 2 of 3
==26518==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26518==    by 0x4EC02B9: strdup (strdup.c:42)
==26518==    by 0x4006E2: leak3 (leak.c:37)
==26518==    by 0x400732: main (leak.c:53)
==26518==
==26518== 100 bytes in 1 blocks are definitely lost in loss record 3 of 3
==26518==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26518==    by 0x40065E: leak1 (leak.c:11)
==26518==    by 0x400707: main (leak.c:47)
==26518==
==26518== LEAK SUMMARY:
==26518==    definitely lost: 128 bytes in 3 blocks
==26518==    indirectly lost: 0 bytes in 0 blocks
==26518==      possibly lost: 0 bytes in 0 blocks
==26518==    still reachable: 0 bytes in 0 blocks
==26518==         suppressed: 0 bytes in 0 blocks
  • 詳細結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
$ CFLAGS= -g make leak
cc  -g    leak.c   -o leak

$ valgrind --leak-check=full --show-leak-kinds=all --verbose ./leak
==26518== Memcheck, a memory error detector
==26518== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26518== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==26518== Command: ./leak
==26518==
--26518-- Valgrind options:
--26518--    --leak-check=full
--26518--    --show-leak-kinds=all
--26518--    --verbose
--26518-- Contents of /proc/version:
--26518--   Linux version 3.13.0-40-generic (buildd@comet) (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ) #69-Ubuntu SMP Thu Nov 13 17:53:56 UTC 2014
--26518-- Arch and hwcaps: AMD64, amd64-cx16-rdtscp-sse3-avx
--26518-- Page sizes: currently 4096, max supported 4096
--26518-- Valgrind library directory: /usr/lib/valgrind
--26518-- Reading syms from /home/wen/work/practice/Linux_Programming_Practice/15_leak/leak
--26518-- Reading syms from /lib/x86_64-linux-gnu/ld-2.19.so
--26518--   Considering /lib/x86_64-linux-gnu/ld-2.19.so ..
--26518--   .. CRC mismatch (computed 4cbae35e wanted 8d683c31)
--26518--   Considering /usr/lib/debug/lib/x86_64-linux-gnu/ld-2.19.so ..
--26518--   .. CRC is valid
--26518-- Reading syms from /usr/lib/valgrind/memcheck-amd64-linux
--26518--   Considering /usr/lib/valgrind/memcheck-amd64-linux ..
--26518--   .. CRC mismatch (computed 37cdde19 wanted adc367dd)
--26518--    object doesn't have a symbol table
--26518--    object doesn't have a dynamic symbol table
--26518-- Scheduler: using generic scheduler lock implementation.
--26518-- Reading suppressions file: /usr/lib/valgrind/default.supp
==26518== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-26518-by-wen-on-???
==26518== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-26518-by-wen-on-???
==26518== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-26518-by-wen-on-???
==26518==
==26518== TO CONTROL THIS PROCESS USING vgdb (which you probably
==26518== don't want to do, unless you know exactly what you're doing,
==26518== or are doing some strange experiment):
==26518==   /usr/lib/valgrind/../../bin/vgdb --pid=26518 ...command...
==26518==
==26518== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==26518==   /path/to/gdb ./leak
==26518== and then give GDB the following command
==26518==   target remote | /usr/lib/valgrind/../../bin/vgdb --pid=26518
==26518== --pid is optional if only one valgrind process is running
==26518==
--26518-- REDIR: 0x4019ca0 (strlen) redirected to 0x38068331 (???)
--26518-- Reading syms from /usr/lib/valgrind/vgpreload_core-amd64-linux.so
--26518--   Considering /usr/lib/valgrind/vgpreload_core-amd64-linux.so ..
--26518--   .. CRC mismatch (computed 329d6860 wanted c0186920)
--26518--    object doesn't have a symbol table
--26518-- Reading syms from /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so
--26518--   Considering /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so ..
--26518--   .. CRC mismatch (computed 1fb85af8 wanted 2e9e3c16)
--26518--    object doesn't have a symbol table
==26518== WARNING: new redirection conflicts with existing -- ignoring it
--26518--     old: 0x04019ca0 (strlen              ) R-> (0000.0) 0x38068331 ???
--26518--     new: 0x04019ca0 (strlen              ) R-> (2007.0) 0x04c2e1a0 strlen
--26518-- REDIR: 0x4019a50 (index) redirected to 0x4c2dd50 (index)
--26518-- REDIR: 0x4019c70 (strcmp) redirected to 0x4c2f2f0 (strcmp)
--26518-- REDIR: 0x401a9c0 (mempcpy) redirected to 0x4c31da0 (mempcpy)
--26518-- Reading syms from /lib/x86_64-linux-gnu/libc-2.19.so
--26518--   Considering /lib/x86_64-linux-gnu/libc-2.19.so ..
--26518--   .. CRC mismatch (computed e7228afa wanted 93ff6981)
--26518--   Considering /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.19.so ..
--26518--   .. CRC is valid
--26518-- REDIR: 0x4ec47e0 (strcasecmp) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26518-- REDIR: 0x4ec6ad0 (strncasecmp) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26518-- REDIR: 0x4ec3fb0 (memcpy@GLIBC_2.2.5) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26518-- REDIR: 0x4ec2240 (rindex) redirected to 0x4c2da30 (rindex)
--26518-- REDIR: 0x4eba1d0 (malloc) redirected to 0x4c2ab10 (malloc)
--26518-- REDIR: 0x4ec0540 (strlen) redirected to 0x4c2e0e0 (strlen)
--26518-- REDIR: 0x4ec9200 (__GI_memcpy) redirected to 0x4c2fc90 (__GI_memcpy)
--26518-- REDIR: 0x4ecb540 (strchrnul) redirected to 0x4c319b0 (strchrnul)
Hey Hey Hey!
Hey Hey Hey!
--26518-- REDIR: 0x4eba870 (free) redirected to 0x4c2bd80 (free)
==26518==
==26518== HEAP SUMMARY:
==26518==     in use at exit: 128 bytes in 3 blocks
==26518==   total heap usage: 4 allocs, 1 frees, 136 bytes allocated
==26518==
==26518== Searching for pointers to 3 not-freed blocks
==26518== Checked 78,136 bytes
==26518==
==26518== 14 bytes in 1 blocks are definitely lost in loss record 1 of 3
==26518==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26518==    by 0x4EC02B9: strdup (strdup.c:42)
==26518==    by 0x400687: leak2 (leak.c:20)
==26518==    by 0x40070C: main (leak.c:48)
==26518==
==26518== 14 bytes in 1 blocks are definitely lost in loss record 2 of 3
==26518==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26518==    by 0x4EC02B9: strdup (strdup.c:42)
==26518==    by 0x4006E2: leak3 (leak.c:37)
==26518==    by 0x400732: main (leak.c:53)
==26518==
==26518== 100 bytes in 1 blocks are definitely lost in loss record 3 of 3
==26518==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26518==    by 0x40065E: leak1 (leak.c:11)
==26518==    by 0x400707: main (leak.c:47)
==26518==
==26518== LEAK SUMMARY:
==26518==    definitely lost: 128 bytes in 3 blocks
==26518==    indirectly lost: 0 bytes in 0 blocks
==26518==      possibly lost: 0 bytes in 0 blocks
==26518==    still reachable: 0 bytes in 0 blocks
==26518==         suppressed: 0 bytes in 0 blocks
==26518==
==26518== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
==26518== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

參考資料

  • $ man valgrind

[Debian套件打包] Pbuilder 中如何安裝自己打包的套件

| Comments

標題有點難下。直接講為什麼會有這樣的需求好了,在這邊介紹了使用pbuilder驗證單獨的套件。但是如果你要打包的套件需要先安裝另外一個自行先打包的套件就GG了。原因是因為pbuilder不知道去那邊找相依的套件以致於無法編譯你的套件。

舉例來說,假設你現在要打包一個套件是顯示圖片的程式,我們把他叫pkg_image。而這個pkg_image套件使用了lib_img這套函式庫開發。不巧的是lib_img並不在debian官方套件中,所以你必須要先打包lib_imglib_img-devlib_dbg這三個套件。接下來你要確定lib_img-dev套件安裝到你的系統中,你才能夠開始打包pkg_img

這樣的狀況在pbuilder會有前面講到的套件相依問題。後來在Debian文件的PbuilderTricks中找到解法,整理如下。

複習 & 事先準備

首先我們依照Ubuntu的文件PbuilderHowto中的建議,安裝下面的套件。這部份應該和前面有重複。

1
sudo apt-get install pbuilder debootstrap devscripts

其中devscripts提供打包debian套件所需要的script,如前面提到的debuild。而debootstrap則是可以讓你在系統中安裝並執行另外一個debian Linux。在使用時可以指定要裝debian的版本,至於debootstrap支援那些版本?你可以用下面的方式查詢: ls /usr/share/debootstrap/scripts

如果你準備打包並上傳到debian官方套件管理庫,那麼你還要安裝debian 的GPG key,指令如下:

1
sudo apt-get install debian-archive-keyring

接下來我們要建立一個待測Debian的基本image。我使用

1
2
3
4
sudo pbuilder create --distribution sid \
                     --mirror http://ftp.tw.debian.org/debian \
                     --debootstrapopts \ 
                     "--keyring=/usr/share/keyrings/debian-archive-keyring.gpg --variant=buildd"

這個指令表示

  • 我要針對下建立基於debian sid的base.tgz檔案: --distribution sid
  • 指定mirror site: --mirror http://ftp.tw.debian.org/debian
  • 傳遞下面的參數給debootstrap: --debootstrapopts
    • 使用debian archive的gpg key 來驗證下載的套件--keyring=/usr/share/keyrings/debian-archive-keyring.gpg
    • 除了基本的套件外,請幫我額外安裝編譯軟體需要的相關套件 (–variant=buildd)

修改pbuilder config檔案

建立了base.tgz後。還要更改pbuilder的設定以及加入hook。請編輯/etc/pbuilderrc,或是你執行的pbuilder身份home目錄下的~/.pbuilderrc。主要是描述你的local repository放在那邊,以及hook放在那邊。local repository就是你自己打包的套件要放的地方,以前面的例子,就是要放lib_imag.deb和lib_imag-dev.deb到該目錄。而hook可以比喻成/etc/rc.d內的東西,基本上就是在該目錄寫一些script,當pbuilder啟動後就會依順序呼叫。

/etc/pbuilder.c
1
2
3
4
OTHERMIRROR="deb [trusted=yes] file:///path/to/the/dir/deps ./"
BINDMOUNTS="/path/to/the/dir/deps"
HOOKDIR="/path/to/hook/dir"
EXTRAPACKAGES="apt-utils"

請確認內容中的每個路徑都存在,還有[trusted=yes]有被加入。沒加這個在pbuilder安裝自己local套件會因為安全條件不滿足而無法安裝。

寫完設定檔後,記得要告訴pbuilder你已經更新了設定。指令如下

/etc/pbuilder.c
1
sudo pbuilder --update --override-config --distribution sid

接下來就是在HOOKDIR指定的目錄中填寫hook的內容,debian trick網頁是使用D05deps,我也是照抄。

D05deps
1
2
3
#!/bin/sh
(cd /path/to/the/dir/deps; apt-ftparchive packages . > Packages)
apt-get update

這個hook就是切換到你存放自己打包deb套件的目錄,然後產生出apt看得懂的套件列表到Packages檔案,最後就是更新apt。所以每次該目錄有新的deb套件時,都可以確保pbuilder一定可以使用。

套用並測試

完全和前面一樣,pbuilder –build 一個dsc檔。以前面的例子,我們會依照下面的順序打包

  • pbuilder --build lib_img.dsc打包lib_imglib_img-devlib_img-dbg這三個套件。
  • lib_imglib_img-devlib_img-dbg搬到你給pbuilder吃的local repository目錄
  • pbuilder --build pkg_image.dsc,pbuilder會自動從local repository中找出並安裝相依套件。

參考資料

單人本地長期旅遊心得分享

| Comments

分類

住宿

  • 換洗衣物以快乾為考量。
  • 節省旅費可以住青年旅社(背包客棧),收費約新台幣五百元上下。一般的平價旅社價位約一千上下,所以兩人出遊選擇青年旅社不一定比較省錢。
    • 青年旅社不一定提供盥洗用品,請自備牙刷牙膏毛巾。
  • 如果找到旅館不是整棟的,很有機會可以使用免費的烘衣設備。
    • 推測是整棟通常有自備曬衣場,但是只有幾層的就有沒有足夠的空間晾衣服。

行李

  • 自備牙刷牙膏的話,牙膏可以使用兒童牙膏節省空間。
  • 建議自備延長線,以防止插座不夠和插座離床位太遠。
  • 電器類物品放入厚的布質購物袋後再放厚的塑膠袋,一方面防止插頭刺破袋子、一方面也防水。
  • 確認沒有需要參加極度正式的場合的話,穿有護趾的運動涼鞋可以省去襪子,並且下水也比較方便。
  • 建議攜帶藥品:綠油精、止痛藥、止瀉藥、面速力達姆(胯下摩擦紅腫疼痛用)。
  • 可以攜帶附剪刀的原廠瑞士刀,這東西處理指甲和鼻毛很好用。
  • 連續下整天又要騎車的時候,什麼樣的雨具效果都有限。多準備幾個塑膠袋,當遇到長時間下雨時,就把貴重物品,皮夾,手機等東西放入塑膠袋內防水。

外出

  • 防曬一定要做,有寒流來被曬傷的經驗。
  • 隨身攜帶水壺。
  • 可以攜帶環保餐具減少垃圾。
  • 機車旅行的話油料必須考量,不是所有地方都有加油站的。

其他

  • 好奇心、同理心、不要打擾人。
  • 建議參觀當地市場看看本地人的生活型態以及食物。