Thursday, December 29, 2011

Android adb shell 報錯:more than one device and emulator


資料來源: Android adb shell 報錯:more than one device and emulator

在用adb shell 的時候,提示 more than one device and emulator,怎麼解決辦呢?

1、獲取模擬器/設備列表

adb devices

2、指定device來執行adb shell

adb -s devicename shell

例如:adb -s emulator-5554 shell

在多device的時候,執行adb命令一般都需要用參數-s指定device。例如卸載emulator-5554上的包com.soft.camera

adb -s emulator-5554 uninstall com.soft.camera

Wednesday, December 28, 2011

Android開機啟動分析(一)logo的顯示


Android開機啟動的時候會有一個logo出現,它對應的源代碼位於/system/core/init/目錄下的logo.c中:
下面是我註釋過的代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
  
#include <linux/fb.h>
#include <linux/kd.h>
  
#include "init.h"  
  
#ifdef ANDROID  
#include <cutils/memory.h>
#else  
void android_memset16(void *_ptr, unsigned short val, unsigned count)  
{  
    unsigned short *ptr = _ptr;  
    count >>= 1;  
    while(count--)  
        *ptr++ = val;  
}  
#endif  
  
struct FB {  
    unsigned short *bits;  
    unsigned size;  
    int fd;  
    struct fb_fix_screeninfo fi;  
    struct fb_var_screeninfo vi;  
};  
  
#define fb_width(fb) ((fb)->vi.xres)  
#define fb_height(fb) ((fb)->vi.yres)  
#define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2)  
  
static int fb_open(struct FB *fb)//打開framebuffer設備  
{  
//fd對應設備文件為 /dev/graphics/fb0  
    fb->fd = open("/dev/graphics/fb0", O_RDWR);  
    if (fb->fd < 0)  
        return -1;  
  
    if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0)  
        goto fail;  
    if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0)  
        goto fail;  
//對fd對應的Framebuffer大小進行mmap  
    fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE,   
                    MAP_SHARED, fb->fd, 0);  
    if (fb->bits == MAP_FAILED)  
        goto fail;  
  
    return 0;  
  
fail:  
    close(fb->fd);  
    return -1;  
}  
  
static void fb_close(struct FB *fb)//關閉framebuffer  
{  
    munmap(fb->bits, fb_size(fb));  
    close(fb->fd);  
}  
  
/* there's got to be a more portable way to do this ... */  
static void fb_update(struct FB *fb)  
{  
    fb->vi.yoffset = 1;  
    ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);  
    fb->vi.yoffset = 0;  
    ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);  
}  
//設置終端顯示模式:Graphics or Text?  
static int vt_set_mode(int graphics)  
{  
    int fd, r;  
    fd = open("/dev/tty0", O_RDWR | O_SYNC);  
    if (fd < 0)  
        return -1;  
//設置圖片還是文字  
    r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT));  
    close(fd);  
    return r;  
}  
  
/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */  
  
int load_565rle_image(char *fn)  
{  
    struct FB fb;  
    struct stat s;  
    unsigned short *data, *bits, *ptr;  
    unsigned count, max;  
    int fd;  
  
    if (vt_set_mode(1)) //打開Graphics顯示模式  
        return -1;  
  
//判斷是否能夠打開INIT_IMAGE_FILE  
    fd = open(fn, O_RDONLY);  
    if (fd < 0) {  
        ERROR("cannot open '%s'/n", fn);  
//如果不行就恢復Text顯示模式  
        goto fail_restore_text;  
    }  
  
    if (fstat(fd, &s) < 0) {  
        goto fail_close_file;  
    }  
/*在用戶空間調用mmap,相當於在進程中開闢了文件的存儲IO映射 
  data為返回的映射地址,把圖片文件內容映射到存儲區 
*/  
    data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);  
    if (data == MAP_FAILED)  
        goto fail_close_file;  
  
    if (fb_open(&fb))  
        goto fail_unmap_data;  
  
    max = fb_width(&fb) * fb_height(&fb);  
    ptr = data;  
    count = s.st_size;  
    bits = fb.bits;  
    while (count > 3) {  
        unsigned n = ptr[0];  
        if (n > max)  
            break;  
//將ptr對應的圖片寫到bits對應的framebuffer中,細節不追究了  
        android_memset16(bits, ptr[1], n << 1);  
        bits += n;  
        max -= n;  
        ptr += 2;  
        count -= 4;  
    }  
  
    munmap(data, s.st_size);  
    fb_update(&fb);  
    fb_close(&fb);  
    close(fd);  
    unlink(fn); //刪除fn ?  
    return 0;  
  
fail_unmap_data:  
//munmap釋放申請的內存映射IO  
    munmap(data, s.st_size);      
fail_close_file:  
    close(fd);  
fail_restore_text:  
    vt_set_mode(0);  
//返回-1,於是就直接顯示"Android"字樣!  
    return -1;  
}

分析如下:
Android開機的第一個進程為init,在它的實現文件,即init.c中,會通過調用函數load_565rle_image(INIT_IMAGE_FILE)來實現開機啟動顯示logo的。而上述這個函數是在同目錄下的logo.c中實現的。
在load_565rle_image()這個主函數中先調用vt_set_mode()來設定終端的顯示方式,默認是使用終端的graphics顯示方式,通過對開/dev/tty0設備文件調用ioctl系統調用來設定,但是如果不能打開load_565rle_image(INIT_IMAGE_FILE)中指定的INIT_IMAGE_FILE圖片文件時,就直接將/dev/tty0設置為Text顯示模式,然後直接返回,執行init.c中後面的代碼(直接向/dev/tty0設備寫入"Android"字樣的logo,這也就是默認的啟動logo,很沒有創意,哈哈!)
但是如果存在INIT_IMAGE_FILE這個圖片文件的話,就會在load_565rle_image中通過調用mmap,將圖片文件映射到當前進程的存儲空間中,接著就又調用fb_open()函數來打開linux framebuufer對應的設備文件 /dev/graphics/fb0 ,並把framebuffer也通過mmap映射到存儲空間,於是就在後面調用android_memset16()來寫入圖片,從android_memset16()這樣函數的參數就可以看出,程序的目的就是要把ptr對應的圖片存儲空間地址所對應的字節,寫到bits對應的framebuffer對應的存儲空間中。寫Framebuffer具體細節就不追究了,可能牽涉到Framebuffer原理的一些知識。
而在init.h文件中:
#define INIT_IMAGE_FILE "/initlogo.rle"
說明了圖片文件的位置和默認的文件名,這樣就可以自定義開機顯示畫面了,哈哈!!!
總結:顯示開機logo,如果使用圖片的話就是通過mmap,將圖片內容寫到Framebuffer存儲映射空間來完成的;而如果只是顯示Text的話,就是直接和/dev/tty0打交道了。分析完畢!!

Friday, December 23, 2011

寫EFI APP,要引用pST和gBS兩個全局量,要include什麼文件?


資料來源: 寫EFI APP,要引用pST和gBS兩個全局量,要include什麼文件?
EFI_STATUS AppEntry(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
{
pST->……;//需要引用什麼文件?
gBS->……;//需要引用什麼文件?
}
新手請大家多多指教,非常感謝!

Ans:
AppEntry中的第二個參數Systemtable就是你要用的pST,你可以給pST賦值(pST  = SystemTable),gBS = SystemTable->BootServices)
UDK: #include <Library/UefiBootServicesTableLib.h>

Thursday, December 22, 2011

Coding 懶人法 for gvim

Reference: http://www.vim.org/scripts/script.php?script_id=230

自動新增 Brace, 也就是 {} 方法。
Brace Complete for C/C++ : Adds braces after if, else, while
到此網站下載後放到 /usr/share/vim/vim72
再新增下列到 /etc/vim/gvimrc.local 即可
source $VIMRUNTIME/complete.vim

Monday, December 19, 2011

How to call C functions from CPP file

Reference: How to call c function from CPP file

For example

filea.c
int apb(int a, int b)
{
return a+b;
}
filea.h
int apb(int a, int b);
fileb.cpp
#include "filea.h"
int main(void)
{
 return apb(5,1);
}
compiler tells me error:
Error: L6218E: Undefined symbol apb(int, int) (referred from fileb.o).
Solution:
extern "C"
{
#include "filea.h"
}

int main(void)
{
 return apb(5,1);
}

How to extract/compress cpio image? (Android ramdisk.img)

Android ramdisk.img 是一個由cpio封裝,然後使用gzip壓縮的檔案, 我們可以輕易的透過以下的
指令解開ramdisk.img:


# cat ramdisk.img | gzip -d | cpio -i
同樣的要是我們修改到root file system裡面的東西,也可以透過以下的指令包裝回ramdisk.img:


# find . | cpio -o -H newc | gzip > ramdisk.img

Saturday, December 17, 2011

MBR structure

——————————————————————–
|位移值|大小 | 說明                                                                          |
+——+———————————————————–+
|  00  |Byte | BOOT ID – 若為可開機的分割區則為 80h ~ FFh, 否則為  |
|      |     | 00h。 80h = C, 81h = D, 82h = E … 以此類推        |
|      |     | 以 fdisk 來說,若第一顆硬碟的分割區設為 Active 則 ID |
|      |     | = 80h。 4 個分割表中, 只能有一個被設為可開機, 否則  |
|      |     | 將會發生錯誤。                                      |
+——+—–+—————————————————–+
|  01h |Byte | 此分割開始之磁頭編號                                |
+——+—–+—————————————————–+
|  02h |Byte | 此分割開始之磁區編號 (6 bits)                       |
|      |     | 最高的 2 個 bits(bit6-7), 為磁柱編號的 bit8-9       |
+——+—–+—————————————————–+
|  03h |Byte | 此分割開始之磁柱編號 (10 bits)                      |
|      |     | bit8-9 放在位移值 02h 的 bit 6-7                    |
+——+—–+—————————————————–+
|  04h |Byte | 作業系統識別碼                                      |
|      |     |   00 None                                           |
|      |     |   01 DOS FAT-12 bits                                |
|      |     |   02 XENIX root                                     |
|      |     |   03 XENIX usr                                      |
|      |     |   04 DOS FAT-16 bits < 32M                          |
|      |     |   05 Extended                                       |
|      |     |   06 DOS FAT-16 bits > 32M                          |
|      |     |   07 HPFS/NTFS                                      |
|      |     |   08 AIX                                            |
|      |     |   09 AIX bootable                                   |
|      |     |   0A OS/2 Boot Manager                              |
|      |     |   0B DOS FAT-32 bits (Int 13h extensions)           |
|      |     |   0C DOS FAT Cylinder > 1024 (Int 13h extensions)   |
|      |     |   0E DOS FAT System (Int 13h extensions)            |
|      |     |   0F DOS BigExtended (Int 13h extensions)           |
|      |     |   20 SPF Boot manager                               |
|      |     |   40 Venix 80286                                    |
|      |     |   41 PPC PReP Boot                                  |
|      |     |   51 Novell                                         |
|      |     |   52 Microport                                      |
|      |     |   63 GNU HURD                                       |
|      |     |   64 Novell Netware                                 |
|      |     |   65 Novell Netware                                 |
|      |     |   75 PC/IX                                          |
|      |     |   80 Old MINIX                                      |
|      |     |   81 Linux/MINIX                                    |
|      |     |   82 Linux swap                                     |
|      |     |   83 Linux native                                   |
|      |     |   85 Linux extended                                 |
|      |     |   93 Amoeba                                         |
|      |     |   94 Amoeba BBT                                     |
|      |     |   A5 FreeBSD                                        |
|      |     |   A6 Open BSD                                       |
|      |     |   A7 NETSTEP                                        |
|      |     |   A9 NetBSD                                         |
|      |     |   B7 BSDI fs                                        |
|      |     |   B8 BSDI swap                                      |
|      |     |   C7 Syrinx                                         |
|      |     |   DB CP/M                                           |
|      |     |   E1 DOS access                                     |
|      |     |   E3 DOS R/O                                        |
|      |     |   EB BeOS fs                                        |
|      |     |   F2 DOS secondary                                  |
|      |     |   FF BBT                                            |
+——+—–+—————————————————–+
|  05h |Byte | 此分割結束之磁頭編號                                |
+——+—–+—————————————————–+
|  06h |Byte | 此分割結束之磁區編號 (6 bits)                       |
|      |     | 最高的 2 個 bits(bit6-7), 為磁柱編號的 bit8-9       |
+——+—–+—————————————————–+
|  07h |Byte | 此分割結束之磁柱編號 (10 bits)                      |
|      |     | bit8-9 放在位移值 06h 的 bit 6-7                    |
+——+—–+—————————————————–+
|  08h |DWord| 此分割區前之磁區總數                                |
+——+—–+—————————————————–+
|  12h |DWord| 此分割之磁區總數                                    |
+——+—–+—————————————————–+

Friday, December 16, 2011

Linux內核驅動程序初始化順序的調整

資料來源: Linux內核驅動程序初始化順序的調整

今天在做一個驅動的時候要用到另一個驅動(I2C)提供的API,在內核初始化時碰到了一個依賴問題。

我的驅動在I2C初始化之前就運行起來了,而這時I2C提供的API還處於不可用狀態。查了很多資料,網上有人說所有使用module_init這個宏的驅動程序的起動順序都是不確定的(我沒有查到權威的資料)。

所有的__init函數在區段.initcall.init中還保存了一份函數指針,在初始化時內核會通過這些函數指針調用這些__init函數指針,並在整個初始化完成後,釋放整個init區段(包括.init.text,.initcall.init等)。

注意,這些函數在內核初始化過程中的調用順序只和這裡的函數指針的順序有關,和1)中所述的這些函數本身在.init.text區段中的順序無關。在2.4內核中,這些函數指針的順序也是和鏈接的順序有關的,是不確定的。在2.6內核中,initcall.init區段又分成7個子區段,分別是
.initcall1.init 
.initcall2.init 
.initcall3.init 
.initcall4.init 
.initcall5.init 
.initcall6.init 
.initcall7.init
當需要把函數fn放到.initcall1.init區段時,只要聲明
core_initcall(fn);
即可。
其他的各個區段的定義方法分別是:
core_initcall(fn) --->.initcall1.init 
postcore_initcall(fn) --->.initcall2.init 
arch_initcall(fn) --->.initcall3.init 
subsys_initcall(fn) --->.initcall4.init 
fs_initcall(fn) --->.initcall5.init 
device_initcall(fn) --->.initcall6.init 
late_initcall(fn) --->.initcall7.init


而與2.4兼容的initcall(fn)則等價於device_initcall(fn)。各個子區段之間的順序是確定的,即先調用.initcall1.init中的函數指針,再調用.initcall2.init中的函數指針,等等。而在每個子區段中的函數指針的順序是和鏈接順序相關的,是不確定的。

在內核中,不同的init函數被放在不同的子區段中,因此也就決定了它們的調用順序。這樣也就解決了一些init函數之間必須保證一定的調用順序的問題。按照include/Linux/init.h文件所寫的,我在驅動裡償試了這樣兩種方式:

__define_initcall("7", fn);
late_initcall(fn);
都可以把我的驅動調整到最後調用。實際上上面兩個是一回事:
#define late_initcall(fn) __define_initcall("7", fn)

Thursday, December 15, 2011

zImage,uImage 區別


資料來源: zImage,uImage 區別
對於Linux內核,編譯可以生成不同格式的映像文件,例如:
# make zImage
# make uImage
zImage是ARM Linux常用的一種壓縮映像文件,uImage是U-boot專用的映像文件,它是在zImage之前加上一個長度為0×40的「頭」,說明這個映像文件的類型、加載位置、生成時間、大小等信息。換句話說,如果直接從uImage的0×40位置開始執行,zImage和uImage沒有任何區別。另外,Linux2.4內核不支持uImage,Linux2.6內核加入了很多對嵌入式系統的支持,但是uImage的生成也需要設置。
一、vmlinuz
vmlinuz是可引導的、壓縮的內核。「vm」代表「Virtual Memory」。Linux 支持虛擬內存,不像老的操作系統比如DOS有640KB內存的限制。Linux能夠使用硬盤空間作為虛擬內存,因此得名「vm」。
vmlinuz 的建立有兩種方式。一是編譯內核時通過「make zImage」創建,然後通過:「cp /usr/src/linux-2.4/arch/i386/linux/boot/zImage/boot/vmlinuz」產生。zImage適用於小內核的情況,它的存在是為了向後的兼容性。
二是內核編譯時通過命令make bzImage創建,然後通過:「cp/usr/src/linux-2.4/arch/i386/linux/boot/bzImage /boot/vmlinuz」產生。bzImage是壓縮的內核映像,需要注意,bzImage不是用bzip2壓縮的,bzImage中的bz容易引起誤解,bz表示「big zImage」。 bzImage中的b是「big」意思。 zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip壓縮的。它們不僅是一個壓縮文件,而且在這兩個文件的開頭部分內嵌有 gzip解壓縮代碼。所以你不能用gunzip 或 gzip –dc解包vmlinuz。
二、initrd-x.x.x.img
initrd是「initial ramdisk」的簡寫。initrd一般被用來臨時的引導硬件到實際內核vmlinuz能夠接管並繼續引導的狀態。
initrd 映像文件是使用mkinitrd創建的。mkinitrd實用程序能夠創建initrd映像文件。這個命令是RedHat專有的。其它Linux發行版或許有相應的命令。這是個很方便的實用程序。具體情況請看幫助:man mkinitrd下面的命令創建initrd映像文件。
最後生成的內核鏡像有兩種 zImage 以及 uImage 。其中 zImage 下載到目標板中後,可以直接用 uboot 的命令go 來進行直接跳轉。這時候內核直接解壓啟動。但是無法掛載文件系統,因為 go 命令沒有將內核需要的相關的啟動參數傳遞給內核。傳遞啟動參數我們必須使用命令 bootm 來進行跳轉。 Bootm 命令跳轉只處理 uImage 的鏡像。
uboot 源代碼的 tools/ 目錄下有 mkimage 工具,這個工具可以用來製作不壓縮或者壓縮的多種可啟動映像文件。
mkimage 在製作映像文件的時候,是在原來的可執行映像文件的前面加上一個 0×40 字節的頭,記錄參數所指定的信息,這樣 uboot 才能識別這個映像是針對哪個 CPU 體系結構的,哪個 OS 的,哪種類型,加載內存中的哪個位置, 入口點在內存的那個位置以及映像名是什麼
用法如下:
./mkimage -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
-A ==> set architecture to ‘arch’
-O ==> set operating system to ‘os’
-T ==> set image type to ‘type’
-C ==> set compression type ‘comp’
-a ==> set load address to ‘addr’ (hex)
-e ==> set entry point to ‘ep’ (hex)
-n ==> set image name to ‘name’
-d ==> use image data from ‘datafile’
-x ==> set XIP (execute in place)
參數說明:
-A 指定 CPU 的體系結構:
取值 表示的體系結構
alpha Alpha
arm A RM
x86 Intel x86
ia64 IA64
mips MIPS
mips64 MIPS 64 Bit
ppc PowerPC
s390 IBM S390
sh SuperH
sparc SPARC
sparc64 SPARC 64 Bit
m68k MC68000
-O 指定操作系統類型,可以取以下值:
openbsd 、 netbsd 、 freebsd 、 4_4bsd 、 linux 、 svr4 、 esix 、 solaris 、 irix 、 sco 、 dell 、 ncr 、 lynxos、 vxworks 、 psos 、 qnx 、 u-boot 、 rtems 、 artos
-T 指定映像類型,可以取以下值:
standalone 、 kernel 、 ramdisk 、 multi 、 firmware 、 script 、 filesystem
-C 指定映像壓縮方式,可以取以下值:
none 不壓縮
gzip 用 gzip 的壓縮方式
bzip2 用 bzip2 的壓縮方式
-a 指定映像在內存中的加載地址,映像下載到內存中時,要按照用 mkimage 製作映像時,這個參數所指定的地址值來下載
-e 指定映像運行的入口點地址,這個地址就是 -a 參數指定的值加上 0×40 (因為前面有個 mkimage 添加的0×40 個字節的頭)
-n 指定映像名
-d 指定製作映像的源文件
我在編譯時用到的命令如下:
# make zImage      // 生成 zImage 鏡像
# /usr/local/arm/k9uboot/tools/ mkimage -n ‘Linux 2.4.27′ -A arm -O linux -T
kernel -C none -a 0×20007fc0 -e 0×20008000 -d zImage uImage

Tuesday, December 13, 2011

Linux Kernel oops


資料來源: Linux Kernel oops

每次kernel掛掉總是會出現一堆16進制的東西
但我都沒有認真看過
因為….真的看不懂
不過上星期上完課之後
終於知道這些數字代表什麼
例如main.c裡面有個faulty_write function, 故意讓0的位置寫0
ssize_t faulty_write (struct file *filp, const char __user *buf, size_t count,
loff_t *pos)
{
/* make a simple fault by dereferencing a NULL pointer */
*(int *)0 = 0;
return 0;
}
當我去執行的時候kernel掛掉
$ echo “1″ > /dev/aaa
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c3a70000
[00000000] *pgd=33a7e031, *pte=00000000, *ppte=00000000
Internal error: Oops: 817 [#3]
Modules linked in: faulty
CPU: 0 Tainted: G D (2.6.29.2 #2)
PC is at faulty_write+0×10/0×18 [faulty]
LR is at vfs_write+0xc4/0×148
pc : [<bf00009c>] lr : [<c00a5f6c>] psr: a0000013
sp : c3a69f44 ip : c3a69f54 fp : c3a69f50
r10: 400d0948 r9 : c3a68000 r8 : 00000000
r7 : 00000002 r6 : c3a69f78 r5 : 400d3704 r4 : c382cea0
r3 : c3a69f78 r2 : 00000002 r1 : 400d3704 r0 : 00000000
Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
Control: 0000717f Table: 33a70000 DAC: 00000015
Process echo (pid: 882, stack limit = 0xc3a68260)
Stack: (0xc3a69f44 to 0xc3a6a000)
9f40: c3a69f74 c3a69f54 c00a5f6c bf00009c 00000000 c382cec0 c382cea0
9f60: c3a69f78 00000000 c3a69fa4 c3a69f78 c00a60b0 c00a5eb8 00000000 00000000
9f80: 00000000 400d1188 00000002 400d3704 00000004 c0035fa4 00000000 c3a69fa8
9fa0: c0035e00 c00a6074 400d1188 00000002 00000001 400d3704 00000002 4009f9b4
9fc0: 400d1188 00000002 400d3704 00000002 000a8374 00000002 400d0948 00000000
9fe0: 400a491c bec33d74 4009f008 4008accc 20000010 00000001 fffff7f7 fff5ffdf
Backtrace:
[<bf00008c>] (faulty_write+0×0/0×18 [faulty]) from [<c00a5f6c>] (vfs_write+0xc4/0×148)
[<c00a5ea8>] (vfs_write+0×0/0×148) from [<c00a60b0>] (sys_write+0×4c/0×74)
r7:00000000 r6:c3a69f78 r5:c382cea0 r4:c382cec0
[<c00a6064>] (sys_write+0×0/0×74) from [<c0035e00>] (ret_fast_syscall+0×0/0×2c)
r8:c0035fa4 r7:00000004 r6:400d3704 r5:00000002 r4:400d1188
Code: e1a0c00d e92dd800 e24cb004 e3a00000 (e5800000) 
—[ end trace 0f24d785ce6e74e6 ]—
Segmentation fault
/ $

注意看裡面
PC is at faulty_write+0×10/0×18 [faulty]
Code: e1a0c00d e92dd800 e24cb004 e3a00000 (e5800000)

因為instruction length是4 byte
faulty_write+0×10/0×18 = > 4 / 6 (6行的第四行錯)
在位置e3a00000 的地方

執行mipsel-linux-objdump -S main.o 可以看到組語和程式碼的對應
ssize_t faulty_write (struct file *filp, const char __user *buf, size_t count,
loff_t *pos)
{
8c:     e1a0c00d mov ip, sp
90:     e92dd800 stmdb sp!, {fp, ip, lr, pc}
94:     e24cb004 sub fp, ip, #4 ; 0×4
/* make a simple fault by dereferencing a NULL pointer */
*(int *)0 = 0;
98:     e3a00000 mov r0, #0 ; 0×0
9c:     e5800000 str r0, [r0]
return 0;
}

所以這樣就可以找到哪一個錯誤!

Monday, December 12, 2011

做出自己的back trace function




相信有在用arm linux的, 應該對kernel panic不陌生吧~~ 在你對kernel做了一些無法挽救的錯事後, kernel叫了一聲”Oops~~”,然後就死在路邊~~ 不過幸運的事, 通常kernel會在死之前留下一些”線索”, 好讓你跟隨這些線索找出些端倪…
舉例來說, 我故意在我的init module 裡插入存取非法位址的動作, kernel果然就掛在那邊並且印出:
Unable to handle kernel paging request at virtual address dcc01120
pgd = c0004000
[dcc01120] *pgd=00000000
Internal error: Oops: 805 [#1]
Modules linked in:
CPU: 0
PC is at audio_aic32_init+0×44/0×1cc
LR is at 0×1
pc : [] lr : [<00000001>] Not tainted
sp : c0387fc4 ip : 60000013 fp : c0387fd4
r10: 00000000 r9 : 00000000 r8 : 00000000
r7 : c001f70c r6 : 00000000 r5 : c0386000 r4 : 00000000
r3 : fbbc0000 r2 : e1041128 r1 : 00000001 r0 : e1041120
Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment kernel
Control: 5317F Table: 80004000 DAC: 00000017
Process swapper (pid: 1, stack limit = 0xc0386198)
Stack: (0xc0387fc4 to 0xc0388000)
7fc0: c001f6dc c0387ff4 c0387fd8 c0076290 c001d00c 00000000 00000000
7fe0: 00000000 00000000 00000000 c0387ff8 c008d738 c007620c 00000000 00000000
Backtrace:
[] (audio_aic32_init+0×0/0×1cc) from [] (init+0×94/0×1e0)
r4 = C001F6DC
[] (init+0×0/0×1e0) from [] (do_exit+0×0/0xdc0)
r7 = 00000000 r6 = 00000000 r5 = 00000000 r4 = 00000000
Code: e59f3174 e3a01001 e5801000 e2422d25 (e7801003)
<0>Kernel panic – not syncing: Attempted to kill init!
嘿嘿, 很明顯的kernel就是死在audio_aic32_init,
我們再去仔細看看這個function即可;
這麼說起來, kernel的這個機制還真好用, 能讓我們了解掛掉的時候,
是由哪幾個function call下來的,
那麼…我們有沒有辦法拿來用呢?
有時你會想知道, kernel到底何時, 又是從哪呼叫到這個function;
又有時你會想知道, kernel到底怎麼call到我們function裡的,
我明明只是在結構中加入 .probe= probe_function,
kernel卻能找到我的probe_function, 到底是從哪進入的呢,
當然你也可以一層一行的去追code去printk,
但是如果能善用kernel的這個”線索”function, 想必能省力不少
好, 既然要用這個工具,
讓我們先找看看他被放在kernel source tree的哪裡
用lxr search看看, 發現他是位於/arch/arm/kernel/traps.c
從die開始(但是如何跑到die的呢? 我猜是fault interrupt),接著die()->dump_backtrace()->c_backtrace(),
但是lxr search不到c_backtrace不到, 按照慣例,
應該是一個assembly functon,
去arch/arm/lib/裡找找, 果然找到一個backtrace.S,
c_backtrace就在裡面,
有興趣的可以去看看他是怎麼寫的, 我則只想知道怎麼用它
最簡單的方式就是 看dump_backtrace()是怎麼call c_backtrace()的, 我們跟著做, 這樣應該就能印出資訊吧
在dump_backtrace()裡:
157 static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
158 {
159 unsigned int fp;
160 int ok = 1;
161
162 printk(“Backtrace: “);
163 fp = regs->ARM_fp;
164 if (!fp) {
165 printk(“no frame pointer”);
166 ok = 0;
167 } else if (verify_stack(fp)) {
168 printk(“invalid frame pointer 0x%08x”, fp);
169 ok = 0;
170 } else if (fp < (unsigned long)(tsk->thread_info + 1))
171 printk(“frame pointer underflow”);
172 printk(“\n”);
173
174 if (ok)
175 c_backtrace(fp, processor_mode(regs));
176 }
其中 c_backtrace需要2個參數, 一個是fp,
一個是processor目前的mode
processor 目前的mode倒是比較容易解決,
因為我們driver都是在system mode, 所以傳入固定0×1f即可
至於fp呢, 要取得就比較麻煩啦, 要先get register的值, 我的做法如下
static void load_regs( struct pt_regs *ptr )
{
asm volatile(
“stmia %0, {r0 – r15}\n\t”
:
: “r” (ptr)
: “memory”
);
}
最後則是寫成一個function 讓別人call啦~
void back_trace( void )
{
struct pt_regs *ptr;
unsigned int fp;
unsigned long flags;
ptr = kmalloc( sizeof( struct pt_regs ), GFP_KERNEL);
local_irq_save(flags);
printk(“\n\nstart back trace…”);
load_regs( ptr );
fp = ptr->ARM_fp;
c_backtrace(fp, SYS_MODE);
printk(“back trace end…\n\n”);
local_irq_restore(flags);
kfree ( ptr );
}
EXPORT_SYMBOL( back_trace);
如此, 我們只要在driver中加入back_trace();
就可以知道整個來龍去脈啦
舉個例子, 在audio 的probe function中加入back_trace();
則會印出

start back trace…
[] (back_trace+0×0/0×68) from [] (davinci_aic32_probe+0×3c/0×144)
r5 = C0293EE8 r4 = 00000000
[] (davinci_aic32_probe+0×0/0×144) from [] (audio_probe+0×24/0×2c)
r5 = C02471B4 r4 = C024725C
[] (audio_probe+0×0/0×2c) from [] (driver_probe_device+0×54/0×74)
[] (driver_probe_device+0×0/0×74) from [] (driver_attach+0×54/0×90)
r5 = C024725C r4 = C02471BC
[] (driver_attach+0×0/0×90) from [] (bus_add_driver+0×78/0×120)
r6 = C024725C r5 = C0293E48 r4 = C0243C34
[] (bus_add_driver+0×0/0×120) from [] (audio_register_codec+0xbc/0xe8)
[] (audio_register_codec+0×0/0xe8) from [] (audio_aic32_init+0×14/0×1c4)
r6 = 00000000 r5 = C0386000 r4 = C001F6DC
[] (audio_aic32_init+0×0/0×1c4) from [] (init+0×94/0×1e0)
r4 = C001F6DC
[] (init+0×0/0×1e0) from [] (do_exit+0×0/0xdc0)
r7 = 00000000 r6 = 00000000 r5 = 00000000 r4 = 00000000
back trace end…

如此,整個流程就清清楚楚了~~~

Sunday, December 11, 2011

request_module: runaway loop modprobe binfmt-464c

Reference: request_module: runaway loop modprobe binfmt-464c


Short answer: If you are getting this error right after linux kernel initialization, you are likely booting a 32-bit kernel with a 64-bit OS.

Long answer: If you boot a 32-bit kernel with a 64-bit OS, when the kernel tries to start /sbin/init (a 64-bit binary), it won't recognize the binary format, and it'll try to load the binfmt-464c kernel module, which is ELF support. (ELF support is generally compiled into the kernel, not built as a module, by the way.)

The reason for the loop error is that the kernel is trying to invoke modprobe to load the module, and modprobe is itself an ELF binary, resulting in a recursion loop...

Viewing single commit diff in Git

Reference: Viewing single commit diff in Git


I have started using Git for my projects and am very impressed by it. More about it in later post.

In the mean time, I just want to share about how to view a diff of single commit.

In SVN, this is what I usually do:

svn diff -c ARG



I didn't know the equivalent of it in Git and I used to do this, which is a pain:

git diff [commit]^ [commit]



That is, until I found out about:

git show [commit]



Problem solved.

Thursday, December 08, 2011

線程同步機制小結 1 – Atomic Operation


資料來源: 辛望的開發日誌
線程同步機制有很多種,從概念上大概可以分為以下幾種:
  1. Atomic Operation
  2. Mutex
  3. Spinlock
  4. Condition
  5. Read/Write Lock
  6. Semaphore
這些概念在不同的平台上有不同的實現,某些平台缺少某些概念,如 Java 在 1.5 前沒有 Atomic Operation 和 Semaphore;某些平台將多個概念混合在一個 API 中,如 Win32 的 CriticalSection 混合了 Mutex 和 Spinlock;某些平台在這些概念的基礎上又提出(構建)了一些新的概念。下面對每種概念在每個平台上的實現做一個總結。
1. Atomic Operation
所謂原子操作指的是不會被打斷的操作,一般來說一個原子操作對應一個特殊的 CPU 指令。在所有的線程同步機制裡原子操作是最迅速的,因為完全不需要加鎖。原子操作是實現 Lock-Free 最重要的武器。原子操作中除了常見操作,如「加、減、與、或、非、賦值」操作等,常見的還有 Compare-and-Swap, Test-and-Set, Add/Sub-and-Get 等組合操作。
Java 5 裡新加入的 Concurrent API 包含了一系列的 AtomicXXX 的類。實際上這些類只做了兩件事情:1. 封裝一個 volatile 修飾的變量來保證一般操作(加、減、賦值)是原子的;2. 通過調用系統相關的 API 來實現組合操作。Java 的 volatile 會保證對變量的操作是原子性的,這和 C/C++ 中的 volatile 有很大不同。
在 GCC 中原子操作需要通過特殊的內建函數來實現,參見:http://gcc.gnu.org/onlinedocs/gcc-4.3.0/gcc/Atomic-Builtins.html
Microsoft 將原子操作叫做 Interlocked Operation,參見:http://msdn.microsoft.com/en-us/library/aa911383.aspx。.Net 裡提供的 API 其實就是這些函數的簡單封裝,畢竟 CLR 的 Thread 就是 Win32 的 Thread。
C++ 0x 標準裡也包括了一些列的 Atomic 操作,這裡就不詳細給出。

Linux下使用od查看文件


查看文本文件使用vim/less就足夠了,但如果想要查看一些其他二進制文件(例如DB的數據文件、程序的二進制代碼)則借助於od/hexdump這些工具會很方便,這裡會通過一些實例來詳細介紹od命令的相關參數。
1. od參數詳解
$od -j 49152 -N 38 -w4 -A d -t x1 -v tmp_test.ibd 0049152 fd 6f fb 70 0049156 00 00 00 03 0049160 ff ff ff ff 0049164 ff ff ff ff 0049168 00 00 00 03 0049172 ba 92 ac 0c 0049176 45 bf 00 00 0049180 00 00 00 00 0049184 00 00 00 00
-j 49152 跳過前49152個字節(bytes)
-N 38 僅顯示38個字節
-w4 每一行顯示4個字節
-A d 表示最左側的偏移量按十進制顯示;-A還可以接[doxn],其中d表示十進制,o表示8進制,x表示16進制,n表示不顯示該偏移量,如:
od -j 49152 -N 8 -w4 -A n -t x1 -v ibdata1 b4 0b c7 31 00 00 00 03
-t x1表示如何顯示文件內容。這裡,x表示按照16進制輸出文件內容,x後面跟的「1」表示一次顯示一個字節。下面是一個-t x2的輸出:
od -j 49152 -N 8 -w4 -A n -t x2 -v ibdata1 0bb4 31c7 # 0bb4是十進製表示的兩個字節 0000 0300 #
另外,注意到這裡是0bb4和前面的b40b略有不同,這是因為我這裡的測試平台是little-endian的(x86_64 Linux)。
最後,參數-v表示,即使連續多行都是完全相同的字符0,仍然原樣輸出;不加該參數,則會使用星號(*)跳過多個相同的行。
好了,再回頭看看最前面的命令,已經沒那麼難理解了吧。
參考文獻:man od

Tuesday, December 06, 2011

Sample code to query window name.

Sample code to query window name.

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
static char *window_id_format = "0x%lx";
int main(void)
{
 unsigned int numkids, i, mapped, scrn;
 Window r, p, *kids;
 XWindowAttributes attr;
 Window root;
 Display *dispsy;
 char *win_name;
 XTextProperty text_prop;
 dispsy = XOpenDisplay(0);

 scrn = DefaultScreen(dispsy);
 root = RootWindow(dispsy, scrn);

 mapped = 0;
 XQueryTree(dispsy, root, &r, &p, &kids, &numkids);
 for(i = 0; i < numkids; ++i)
 {
  XGetWindowAttributes(dispsy, kids[i], &attr);
  if(attr.map_state == IsViewable)
  {
   ++mapped;
   printf(window_id_format, kids[i]);
   status = XGetWMName(dispsy, kids[i], &text_prop);
   if(!status || !text_prop.value || !text_prop.nitems)
   {
    printf("GetWMName error\n");
 //   return -1;
   }
   if(!XFetchName(dispsy, kids[i], &win_name))
   {
    printf("(has no name)\n");
    printf(" \n");
   }
   else if(win_name)
   {
    printf("win_name =  %s \n", win_name);
    XFree(win_name);
   }
  }
 }
}