プロフィール

kosaki

Author:kosaki
連絡先はコチラ

ブログ検索
最近の記事
最近のコメント
最近のトラックバック
リンク
カテゴリー
月別アーカイブ
RSSフィード
FC2ブログランキング

スポンサーサイト このエントリーをはてなブックマークに追加

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。


スポンサー広告 | 【--------(--) --:--:--】 | Trackback(-) | Comments(-)

gitの diff でgoto label を除く このエントリーをはてなブックマークに追加

11:00 (peterz) # cat ~/.gitconfig
11:00 (peterz) [diff "default"]
11:00 (peterz) xfuncname = "^[[:alpha:]$_].*[^:]$"

以下の設定でいけるらしい。ふーん


プログラミング | 【2012-06-22(Fri) 02:32:34】 | Trackback:(0) | Comments:(0)

[Linus先生のgit講座] このバグはいつ入ったの? このエントリーをはてなブックマークに追加

> According to specification
> mkdir d; ln -s d a; open("a/", O_NOFOLLOW | O_RDONLY)
> should return success but currently it did return ELOOP. Fix the code to ignore
> O_NOFOLLOW in case the provided path has trailing slashes. This is a regression
> caused by path lookup cleanup patch series.
>
> CC: stable@kernel.org

Hmm? Is this correct? Isn't the bug introduced in this merge window, and
thus not relevant for stable?



Jan KaraがAl ViroのせいでVFSがこわれたぞって修正パッチをポストしたところ、
Linusから、なんで今回のマージウィンドウでマージされたパッチに対する修正が -stableへの
バックポートが必要なのか。というツッコミが。


On Thu, 13 May 2010, Andrew Morton wrote:
>
> > Ah, you're right! I've seen dates in the patches around December so I
> > automatically thought the series went to 2.6.33 but checking git logs and
> > the actual source code of 2.6.33 it went in later. I'm sorry for the
> > confusion.
>
> Yes, it's a bit tricky (for me, at least) to work out "which kernel version did
> that patch go into" via git.
>
> I just keep lots of kernel trees around and poke about with `patch
> --dry-run'. PITA.



まず、Andrew Mortonがバージョン毎のツリーを抱えて、いちいち patch --dry-run で
当ててみるという自身の力業テクを披露

それをみたLinus大先生による、どうやってバージョン特定を簡単にやるかという
git講座が開始される


What I did to double-check was:

git log fs/namei.c

to find the commit series by Al (obviously, you can do it other ways too,
but that was the easy way). Then, when you find the commit just do

git name-rev 1f36f774b22a0ceb7dd33eca626746c81a97b6a5

which gives us

1f36f774b22a0ceb7dd33eca626746c81a97b6a5 tags/v2.6.34-rc1~195^2

ie that commit is reachable from v2.6.34-rc1, not from any stable kernel.

(Or alternatively, use "git describe", and get "v2.6.33-5088-g1f36f77",
which means that it is v2.6.33 plus 5088 commits).

And as usual, there are other ways. One particularly obscure one is to say

git log --tags --source --author=viro fs/namei.c

which basically says "show only commits by viro in fs/namei.c, start from
all tags, and for each commit, show _which_ tag the commit was reached
from". It's not perfect, but it does it in one go. With "--oneline", you'd
have gotten a listing like

3e297b6 v2.6.34-rc3 Restore LOOKUP_DIRECTORY hint handling in final lookup on open()
781b167 v2.6.34-rc2 Fix a dumb typo - use of & instead of &&
1f36f77 v2.6.34-rc2 Switch !O_CREAT case to use of do_last()
def4af3 v2.6.34-rc2 Get rid of symlink body copying
3866248 v2.6.34-rc2 Finish pulling of -ESTALE handling to upper level in do_filp_open()
806b681 v2.6.34-rc2 Turn do_link spaghetty into a normal loop
10fa8e6 v2.6.34-rc2 Unify exits in O_CREAT handling
9e67f36 v2.6.34-rc2 Kill is_link argument of do_last()
...

so you see into which -rc the different patches from Al went.

Linus
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html



超訳

git name-rev 1f36f774b22a0ceb7dd33eca626746c81a97b6a5

みたいに、git name-rev [commit-id] ってやれば

1f36f774b22a0ceb7dd33eca626746c81a97b6a5 tags/v2.6.34-rc1~195^2

こんな感じで、v2.6.34-rc1 で入ったって分かる。
もっと簡単にやるには

git log --tags --source --author=viro --oneline fs/namei.c

とかすると、fs/namei.c ファイルに入ったAl Viroの修正一覧がバージョンと共にずらっとでるよ。

3e297b6 v2.6.34-rc3 Restore LOOKUP_DIRECTORY hint handling in final lookup on open()
781b167 v2.6.34-rc2 Fix a dumb typo - use of & instead of &&
1f36f77 v2.6.34-rc2 Switch !O_CREAT case to use of do_last()
def4af3 v2.6.34-rc2 Get rid of symlink body copying
3866248 v2.6.34-rc2 Finish pulling of -ESTALE handling to upper level in do_filp_open()
806b681 v2.6.34-rc2 Turn do_link spaghetty into a normal loop
10fa8e6 v2.6.34-rc2 Unify exits in O_CREAT handling
9e67f36 v2.6.34-rc2 Kill is_link argument of do_last()
...



プログラミング | 【2010-05-17(Mon) 11:26:17】 | Trackback:(0) | Comments:(0)

prefetch このエントリーをはてなブックマークに追加

http://d.hatena.ne.jp/issei_y/comment?date=20100501§ion=1272668964#c

によると、なんか最近のチェスソフトは手動プリフェッチで高速化とかしてるらしい。へー

面白いのはprefetch乱用ソフトの最右翼だったLinuxはprefetchを削除する方向に話を進めていることである。
最近はIntelがプリフェッチを推奨してないのよ。CPU内蔵の自動プリフェッチのほうが石の内部事情に
詳しいので。
なのでインテルコンパイラがプリフェッチ命令を削除してしまうケースがあるというのはおそらく意図的な
ものじゃないかな。

昔のCPUは自動プリフェッチとか弱かったから意味あったんだろうけど・・・


プログラミング | 【2010-05-15(Sat) 16:29:28】 | Trackback:(0) | Comments:(2)

OpenSSL 1.0.0 released このエントリーをはてなブックマークに追加

http://news.slashdot.org/story/10/03/29/1952215/OpenSSL-100-Released?art_pos=5

リリースされちゃったみたい。0.9系と色々と非互換があるので中の人にはもうちょっと頑張って欲しかったのだが。


プログラミング | 【2010-03-30(Tue) 07:48:34】 | Trackback:(0) | Comments:(0)

Rubyで学ぶx86_64 ABI このエントリーをはてなブックマークに追加

ひさしぶりにプログラミングの話題でも。
現在のRubyのtrunkをx86_64上のFedora12でmake test-allすると8個ぐらいテストが失敗するのだが、その中の1つにこういうエラーがある

  1) Failure:
test_sin(DL::TestDL) [/home/kosaki/linux/ruby/test/dl/test_dl2.rb:95]:
<1.0> expected but was
<1.38523885234213e-309>.


test/dl/test_dl2.rbというのが、なにをしているテストかというと、ようするに以下のようにdlモジュールを使ってlibmのsin関数を呼んでいるわけだ

module DL
class TestDL < TestBase
# TODO: refactor test repetition
def test_sin()
pi_2 = Math::PI/2
cfunc = CFunc.new(@libm['sin'], TYPE_DOUBLE, 'sin')
x = cfunc.call([pi_2].pack("d").unpack("l!*"))
assert_equal(Math.sin(pi_2), x)
end
end
end # module DL


dlモジュールというのは(僕は知らなかったのだが)RubyからCのコードを直接呼ぶためのしくみで、引数をpackとunpackを使ってlongの配列にしておくと、なぜか関数の型に関わらず正しい結果がかえって来るという魔法のライブラリである。

まあ sin(π/2)が1.0にならなかったら、バグだよね。


では、その実装をみていこう


static VALUE
rb_dlcfunc_call(VALUE self, VALUE ary)
{
struct cfunc_data *cfunc;
int i;
DLSTACK_TYPE stack[DLSTACK_SIZE];
VALUE result = Qnil;

rb_secure_update(self);

memset(stack, 0, sizeof(DLSTACK_TYPE) * DLSTACK_SIZE);
Check_Type(ary, T_ARRAY);

TypedData_Get_Struct(self, struct cfunc_data, &dlcfunc_data_type, cfunc);

if( cfunc->ptr == 0 ){
rb_raise(rb_eDLError, "can't call null-function");
return Qnil;
}

for( i = 0; i < RARRAY_LEN(ary); i++ ){
if( i >= DLSTACK_SIZE ){
rb_raise(rb_eDLError, "too many arguments (stack overflow)");
}
rb_check_safe_obj(RARRAY_PTR(ary)[i]);
stack[i] = NUM2LONG(RARRAY_PTR(ary)[i]);
}


まず、RubyのArray(ary)をCのArray(stack[])に変換する。


switch( cfunc->type ){
case DLTYPE_VOID:
#define CASE(n) case n: { \
DECL_FUNC_CDECL(f,void,DLSTACK_PROTO##n) = cfunc->ptr; \
f(DLSTACK_ARGS##n(stack)); \
result = Qnil; \
}
CALL_CASE;
#undef CASE
break;
・・・

case DLTYPE_DOUBLE:
#define CASE(n) case n: { \
DECL_FUNC_CDECL(f,double,DLSTACK_PROTO##n) = cfunc->ptr; \
double ret; \
ret = f(DLSTACK_ARGS##n(stack)); \
result = rb_float_new(ret); \
}
CALL_CASE;
#undef CASE


んで、CFunc.newで渡したtypeに応じてクソ長い switch caseが始まる。ここでさらに引数の数に応じて場合分けしたいが最大引数数がrubyのdlモジュールの仕様上20個もあるため、いちいち書くのがめんどくさいのでCASE, CALLCASEマクロで量産している。CALL_CASEマクロの定義は以下


#define CALL_CASE switch( RARRAY_LEN(ary) ){ \
CASE(0); break; \
CASE(1); break; CASE(2); break; CASE(3); break; CASE(4); break; CASE(5); break; \
CASE(6); break; CASE(7); break; CASE(8); break; CASE(9); break; CASE(10);break; \
CASE(11);break; CASE(12);break; CASE(13);break; CASE(14);break; CASE(15);break; \
CASE(16);break; CASE(17);break; CASE(18);break; CASE(19);break; CASE(20);break; \
default: rb_raise(rb_eArgError, "too many arguments"); \
}


というわけで、cfunc.call()すると


DECL_FUNC_CDECL(f,double,DLSTACK_PROTO##n) = cfunc->ptr; \


はCFunc.newでlookupした関数ポインタcfunc->ptr(この場合はsin())をfに代入して、


double ret; \
ret = f(DLSTACK_ARGS##n(stack)); \


fをcallして


result = rb_float_new(ret); \


それをrubyのfloatオブジェクトに変換して返す。ということをしているのだと分かる。実装の詳細を見るために
DLSTACK_PROTO1
DECL_FUNC_CDECL
DLSTACK_ARGS1
のマクロ定義も見ていこう。(あ、今回は引数1つなので##nは1になる。CALL_CASEマクロをもう一度みると納得できると思います。)

まず、DLSTACK_PROTO1とDECL_FUNC_CDECLから見る

#define DLSTACK_TYPE long
#define DLSTACK_PROTO1_ DLSTACK_TYPE
#define DLSTACK_PROTO1 DLSTACK_PROTO1_, ...

#if !defined(FUNC_CDECL)
# define FUNC_CDECL(x) x
#endif
# define DECL_FUNC_CDECL(f,ret,args) ret (FUNC_CDECL(*f))(args)


ようするに DLSTACK_PROTO1 → "long, ..." で結果として

DECL_FUNC_CDECL(f,double,DLSTACK_PROTO##n) = cfunc->ptr; \

という行は

double (*f)(long, ...) = cfunc->ptr;

となる。

わざわざ可変長引数にしている理由は、すぐそばに以下のコメントががが

/*
* Add ",..." as the last argument.
* This is required for variable argument functions such
* as fprintf() on x86_64-linux.
*
* http://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf
* page 19:
*
* For calls that may call functions that use varargs or stdargs
* (prototype-less calls or calls to functions containing ellipsis
* (...) in the declaration) %al is used as hidden argument to
* specify the number of SSE registers used.
*/


つまり、x86_64では...を特別扱いするから、これでうまくいくんだよ。と言いたいようだ。

でもって、DLSTACK_ARGS1は


#define DLSTACK_ARGS1(stack) stack[0]


なので、

f(DLSTACK_ARGS##n(stack)); \



f(stack[0]);

という普通の関数呼び出しになる。

あれ?なんかx86_64対策も入ってるのになんでうまくいかんのだ?という話になるので、参照されているx86_64 ABI
の原典を読む



3.2.3 Parameter Passing

After the argument values have been computed, they are placed in registers, or
pushed on the stack. The way how values are passed is described in the following
sections.

Definitions We first define a number of classes to classify arguments. The
classes are corresponding to AMD64 register classes and defined as:

INTEGER This class consists of integral types that fit into one of the general
purpose registers.
SSE The class consists of types that fits into a SSE register.
SSEUP The class consists of types that fit into a SSE register and can be passed
and returned in the most significant half of it.
X87, X87UP These classes consists of types that will be returned via the x87 FPU.
COMPLEX_X87 This class consists of types that will be returned via the x87 FPU.
NO_CLASS This class is used as initializer in the algorithms. It will be used for
padding and empty structures and unions.
MEMORY This class consists of types that will be passed and returned in memory
via the stack.


超訳。ABI的にはINTEGER, SSE, SSEUP, X87, X87UP, COMPLEX_X87, NO_CLASSという
引数のタイプがあるよ。



Classification The size of each argument gets rounded up to eightbytes.
The basic types are assigned their natural classes:

• Arguments of types (signed and unsigned) _Bool, char, short, int,
long, long long, and pointers are in the INTEGER class.
• Arguments of types float, double and __m64 are in class SSE.
(しばらく略)


超訳
(signed and unsigned) _Bool, char, short, int, long, long long, およびポインタは INTEGERだよ
float, double and __m64 はSSEだよ

(この後、__float128とか構造体とかの説明がつづくが割愛)



Passing Once arguments are classified, the registers get assigned (in left-to-right
order) for passing as follows:

1. If the class is MEMORY, pass the argument on the stack.
2. If the class is INTEGER, the next available register of the sequence %rdi,
%rsi, %rdx, %rcx, %r8 and %r9 is used.
3. If the class is SSE, the next available SSE register is used, the registers are
taken in the order from %xmm0 to %xmm7.
4. (略)
5. (略)


超訳。
MEMORYならスタック渡し確定
INTEGERならrdi, rsi, rdx, rcx, r8, r9の順に使うよ
SSEならSSEレジスタをxmm0からxmm7の順につかうよ


If there is no register available anymore for any eightbyte of an argument, the
whole argument is passed on the stack. If registers have already been assigned for
some eightbytes of this argument, those assignments get reverted.

Once registers are assigned, the arguments passed in memory are pushed on
the stack in reversed (right-to-left) order.


超訳
レジスタが足りなくなったらスタックを使って引数を渡すよ。
スタックには逆順(右から左)でpushするよ


で、ここで問題のセンテンスが現れる

For calls that may call functions that use varargs or stdargs (prototype-less
calls or calls to functions containing ellipsis (. . . ) in the declaration) %al is used
as hidden argument to specify the number of SSE registers used. The contents of
%al do not need to match exactly the number of registers, but must be an upper


超訳
varargsまたはstdargsな関数(プロトタイプがなかったり、宣言で...を使っていたり)を呼ぶときは
%al に何個SSEレジスタを使ったか隠し引数で入れるよ。


つまり、ここではprintf()的なまっとうな可変長引数の事を語っているだけであって、呼び出し元と呼び出し先で
関数の型が適合しない場合については何も語っていない。

今回の場合で言うとsin関数はlibmの中では
double sin(double)
という型でコンパイルされていて、呼び出し側では
double f(long, ...)
という型だと思ってcallしている。

じゃあ、実際に何が起こるかを示すためにテストプログラムを書いてみた。

main.c

#define _GNU_SOURCE
#include
#include

void f1(void) {
double (*func)(long, ...);
double d = 57.0;
long l = *(long*)&d;
double ret;

func = dlsym(RTLD_DEFAULT, "func");
ret = func(l);
printf("%f\n",ret);
}

int main(void)
{
f1();
return 0;
}


f.c

double func(double d)
{
return d*2;
}


実行結果

% gcc *.c -ldl -Wall -g -rdynamic; ./a.out
0.000000


はい、57*2で114 が返ってくるはずが0が返ってきてしまいました。

んではdisassemble,


0000000000400834 :
400834: 55 push %rbp
400835: 48 89 e5 mov %rsp,%rbp
400838: f2 0f 11 45 f8 movsd %xmm0,-0x8(%rbp)
40083d: f2 0f 10 45 f8 movsd -0x8(%rbp),%xmm0
400842: f2 0f 58 c0 addsd %xmm0,%xmm0
400846: c9 leaveq
400847: c3 retq


funcでは素直にxmm0を引数だと思って二倍しています。


00000000004008e0 :
4008e0: 55 push %rbp
4008e1: 48 89 e5 mov %rsp,%rbp
4008e4: 48 83 ec 20 sub $0x20,%rsp
4008e8: 48 b8 00 00 00 00 00 mov $0x404c800000000000,%rax
4008ef: 80 4c 40
4008f2: 48 89 45 e0 mov %rax,-0x20(%rbp)
4008f6: 48 8d 45 e0 lea -0x20(%rbp),%rax
4008fa: 48 8b 00 mov (%rax),%rax
4008fd: 48 89 45 f0 mov %rax,-0x10(%rbp)
400901: b8 48 0b 40 00 mov $0x400b48,%eax
400906: 48 89 c6 mov %rax,%rsi
400909: bf 00 00 00 00 mov $0x0,%edi
40090e: e8 2d fe ff ff callq 400740
400913: 48 89 45 e8 mov %rax,-0x18(%rbp)
400917: 48 8b 45 f0 mov -0x10(%rbp),%rax
40091b: 48 8b 55 e8 mov -0x18(%rbp),%rdx
40091f: 48 89 c7 mov %rax,%rdi
400922: b8 00 00 00 00 mov $0x0,%eax
400927: ff d2 callq *%rdx
400929: f2 0f 11 45 f8 movsd %xmm0,-0x8(%rbp)
40092e: b8 4d 0b 40 00 mov $0x400b4d,%eax
400933: f2 0f 10 45 f8 movsd -0x8(%rbp),%xmm0
400938: 48 89 c7 mov %rax,%rdi
40093b: b8 01 00 00 00 mov $0x1,%eax
400940: e8 db fd ff ff callq 400720
400945: c9 leaveq
400946: c3


でもf1ではrdi(INTEGERの第一引数)に l を、eaxに0を入れて関数コールしています。

つまり、これが起きていることのすべてです。
呼び出し側がrdiに引数を詰んでいるのに、呼び出され側はxmm0を呼んでいるのですから
正しい答えが返るはずがありません。

というわけで、これはdlモジュール制作者のABIの誤読ですね。
なお、Cの規格的には呼び出し元と呼び出し先で関数型が適合しない場合は未定義。たぶん、dlモジュール作者は規格を元に考えたのではなく

1)全CPUアーキテクチャでlongで無理矢理callしても安心と考えた。または
2)x86_32 だけで動けばいいや。と割り切った

のどちらかだと思いますが、イマドキx86_64は普通ですからねぇ。。。

現在のインターフェースだと直しようがないので、RubyのCFuncの仕様を大きく変えないと x86_64対応は出来ないでしょう。
というわけで、RubyユーザのみなさんはCFuncでfloat, doubleを使わないようにしておくと将来幸せになれるかもしれないというお話でした。

ではでは


追記: なぜこの問題がx86_64だけで発生するのかを、つらつらと考えてみたのだが、やはり歴史的な理由だろう。昔は関数宣言を忘れるとか当たり前だったので、昔からあるアーキだと、こういう関数型が不適合な呼び出しに対してABIを寛容に作ることが多かったのではないか?

追記2: 関数の型の適合性とか安易に書いた気がする。でもCの規格を読み直す気がおきない。めどい
簡単にいうと
1)呼び出し元と呼び出し先が両方とも関数宣言有り: 引数、戻り値の型が全部一致していた場合のみOK
2)どちらかが宣言なし: 戻り値の型は厳密一致が必要だけど、引数は型がshortとintみたいに十分近ければOK
みたいなノリだったと思う。今回はケース1だけど、2の基準で反転してもアウトという論外さ



プログラミング | 【2010-01-19(Tue) 23:47:20】 | Trackback:(0) | Comments:(5)
前のページ 次のページ
  1. 無料アクセス解析
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。