history

青木日記 RSS

<前の日 | この月 | 次の日>

2004-09-26

HDD 交換

ようやく HDD を換えられた。 買ったのは Seagate の ST3120026A (ATA 120GB)。 二つあった HDD を一つにまとめられてスッキリした。

しかし、Windows だとどんなにでかい HDD でも 平気で一つのパーティションにするんだけど、 Linux だとなぜか細かく区切らないと気がすまないんだよな。

~ % df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/hda5             1.7G   88M  1.5G   6% /
none                   64M  4.0k   63M   1% /tmp
/dev/hda6              16G  2.7G   13G  17% /usr
/dev/hda7              18G  7.4G   10G  43% /home
/dev/hda8              64G  9.7G   51G  16% /d
/dev/hda9             9.2G  2.6G  6.1G  30% /var/backups
/dev/hda1              99M  3.5M   90M   4% /boot

げ、/home が小さすぎたか。

(01:51)

printfのアドレス

Solaris には pmap てコマンドがあって、 プロセスのメモリマップを表示してくれる。 これがいいなーと思ってたんだけど、 Linux の procps をアップデートしたら pmap が入っていた。 そこでさっそくいろいろ実行してみる。 ついでにいろいろなアドレスも表示しとく。

~ % ./src/show-vmmap
extern global   = 08049bf8
static global   = 08049bf4
function static = 08049bf0
function local  = bffff608
malloc mem      = 40140010
alloca mem      = bffff484
main()          = 080486d0
printf()        = 080485a0
dl sin()        = 40251360
1893:   ./src/show-vmmap
08048000      4K r-x--  /show-vmmap        ← printf()
08049000      4K rw---  /show-vmmap
40000000     76K r-x--  /ld-2.2.5.so
40013000      4K rw---  /ld-2.2.5.so
40014000      4K rw---    [ anon ]
4001b000      8K r-x--  /libdl-2.2.5.so
4001d000      4K rw---  /libdl-2.2.5.so
4001e000   1116K r-x--  /libc-2.2.5.so
40135000     24K rw---  /libc-2.2.5.so
4013b000   1048K rw---    [ anon ]
40248000    128K r-x--  /libm-2.2.5.so
40268000      4K rw---  /libm-2.2.5.so
bfffe000      8K rwx--    [ stack ]
 total     2432K

まあだいたいは明らかっていうかあたりまえなんだけど、 興味を引かれたのが printf のアドレス。 よく見ると libc ではなくて show-vmmap の テキスト領域にあることになっている。

それでは Linux/Alpha はどうかと言うと、

dpw600au:~ % ./show-vmmap
extern global   = 0000000120011278
static global   = 0000000120011274
function static = 0000000120011270
function local  = 000000011ffff5f0
malloc mem      = 00000200001d8018
alloca mem      = 000000011ffff5b8
main()          = 0000000120000960
printf()        = 00000200000bcb60
dl sin()        = 0000020000303e80
 
(pmapがなかったんで/proc/XXX/mapsから)
000000011fffe000-0000000120000000 rwxp
0000000120000000-0000000120002000 r-xp /home/aamine/c/linuxintro/src/show-vmmap
0000000120010000-0000000120012000 rwxp /home/aamine/c/linuxintro/src/show-vmmap
0000020000000000-000002000001e000 r-xp /lib/ld-2.2.5.so
000002000001e000-0000020000022000 rw-p
000002000002c000-000002000002e000 rwxp /lib/ld-2.2.5.so
000002000002e000-0000020000032000 r-xp /lib/libdl-2.2.5.so
0000020000032000-000002000003e000 ---p /lib/libdl-2.2.5.so
000002000003e000-0000020000042000 rwxp /lib/libdl-2.2.5.so
0000020000042000-00000200001b4000 r-xp /lib/libc-2.2.5.so   ← ここ
00000200001b4000-00000200001c2000 ---p /lib/libc-2.2.5.so
00000200001c2000-00000200001d2000 rwxp /lib/libc-2.2.5.so
00000200001d2000-00000200001d8000 rwxp
00000200001d8000-00000200002da000 rw-p
00000200002da000-0000020000364000 r-xp /lib/libm-2.2.5.so
0000020000364000-000002000036a000 ---p /lib/libm-2.2.5.so
000002000036a000-0000020000378000 rwxp /lib/libm-2.2.5.so

libc のテキスト領域にあるように見える。普通だ。 もしかして位置独立だとこうなるのかなー (Alpha はデフォルトで PIC だから) と思って i686 のほうを -fPIC でコンパイルしてみたんだけど、 変化はないようだ。

ふーん。何かスタブが入ってるのかねえ。 OK, ディスアセンブルだ。

% objdump -d show-vmmap
                     (略)
080484f0 <.plt>:
 80484f0:       ff 35 90 9b 04 08       pushl  0x8049b90
 80484f6:       ff 25 94 9b 04 08       jmp    *0x8049b94
 80484fc:       00 00                   add    %al,(%eax)
 80484fe:       00 00                   add    %al,(%eax)
 8048500:       ff 25 98 9b 04 08       jmp    *0x8049b98
 8048506:       68 00 00 00 00          push   $0x0
 804850b:       e9 e0 ff ff ff          jmp    80484f0 <_init+0x28>
 8048510:       ff 25 9c 9b 04 08       jmp    *0x8049b9c
 8048516:       68 08 00 00 00          push   $0x8
 804851b:       e9 d0 ff ff ff          jmp    80484f0 <_init+0x28>
 8048520:       ff 25 a0 9b 04 08       jmp    *0x8049ba0
 8048526:       68 10 00 00 00          push   $0x10
 804852b:       e9 c0 ff ff ff          jmp    80484f0 <_init+0x28>
 8048530:       ff 25 a4 9b 04 08       jmp    *0x8049ba4
                     (略)
 8048596:       68 48 00 00 00          push   $0x48
 804859b:       e9 50 ff ff ff          jmp    80484f0 <_init+0x28>
 80485a0:       ff 25 c0 9b 04 08       jmp    *0x8049bc0    ← ここに飛ぶ
 80485a6:       68 50 00 00 00          push   $0x50
 80485ab:       e9 40 ff ff ff          jmp    80484f0 <_init+0x28>
 80485b0:       ff 25 c4 9b 04 08       jmp    *0x8049bc4
 80485b6:       68 58 00 00 00          push   $0x58

printf は 080485a0 なので、いきなり jmp 命令か。 えーと、

jmp    *0x8049bc0

って何だろ。あ、そうか、 「ポインタ 0x8049bc0 をデリファレンスしたアドレスに飛ぶ」だな。 0x8049bc0 は show-vmmap の BSS 領域だから、 ld.so がここに printf 本体のアドレスを置いてるんだろう。 OK, 自力で printf 本体をゲットするぜ。

こんな感じかな?

~ % cat x86resolve.c
#include <stdio.h>
#include <stdlib.h>
 
int
main(int argc, char *argv[])
{
    typedef int (*printf_t)(const char *fmt, ...);
    printf_t myprintf;
    char *p;
 
    p = (char*)printf;
    p += 2;   /* skip instruction */
    printf("BSS address     = %08lx\n", (unsigned long)(*(long*)p));
    myprintf = (printf_t)(*(long*)(*(long*)p));
    printf("printf() body   = %08lx\n", (unsigned long)myprintf);
    myprintf("test %s\n", "OK");
 
    exit(0);
}
~ % ./x86resolve
BSS address     = 08049648
printf() body   = 4006f8ec
test OK

おおっ。うまくいってしまった!

というか .plt って何の略やねん。 …… ld.so(1) によると Procedure Linkage Table らしい。 なるほどね。

このへんまで書いたところでずっと詳しいページを見付けた。

こうなると問題にすべきは 「なんで x86 では libc のテキスト領域を指していないか」 じゃなくて、 「なんで Alpha では libc のテキスト領域を指しているか」 のような気がするな。

……

そうか。 Alpha は最初からグローバルポインタテーブルを使ってるせいだ。 もともと間接参照してるんだからもう一段入れる必要はない。

(01:57)

a

なぜわたしは突然 Alpha アセンブラを書き始めているのでしょうか

        .file   1 "put.c"
        .set noat
        .set noreorder
.section        .rodata
$LC0:
        .ascii "Hello, World!\12\0"
.text
        .align 5
        .globl main
        .ent main
main:
        .mask 0x4000000,-16
        ldah    $29,9($27)
        lda     $29,4896($29)
$main..ng:
        lda $30,-16($30)
        lda $17,$LC0
        lda $16,1
        lda $18,15
        lda $0,4
        callsys
        stq $26,0($30)
        .prologue 1
        mov $31,$16
        lda $16,3
        lda $0,1
        ldq $26,0($30)
        nop
        lda $30,16($30)
        ret $31,($26),1
        .end main

だめだ落ちる……。 とりあえず gcc のインラインアセンブラから始めて _exit(2) は呼べるようになったんだけど、 write(2) を呼ぼうとした瞬間に落ちた。 gp (global pointer) にまともな値が入ってないから、 リテラル (BSS) を参照した瞬間に落ちるようだ。

くそう、BSS がだめならスタックに積んでくれるわ!

~ % cat put.s
        .set noreorder
.text
        .globl main
        .ent main
main:
        lda $16,10              ; a0 ← '\n'
        stb $16,0($30)          ; push a0
        lda $16,97              ; a0 ← 'a'
        stb $16,-1($30)         ; push a0
        subq $30,1,$17          ; a1 ← sp
        lda $16,1               ; a0 ← 1 (STDOUT_FILENO)
        lda $18,2               ; a2 ← 2
        lda $0,4                ; v0 ← 4 (NR_write)
        callsys
        lda $16,7               ; a0 ← 7
        lda $0,1                ; v0 ← 1 (NR_exit)
        callsys
        .end main
~ % gcc -static -nostdlib -Wl,--entry=main put.s -o put
~ % ./put
a
~ % echo $?
7

うごいた!

(02:59)

名前
メールアドレス

<前の日 | この月 | 次の日>
2002|04|05|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|04|05|06|09|10|
2009|07|
2010|09|

Copyright (c) 2002-2007 青木峰郎 / Minero Aoki. All rights reserved. LIRS