目次: ベンチマーク
昨日(2020年7月4日の日記参照)はSEGVするELFバイナリサイズを92バイトまで削ることができました。
その後、色々弄っていて見つけたのですが、プログラムヘッダのtypeをNULLにしておけば、ファイルサイズやオフセットがめちゃくちゃでもexecveは文句を言わないっぽいみたいです。
ELFヘッダのe_identの後半8バイトは0(前半を書き換えるとELFと認識されなくなります)なので、この部分をプログラムヘッダだよ、と指定すればtype NULLに解釈されます。
これで64バイト、つまりELFヘッダしかない実行ファイルができました。何の役にも立たないですけどね……。
$ ls -la a.out -rwxrwxr-x+ 1 katsuhiro katsuhiro 64 Jul 3 06:39 a.out
これ以上1バイトでも削るとELFヘッダの長さを下回るため、ELFバイナリとして成立しません。よって64バイトが最短だと思いますが、私が気づいていない裏技があるかもしれません。
このファイルを実行してみると、システムコールexecveがエラーを返さないで進むので、ELFファイルとして認識してもらえているようです。
$ strace ./a.out execve("./a.out", ["./a.out"], 0x7ffedf7dfa90 /* 47 vars */) = 0 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x400000001} --- +++ killed by SIGSEGV +++ Segmentation fault
$ readelf -a a.out ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x400000001 Start of program headers: 8 (bytes into file) Start of section headers: 0 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 1 Size of section headers: 0 (bytes) Number of section headers: 0 Section header string table index: 0 There are no sections in this file. There are no sections to group in this file. Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align NULL 0x00000001003e0002 0x0000000400000001 0x0000000000000008 0x0000000000000000 0x0038004000000000 0x1 There is no dynamic section in this file. There are no relocations in this file. The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not curr ently supported. Dynamic symbol information is not available for displaying symbols. No version information found in this file.
一応readelfでも読めますが、プログラムヘッダのオフセットやアドレスはめちゃくちゃです。
目次: ベンチマーク
昨日(2020年7月3日の日記参照)試したところによると、C言語は0文字でSEGVするプログラムを書けました。
では、出力されたバイナリだと最短はいくつでしょうか?とりあえずベースとしてC言語の0文字SEGVプログラムの出力を見ます。
$ gcc -nostdlib a.c /usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000001000 $ ls -la a.out -rwxr-xr-x 1 katsuhiro katsuhiro 9528 Jul 3 04:11 a.out
なんと9KBもあります。readelfで見るとダイナミックリンカー関連のシンボルが含まれているようなので、staticオプションを付けてダイナミックリンカー関連のシンボルを消します。
$ echo -n > a.c && gcc -nostdlib -static a.c /usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000 $ ls -la a.out -rwxr-xr-x 1 katsuhiro katsuhiro 968 Jul 3 04:12 a.out $ strace ./a.out execve("./a.out", ["./a.out"], 0x7ffc92c2b390 /* 46 vars */) = 0 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x401000} --- +++ killed by SIGSEGV +++ Segmentation fault
ログが大量に出るはずのstraceも、わずか1行しかログが出ません。何もしないバイナリにも関わらずa.outのサイズは1KB近くあります。
お手軽なバイナリのダイエットとして、実行に不要なセクションをstripします。
$ strip -R ".note.gnu.build-id" -R ".comment" a.out $ ls -la a.out -rwxr-xr-x 1 katsuhiro katsuhiro 376 Jul 3 04:18 a.out
それでも376バイト。まだでかいですね。
この376バイトのファイルを元にして、バイナリを手で削ります。加工の方針としては、
結果92バイトになりました。
$ ls -la a.out -rwxrwxr-x+ 1 katsuhiro katsuhiro 92 Jul 3 05:43 a.out
もっとアグレッシブにELFヘッダとプログラムヘッダを重ねれば、64バイトにできるかもしれません。
このファイルを実行してみると、システムコールexecveがエラーを返さないで進むので、ELFファイルとして認識してもらえているようです。
$ strace ./a.out execve("./a.out", ["./a.out"], 0x7fff2b4f74a0 /* 47 vars */) = 0 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x400000001} --- +++ killed by SIGSEGV +++ Segmentation fault
$ readelf -a a.out ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x400000001 Start of program headers: 36 (bytes into file) Start of section headers: 0 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 1 Size of section headers: 0 (bytes) Number of section headers: 0 Section header string table index: 0 There are no sections in this file. There are no sections to group in this file. Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align NULL 0x0000000000000000 0x0000000100380040 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x1000 There is no dynamic section in this file. There are no relocations in this file. The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported. Dynamic symbol information is not available for displaying symbols. No version information found in this file.
一応readelfでも読めますし、そこそこ真っ当なファイルをキープできていると思います。
SEGVを出すのがTwitterで流行しているみたいなので、少し考えてみました。Twitterで見かけたのは、*a;main(){*a=0;}(16文字)です。最適化オプションにもよりますが、異常なアドレスへのmovか、未定義命令ud2が出力されてSEGVします。
$ echo -n '*a;main(){*a=0;}' > a.c && gcc a.c a.c:1:1: warning: data definition has no type or storage class 1 | *a;main(){*a=0;} | ^ a.c:1:2: warning: type defaults to 'int' in declaration of 'a' [-Wimplicit-int] 1 | *a;main(){*a=0;} | ^ a.c:1:4: warning: return type defaults to 'int' [-Wimplicit-int] 1 | *a;main(){*a=0;} | ^~~~ $ ./a.out Segmentation fault
ただSEGVするだけで良ければ、mainのアドレスを .textではないアドレスにすれば良いので、main;(5文字)でも、達成できます。
$ echo -n 'main;' > a.c && gcc a.c a.c:1:1: warning: data definition has no type or storage class 1 | main; | ^~~~ a.c:1:1: warning: type defaults to 'int' in declaration of 'main' [-Wimplicit-int] $ ./a.out Segmentation fault
この例はmainを関数ではなく変数として定義し、mainをbssセクションに配置します。最近のLinuxならばデータが置かれているセグメントは実行禁止にするはずなので、mainが指すデータが何であろうと、ジャンプした瞬間にSEGV します。
趣旨とは外れますがSEGVを避けて正常終了させたければ、
$ echo -n 'main=0xc3;' > a.c && gcc -z execstack a.c a.c:1:1: warning: data definition has no type or storage class 1 | main=0xc3; | ^~~~ a.c:1:1: warning: type defaults to 'int' in declaration of 'main' [-Wimplicit-int] $ ./a.out (SEGVしない)
変数mainが指す位置にretq命令(0xc3)を置いて、-z execstackオプションでデータ領域を実行可能にしています。attributeで .text領域に置いても実行できますが、そんなことするくらいならmain() 関数を書いた方が短いです。
変化球を使ってよければ0文字でSEGVできます。nostdlibオプションを使います。
$ echo -n > a.c && gcc -nostdlib a.c /usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000001000 $ ./a.out Segmentation fault
GNU ld(他のリンカーでも同じだと思いますけど)はELFのエントリアドレスに _startというシンボルのアドレスを使います。通常はcrt.oなど、リンク時に自動的に追加されるオブジェクトが _startを定義しますが、nostdlibオプションによりcrt.oがリンクされなくなって、_startは未定義になります。
するとldは _startを適当なアドレスに設定(上記の例では0x1000に)します。当然 _startが指すアドレスにコードはありませんから、実行するとクラッシュします。
目次: ゲーム
Steamの買い物カートの中身一覧を見る方法を探していたんですが、全くわかりませんでした。だいぶ彷徨いましたが、未だトップ画面からカートを見る方法はわからないままです。Steamは本当にUIがひどい。Amazonを見習ってくれ……。
その際に副産物として、秘密の実績を確認する方法を見つけたのでメモしておきます。
と辿ると自分が所持しているゲームの一覧が出ますから、実績を見たいゲームの
と辿ると全ての実績が表示されます。
秘密の実績の意味とは一体……??
目次: ゲーム
STATIONflowのランクが100になりました。何か実績と紐づいているかなと思いましたが、特に何も起きませんでした。しょんぼり。
普通のマップだと重いし、時間も掛かりすぎてやってられないので、専用の軽量マップを作ってひたすら待ちました。それでも相当時間が掛かります。普通に遊ぶ分にはランク50で十分ですね。
STATIONflowでランク100にする途中でいくつかわかったことがありました。
ランク100まで行ってもまだ取れない実績が残っていて(ラッキーセブン、トウキョウ)、なかなか気が遠くなるゲームです。
(※)出入口が6 x 9 = 54、乗り場が6 x 2 = 12、集客レベルは2段階上がるため (54 + 12) x 2 = 132です。
STATIONflowの実績にtypoがありました。
漢字だけ間違ってます。惜しい……!
目次: ゲーム
以前(2020年5月28日の日記参照)STATIONflowで速度3にすると駅の評価が下がることをお伝えしましたが、若干間違っていて「画面の処理落ちで評価が下がる」方が実態に近そうです。
速度2でA+ 評価の駅を使って実験したところ、ノートPCのクロック周波数を低くするだけで、駅の評価がAに下がりました。
駅の評価と見せかけて、実はマシンの評価も入ってるんでしょうか?余計なお世話なので、勘弁してくれよ。
目次: Windows
その2はこちら。
ノートPCでゲームをしていると、筐体が焼けそうなほど熱くなり、キーボードまで熱くなってキーを打つのが辛いです。
発熱の原因はグラフィックスチップですが、どうもCPUも無罪ではないらしく、TurboBoostを無効にするとややマシになることがわかりました。
私のマシンでは「電源オプション」 - 「プロセッサの電源管理」 - 「最大のプロセッサの状態」にて、87%以下にするとTurboBoostがOFFになりました。
下記は87%設定(1.49GHz)と88%設定(3.38GHz)にしたときのCPU動作周波数の変化です。
当然ながら1.49GHzと3.38GHzでは性能に天と地ほどの差があって、1.49GHzだとSTATIONflowの画面はめちゃくちゃカクつきます。
しかしシミュレーションゲームでは、待っているだけの時もありますし、常に爆熱で動いてくれる必要はありません。TurboBoostを任意にOFFにできるのは非常に便利です。
CPUのクロック周波数の上限は何段階かあるようなので、変化点を調べました。
こんな感じでした。Core i5-8250Uはベース周波数1.6GHz、ブースト周波数3.4GHzなのにベース周波数である1.6GHzに張り付く設定は存在しません。謎です。
タスクマネージャーに「基本速度1.8GHz」と表示されているのも謎です。どこから1.8GHz出てきた……??
目次: ゲーム
STATIONflowのしょうもない小技 その2です。
実績の解除が非常に難しい「エレベスト」や「効率の鬼」のような難しい実績が、超簡単に取れる方法です。
例えば、エレベストの条件は「駅ランク20以上で階段とエスカレーターを設置せずにA+ 評価」です。前回(2020年6月27日の日記参照)お伝えした通り、エレベーターと通勤客の変な挙動が合わさって、まともにやるとかなり難しいです。
しかしSTATIONflowの実績判定は甘々で「00:00になった瞬間」しか見ません。従って、
これだけで達成できます。正直、こんな低レベルな小細工が通じると思わなかったので、逆にびっくりしました……。
類似した実績「階段抜き」「ノンエスカレーター」「エレベスト」「効率の鬼」ならば同じ手が通用するはずです。
作成者の想定とは違うだろうという意味で「邪道」な感じはしますが、バグを突いた挙動でもなさそうだし、早解きしたい方は利用してみても良いでしょう。
目次: ゲーム
STATIONflowの基本は理解したつもりなので、実績解除に挑んでますが、難しくて取れないものがいくつかあります。
今のところ一番難しいと思うのは「エレベスト」です。条件は「駅ランク20以上で階段とエスカレーターを設置せずにA+ 評価」です。
ありがちな条件ですが、STATIONflowのエレベーターはとてもヘボいため達成は困難です。
一番問題なのは3つ目の条件です。通常、待ち行列から離脱した客は階段やエスカレーターを利用しますが、移動手段がエレベーターしかない場合、不可解な動きをします。
(※)彷徨っている間はエレベーターが到着しても乗りません。一番訳が分からない動きです。しかもこの彷徨う時間が長い。
このようなおかしな行動をし続けた挙句、勝手に怒り始めて、駅の評価を下げてきます。理不尽極まりないですね。
ランク20到達後も駅と人が増え続けるため、どんどん客が捌ききれなくなり条件が悪くなる一方ですから、「エレベスト」実績を達成する最大のチャンスは「ランク20になる瞬間」でしょう。
が、私の腕では評価Aが限界でした。
もっと頑張ればできるのかもしれないですが、何回も同じマップをランク20までやり直すのは時間掛かって辛いし面白くないのでもうやりません……。
目次: ゲーム
STATIONflowのしょうもない小技。
将来的に、どこに駅の入り口と電車の乗り場が出てくるか?を見たい場合は「マップ作成」で見たいマップを「複製」「マップ編集」すれば、全ての入り口、乗り場を見ることができます。
STATIONflowの面白さは、どこに増設されるかわからない入り口や乗り場に対応することではあるのですが、記録を狙ったりするときや、事前に完璧な駅建築計画を立てたい人は、覗いてみると役立つかもしれません。
目次: Linux
最近、というほどでもないのですが、Linux KernelのデバイスツリーのドキュメントはYAMLで書く方が主流らしいです。
ドキュメントの書き方が良くわからず、合っているのか間違っているのかわからなくて困っていたんですが、ドキュメント Writing DeviceTree Bindings in json-schema を見ていたところ、YAMLチェッカーの存在を知りました。
$ make dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/xxxx.yaml
こんな感じで使います。DT_SCHEMA_FILESを指定せずに実行すると全てのYAMLをチェックしようとしますが、16並列でもかなり時間が掛かりますから、自分が変更したファイルだけチェックしたほうが効率的でしょうね。
< | 2020 | > | ||||
<< | < | 07 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | 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 | - |
合計:
本日: