2017年10月18日 星期三

compiler [7] - code generator - funcall call, pass argument

倦怠期中, 無限取材休刊
這陣子有點懶懶的, 突然失去寫技術文章的熱情, 我花了一些時間重溫 c++ virtual function, 卻提不起勁把這些東西整理成一篇文章, 可能真有些倦怠感, 沒意外的話這篇應該會是倦怠期最後一篇的技術文章。不只這樣, 連正在進行的組譯器我也提不起勁繼續下去, 而好不容易理清的 elf section 我竟然也沒動力整理寫下來, 所以才跑去寫俄羅斯方塊。也許該暫停一下這些東西, 轉換一下學習方向。

倦怠期間沒什麼在學習, 看了 jojo 動畫、王牌大律師, 這樣的生活蠻開心的, 也很舒服, 但不知怎麼的, 就是覺得哪裡怪怪的 ... 「學如逆水行舟不進則退」如果一天下來都沒進步, 這樣的生活令我惶恐, 不過適度的放鬆也是必要的, 我懷疑我放鬆過了頭。

在四則運算告一個段後之後, 本來應該是 if/else, 不過 if/else 不是太難, 就先跳過, 來看看 c function 的呼叫應該怎麼產生對應的組合語言。本篇文章介紹怎麼產生 c function 參數傳遞的組合語言。也許有人知道用 stack 用來傳遞參數, 由右而左的順序放進 stack, 除了這些, 還需要其他的知識才能產生對的組合語言。

把 char c, 傳給 fun1(int a) 時, c 需要做什麼特別的事情嗎?

依照慣例, 先來一個很簡短的 c 程式, 是簡單的 c 函式呼叫, 來看 gcc 會輸出什麼樣的組合語言?

f.c 是 source code, list 1 則是 gcc 輸出的 x86 32bit 組合語言。

fc.c
 1 
 2 char func678(char c)
 3 {
 4   return c;
 5 }
 6 int main(int argc, char *argv[])
 7 {
 8   char func678(char c);
 9   func678(5);
10   return 0;
11 }

list 1. gcc -m32 -S fc.c => fc.s
 1  .file "fc.c"
 2  .text
 3  .globl func678
 4  .type func678, @function
 5 func678:
 6  pushl %ebp
 7  movl %esp, %ebp
 8  subl $4, %esp
 9  movl 8(%ebp), %eax
10  movb %al, -4(%ebp)
11  movzbl -4(%ebp), %eax
12  leave
13  ret
14  .size func678, .-func678
15  .globl main
16  .type main, @function
17 main:
18  pushl %ebp
19  movl %esp, %ebp
20  pushl $5
21  call func678
22  addl $4, %esp
23  movl $0, %eax
24  leave
25  ret
26  .size main, .-main
27  .ident "GCC: (GNU) 5.4.0"
28  .section .note.GNU-stack,"",@progbits

function 參數的傳遞比想像中複雜, 當 function 沒有 prototype 時或是使用 K&R style 的宣告或是 ... 這種參數 - ex: printf(const char *format, ...), 會發動 integer promtion, 這很好理解, 可以參考《“对于那些没有原型的函数,传递给函数的实参将进行缺省参数提升”是什么意思?》, 請不要小看中文世界的知識量, 你的問題說不定並沒特別需要到英文世界找答案, 知乎上的回答很有水準, 有這樣的平台, 是中文使用者的福氣, 讓我們別輸懂英文的人太多, 但請不要把這些話理解成我覺得英文不重要, 英文的重要性是已經到不需要特別指出來了。

fc.s L20 那行是 integer promotion 嗎? 因為有 fc.c L8 那行 (有 function prototype), 所以上述規則並不是用在這個情況, 由於 push 4 byte 長度的 5 (list 1 L20), 應該可以輸出 pushb $5, 這樣只要 push 一個 byte 就好, 而 pushl $5, 看起來很像做了 integer promotion, 把 5 傳給 char c 提升到 int。

真相是怎麼樣呢?

為了找到答案, 我參閱了:

  1. c11 spec
  2. C 語言參考手冊
  3. C 編譯器剖析
  4. Linux C 编程一站式学习
  5. C 語言程序設計 - 現代方法
  6. 標準 C 語言指南: p166, p307。

並在
發問, 結合這些回答以及找到的資料再加上 c11 spec 6.5.2.2 function call 查到的

c11 spec 6.5.2.2
If the expression that denotes the called function has a type that does
include a prototype, the arguments are implicitly converted,
as if by assignment, to the types of the corresponding parameters,
taking the type of each parameter to be the unqualified version
of its declared type. The ellipsis notation in a function prototype
declarator causes argument type conversion to stop after
the last declared parameter. The default argument
promotions are performed on trailing arguments

我得出了結論:
把 5 傳給 char c, 相當於 char c=5, 這會用到 assign 那條轉換規則, 參考《第 15 章 数据类型详解/3. 类型转换 3.3. 由赋值产生的类型转换 (implicit conversion)》, 5 的 type 是 int (不是 short, 也不是 unsinged int), 所以會做 implicit conversion (所以若傳入 300, 就爆了, 翻出來的組合語言會傳入 44, 300 = 0x12c, 0x2c = 44), 而 function 的參數傳遞則是 facebook 討論區說的 ABI, 需要用 4 byte alignment 方式傳入。

節錄: https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html

The caller places arguments in the parameter area in reverse order, in 4-byte chunks. That is, the rightmost argument has the highest address.

Figure 2  Argument assignment with arguments of the fundamental data types


由於這些巧合, 看起來就像 integer promotion。

知道了這些之後, 就知道該如何輸出函式參數傳遞的組合語言了。

我一開始並不知道這些規則, 而是在寫到這部份時, 自然就會有這些疑問, 我要根據哪些規則產生對應的程式碼呢? 才開始找尋問題的答案。比想像中難得多。

2017年10月10日 星期二

[娃店] 20170930 白日夢森林


fig 1. 一平方精美背包: 1480 ntfig 2. 一平方精美背包: 1650 nt fig 3. 高跟長靴 1200 nt

20178 月就想拜訪這間娃店, 不過在交通上有些不便, 一直到 20170930 才有一個完整的時間來造訪。

20170930 這天早上 11:20 從中山捷運站搭車到木柵站, 還真的蠻遠的, 再搭乘 251 公車到永安街站下車, 往左前方看過去, 就可以看到大大的白日夢森林字樣, 就是它了, 我們進去吧!

永安街站
一進門隨意亂晃時, 一匹駿馬吸引了我的目光, 馬也有 bjd 的版本阿? 「當然囉!」店長得意的說道。不過這匹馬是非賣品, 純展示用的, 店長隨後還展示了兔子版本的 bjd 讓我觀賞, 小小的兩隻, 大開眼界。

店面算是蠻大的, 分為前後 2 區, 後面那區有店長自己的娃, 還有個照相區, 不是只有單純的風景圖, 還有一個實體的大沙發, 可惜我不方便帶娃來, 只好拍拍空場景過癮。也有桌椅可以讓買家安置自己的娃, 火力展示一番。

店長感覺是個開朗的人, 憑著自己的熱血, 努力的開了這家娃店, 這是很不容易的事情, 娃圈是小眾市場, 經營不易。

娃體本身是以預購的方式來販賣, 所以是有「檔期」的, 其他配件也很豐富, 服飾、頭髮、鞋子、項鍊、包包等 ... 應有盡有, 挑個東西好過中秋佳節。

有些是來自南部店家的商品, 例如: 偶夢、格子貓 ...

一開始我就被 fig 1, fig 2 的背包所吸引, 製作的相當精緻, 是台灣人做的哦! 網站是: 一平方, 看到台灣同好可以製作這麼精美的配件, 很開心, 我們也是有不輸給日本、中國、韓國的配件製作人員, 看了一下價錢, 嗯 ... 果然是很配合該產品的售價, 我心中稍稍猶豫了一下。

背包、項鍊等配件不像服飾、鞋子之類好賣, 畢竟只是輔助之用, 服裝才是最主力的裝扮, 所以在受歡迎程度上是比較吃虧的, 但這 2 個背包實在吸引我, 雖然不便宜, 我還是下手了, 就算不給 3 分娃用, 當一般文創收藏品, 也是很不錯的。

fig 3 的高跟長靴並不是設計給 DD 穿的, 但試了一下之後, DD 也是可以穿的, 不過膝蓋腿圍的部份有點大, 我猶豫了很久, 不急, 才剛來, 慢慢晃, 我可以仔細的挑選想要的配件、服制。

和服令我大開眼界, 那麼大一包的服裝就只是一件和服, 看來和服是一個很複雜的衣服, 我比較少注意和服, 而店長也有介紹 2 件式的版本, 穿脫就容易些了。

和店長聊了不少娃的知識, 也看到店長自己的收藏, 這是實體店面的線下交流, 和網路世界的線上交流有些不同, 畢竟面對面的相處, 總是多了點人情味, 店長也推薦了不少配件, 還有一個新的背包也很漂亮, 不過入手 2 個就已經讓我吃不消了, 讓給喜歡的人吧!



20171001 是白日夢森林的週年慶, 有一些折扣活動, 大家上吧! 能開這樣的店撐過一年真是不容易, 目前還在苦心經營, 讓我們多多支持這樣的實體店家, 讓他們可以永續經營。

2017年10月3日 星期二

[敗家] dd body III M 胸 - 普肌娃體


20170907 訂購於 ruten, 20170911 收到, 8500nt。除了 DD 普肌 M 胸身體之外, 還包含 DDH-07 和 2 對眼睛, 算是撿到便宜。



普肌 M 胸身體日圓定價 28000円。普肌在包裝上會寫ノーマル, 英文是 normal, 普通、通常的意思。自己到日本買時請注意, 不要買錯了。

由於之前已經有了一顆上妝的 DDH-07, 就差一個身體, 只要再購入一個身體, 我就有第二隻 DD。這次剛好在二手市場購入這個娃體, 完全符合我想要的:
  • 普肌
  • M 胸
  • DD 3 代素體
  • 非一體身
一體身可以參考「DD 亞絲娜二版」以下字樣:
一体型ボディパーツ採用
在胸部偏下的地方有個關節, 一體身就沒有那個關節, 一體身娃體在穿泳裝 (露出肚臍) 時會比較好看, 缺點是可動性會降低, 我比較想要可動性較好的版本。而白肌或是半白肌個人沒什麼偏愛, 覺得普肌就很好看了。

這隻是自組娃, 所需費用是: 8500+1600 = 10100, 比起第一隻的 Candy 21000, 足足省下一半費用。



我已經不是一開始什麼都不知道的玩家了, 雖然也不算熟悉娃圈的一切, 但對於 DD 本身有了「略懂」的理解, 而且有了第一隻娃後, 也不用急著買第二隻娃, 我靜靜的等待二手市場的物品, 慢慢蒐集自己想要的東西。

購入 DD 娃後, 陸續的費用非常花錢, 我很懷疑是不是玩娃的朋友都很辛苦度日, 我省錢的方法是從二手市場購入物品, 通常有機會撿便宜, 但因為物美價廉, 所以動作得快, 我好幾次都搶輸了。

不過有時候多少還是有花大錢的時候, 看上某個物品就是愛不釋手, 這就糟糕了, 不拿幾張紙消災是不行的, figure 1. 的鞋子和牛仔裙就是這樣的情形。

figure 1. 鞋子: 20170806 購於可汀, 1880nt;牛仔裙: 1000, 購於娃友
由於有了 L 集中胸, 我想購買的是 M 胸, 看過圖片, 胸型也算好看, 大小也適中, 買衣服也不用在特別考慮只有 L 集中胸可穿的版本, 有了彈性, 大概又會增加敗家的機率。


figure 2 
figure 2 的鞋子在: Home >> BJD Shoes >> SD16 Girl >> GS000020
是 dollheart 的產品, 定價 46.9 us, 但是我不知道幣別是什麼? 不過就算是美金, 在可汀購入還是比較貴的, 不過在可汀可以試穿, 我才能確認 DD 是不是可以穿, 視覺效果如何? 這是網路商店辦不到的。

DD 娃有限定的版本, 就是 volks 推出的版本, 我從之前出的限量娃看到現在, 只有「雪初音」比較吸引我, 其他就還好。現在的我比較偏向自己組娃, 但要組出好的娃倒是沒有那麼容易, 得花不少心力。頭部的妝是最難的部份, 我運氣好, 剛好收了這個妝師自己打造的版本, 而且我也沒什麼要求, 自己覺得好看就行, 當然價錢也是要考慮的, 5000 左右的我基本上就要考慮再三了。

雖然偏向自組娃, 但遇到喜歡的官服該怎麼辦, 我通常喜愛官服更勝於娃體娃頭本身, 但限量娃最有價值的就是娃頭, 不過每個人喜愛的都不同, 有人不喜歡官服, 有人不需要娃體, 就這樣, 在二手市場上各取所需, 我就是這樣買到初音和雪初音的官服, 也才有了初音的造型, 當然和官方的有所不同, 但是勉強滿足自己喜歡的造型。

2017年9月25日 星期一

c++ RAII VS unix signal

RAII 很美好, 威力也很強大, 但一遇到 unix signal, 就沒轍了。

b.cpp
 1 #include <typeinfo>
 2 #include <iostream>
 3 #include <cstdlib>
 4 using namespace std;
 5 
 6 #include <signal.h>
 7 #include <unistd.h>
 8 
 9 int i=5;
10 void sig_handle(int sig)
11 {
12   cout << "sig: " << sig << endl;
13   i=0;
14   exit(0);
15   return;
16 }
17 
18 
19 class A
20 {
21   public:
22     A()
23     {
24       cout << "A ctor" << endl;
25     }
26     ~A()
27     {
28       cout << "~A dtor" << endl;
29     }
30 };
31 
32 
33 
34 int main(int argc, char *argv[])
35 {
36   A a;
37   signal(SIGINT, sig_handle);
38   while(1);
39   // c++ compiler 在這裡插入 A::~A()
50   return 0;
51 }

按下 Ctrl-C 之後, 解構函式不會發動, 這是很自然的, 因為 c++ 編譯器將解構函式安插在 39 那行, 而 Ctrl-C 按下後, 會執行 sig_handle, 不會在回到 39 那行, 自然無法發動解構函式。

RAII 破功? 雖然很不想承認, 但這是真的。c++ 神話破功了。

不過只要 a 是 global object, 那就沒問題, 解構函式還是會正常發動, 算是不幸中的大幸。

回到 local object 的版本, 那該怎麼辦? 如何讓 sig_handle 回到 39 那行呢?

我先是用了 exception handle, 但是失敗。

c.cpp
 1 #include <typeinfo>
 2 #include <iostream>
 3 #include <cstdlib>
 4 using namespace std;
 5 
 6 #include <signal.h>
 7 #include <unistd.h>
 8 #include <setjmp.h>
 9 
10 int i=5;
11 jmp_buf env;
12 
13 void sig_handle(int sig)
14 {
15   cout << "sig: " << sig << endl;
16   i=0;
17   throw 5;
18   return;
19 }
20 
21 
22 class A
23 {
24   public:
25     A()
26     {
27       cout << "A ctor" << endl;
28     }
29     ~A()
30     {
31       cout << "~A dtor" << endl;
32     }
33 };
34 
35 int main(int argc, char *argv[])
36 {
37   A a;
38   try
39   {
40     signal(SIGABRT, sig_handle);
41     while(1);
42   }
43   catch (...)
44   {
45     cout << "ex" << endl;
46   }
47   return 0;
48 }

ref:
Throwing an exception from within a signal handler
24.9 Using a Separate Signal Stack

我推測可能是 sig_hanlde 發動的方式和一般函式呼叫不同, sig_handle 無法回到 main, 或是回到 main 但無法正確配對到 catch 的程式碼, signal handle 連 stack 都不同於這個程式。

使用了 gdb 來驗證看看, gdb 會攔截 sigint, 我改用 sigabort, sigabort 會傳給要除錯的那支程式, 得到

gdb debug sigabort
 1 Program received signal SIGABRT, Aborted.
 2 main (argc=1, argv=0xffd83224) at c.cpp:41
 3 41      while(1);
 4 (gdb) n
 5 sig_handle (sig=6) at c.cpp:14
 6 14  {
 7 (gdb) n
 8 15    cout « "sig: " « sig « endl;
 9 (gdb) bt
10 #0  sig_handle (sig=6) at c.cpp:15
11 #1  <signal handler called>
12 #2  main (argc=1, argv=0xffd83224) at c.cpp:41

看起來好像是 main call sig_handle, 但實際上可能沒有這麼單純, 不管他了, 反正就是不能用 exception handle 處理這問題。換 exception handle 破功。

再用另外一招, 改用 setjmp/longjmp, 這就沒問題了, 解構函式正常發動。

bsjlj.cpp
 1 #include <typeinfo>
 2 #include <iostream>
 3 #include <cstdlib>
 4 using namespace std;
 5 
 6 #include <signal.h>
 7 #include <unistd.h>
 8 #include <setjmp.h>
 9 
10 int i=5;
11 jmp_buf env;
12 
13 void sig_handle(int sig)
14 {
15   cout << "sig: " << sig << endl;
16   i=0;
17   longjmp(env, 5);
18   return;
19 }
20 
21 
22 class A
23 {
24   public:
25     A()
26     {
27       cout << "A ctor" << endl;
28     }
29     ~A()
30     {
31       cout << "~A dtor" << endl;
32     }
33 };
34 
35 
36 
37 int main(int argc, char *argv[])
38 {
39   A a;
40 
41   signal(SIGINT, sig_handle);
42   if (0 == setjmp(env))
43   {
44     while(1);
45   }
46   else
47   {
48     cout << "longjmp" << endl;
49   }
50   return 0;
51 }

20170918 補充
在讀過 The Linux Programming Interface 國際中文版 21.2.1 後, 我知道為什麼要有 sigsetjmp/siglongjmp, 使用 sigsetjmp/siglongjmp 會比較正確, 但原理是一樣的, sigsetjmp/siglongjmp 只是多了處理 signal, 讓 signal 有正確的行為, 本質的流程改變的動作還是一樣的。

細節請參考 21.2.1, signal 是很複雜的東西, 我建議把 20, 21, 22 這 3 章仔細研讀過一次, 再搭配 APUE 的 signal 章節, 應該會比較認識 signal, signal 絕不是你想像中的是一個簡單的概念, 要有會付出極大心力去理解它的心裡準備。

結束了嗎? 還沒, 不要說 signal 這麼複雜的東西, exit 就可以讓 dtor 沒轍了。

r.cpp
 1 #include <typeinfo>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstdio>
 5 using namespace std;
 6 
 7 #include <signal.h>
 8 #include <unistd.h>
 9 
10 class A
11 {
12   public:
13     A()
14     {
15       printf("A ctor\n");
16     }
17     ~A()
18     {
19       printf("~A dtor\n");
20     }
21 };
22 
23 int main(int argc, char *argv[])
24 {
25   A a;
26 
27   exit(1);
28   return 0;
29 }

A::~A() 不會發動呢! 那當然, 看看 r.dis, A::~A 根本沒被插到 main 裡頭, 能發動才有鬼。

r.dis (objdump -CSd r)
 1 int main(int argc, char *argv[])
 2 {
 3  8048e3c:       8d 4c 24 04             lea    0x4(%esp),%ecx
 4  8048e40:       83 e4 f0                and    $0xfffffff0,%esp
 5  8048e43:       ff 71 fc                pushl  -0x4(%ecx)
 6  8048e46:       55                      push   %ebp
 7  8048e47:       89 e5                   mov    %esp,%ebp
 8  8048e49:       51                      push   %ecx
 9  8048e4a:       83 ec 14                sub    $0x14,%esp
10   A a;
11  8048e4d:       83 ec 0c                sub    $0xc,%esp
12  8048e50:       8d 45 f7                lea    -0x9(%ebp),%eax
13  8048e53:       50                      push   %eax
14  8048e54:       e8 69 00 00 00          call   8048ec2 <A::A()>
15  8048e59:       83 c4 10                add    $0x10,%esp
16
17   exit(1);
18  8048e5c:       83 ec 0c                sub    $0xc,%esp
19  8048e5f:       6a 01                   push   $0x1
20  8048e61:       e8 fa 2a 0a 00          call   80eb960 <exit>
21
22 08048e66 <__static_initialization_and_destruction_0(int, int)>:
23   return 0;
24 }

按照慣例, 如果是 global object, 就算呼叫 exit, 依然可以正常發動 dtor。global object 是不是很好用呢?

至於為什麼 global object 可以正常發動 dtor, 不是什麼神奇的黑魔法, 在離開 main 之後, c++ runtime library 補了一段程式碼在 main 之後, 會把所有的 global/static object 的解構函式執行起來。

_exit 就不行了。

2017年9月15日 星期五

pascal 編譯/執行 (0) - 使用 free pascal 編譯出 x86_32 的執行檔

在 rust 大戰 c/c++ 時, 我們似乎忘了還有一個系統層級的語言 pascal, pascal 也沒有 gc, 也是編譯型語言, dos 時代的 turbo pascal, windows 時代的 delphi, 是 pascal 風光的時候, 為什麼突然沒落了, 真是奇怪。我突然對 pascal 有了興趣, 來把玩一下這個傳統的語言。

在談論 x86 32bit 模式之前, 先來看看預設的 x86 64bit 怎麼編譯。

environment:
linux 64bit/debian

在 linux 下有 free pascal 編譯器可用, x86 64bit 系統預設的 ppcx64 只能編譯出 x64 執行檔, 先來看看怎麼使用 free pascal 編譯器。

p1.pas
 1 {$CALLING PASCAL}
 2 Program h;
 3 (*Uses f1, Crt in '/usr/local/lib/fpc/3.1.1/units/i386-linux/rtl-console/crt.ppu';*)
 4 Uses f1, Crt;
 5 
 6 
 7 var x, v: integer;
 8 var str: string;
 9 begin
10 
11   str := 'abc';
12 
13 (*  x := max(5,6); *)
14   x := 7;
15   Writeln('hello free pascal, max: ', x, str);
16 end.

apt-get install fpc           # 安裝 free pascal 編譯器。
fpc p1.pas                    # 編譯 p1.pas
./p1                          # 執行
hello free pascal, max: 7abc  # 執行結果

p1.pas 很簡單的把 int, string 變數用 Writeln 印出來, 不需要像 c printf 用那些老是記不住的 %d, %u, %lld, 還蠻方便的, 這是怎麼做到的? 真令人好奇。

因為只看得懂 x86 32bit 組合語言, 所以我還是很需要可以建構出 32bit 環境的開發工具。

x86 64bit 系統預設的 ppcx64 無法編譯出 x86 32bit code 的程式, 需要自己編譯 32bit 的版本, 參考《From Linux x64 to Linux i386》建構可以 build x86 32bit code 的 pascal compiler。

下載 freepascal source code
https://github.com/graemeg/freepascal.git

還要先安裝 free pascal compiler, free pascal compiler 是用 pascal 寫的。
apt-get install fp-compiler

From Linux x64 to Linux i386
以下是參考《From Linux x64 to Linux i386》的內容

當然要先安裝 x86 32bit 開發工具, 請參考《在 debian 上安裝 x86 32bit gcc/g++

再來是建立 /usr/bin/i386-linux-as, /usr/bin/i386-linux-ld 這 2 個 script, 記得
chmod 755 /usr/bin/i386-linux-as /usr/bin/i386-linux-ld 
給他們執行權限。

/usr/bin/i386-linux-as
1 #!/bin/bash
2 as --32 $@

/usr/bin/i386-linux-ld
1 #!/bin/bash
2 ld -A elf32-i386 $@

以下指令分別是 "編譯" 以及 "安裝"
compile and install command
1 make all CPU_TARGET=i386
2 sudo make crossinstall CPU_TARGET=i386

會安裝到
/usr/local/lib/fpc/3.1.1/

h.pas
 1 Program h;
 2 Uses Crt in '/usr/local/lib/fpc/3.1.1/units/i386-linux/rtl-console/crt.ppu';
 3 
 4   var x, v: integer;
 5 begin
 6 
 7   x := 5;
 8   v := 6;
 9   Writeln('hello free pascal, max: ', x);
10   Writeln('hello free pascal, min: ', v);
11 end.

編譯 h.pas 與執行
 1 descent@debian64:pascal$ /usr/local/lib/fpc/3.1.1/ppcross386 h.pas
 2 Free Pascal Compiler version 3.1.1 [2017/03/27] for i386
 3 Copyright (c) 1993-2017 by Florian Klaempfl and others
 4 Target OS: Linux for i386
 5 Compiling h.pas
 6 Linking h
 7 11 lines compiled, 0.3 sec
 8 descent@debian64:pascal$ ./h 
 9 hello free pascal, max: 5
10 hello free pascal, min: 6

That's it. Edit your /etc/fpc.cfg file if needed.

得到 h.s 的組合語言檔案
/usr/local/lib/fpc/3.1.1/ppcross386 -g -al -Cn h.pas

h.s L86, 87 是呼叫 min 這個 function, 可是卻不是用 pascal call convention 而是使用 register 傳遞參數, 但我加上 {$CALLING PASCAL} 卻沒有看到期待的 pascal call convention。






ModifierPushing orderStack cleaned byalignment





¡none¿Left-to-rightCalleedefault
registerLeft-to-rightCalleedefault
cdeclRight-to-leftCallerGCC alignment
interruptRight-to-leftCalleedefault
pascalLeft-to-rightCalleedefault
safecallRight-to-leftCalleedefault
stdcallRight-to-leftCalleeGCC alignment
oldfpccallRight-to-leftCalleedefault





h.s L58 ~ 60, free pascal compiler 也幫我們產生一個像 c main function 的組合語言。

h.s
  1  .file "h.pas"
  2 # Begin asmlist al_begin
  3 
  4 .section .text.b_DEBUGSTART_$P$H
  5  .balign 4,0x90
  6 .globl DEBUGSTART_$P$H
  7  .type DEBUGSTART_$P$H,@object
  8 DEBUGSTART_$P$H:
  9  .stabs "/media/work/git/progs/pascal/",100,0,0,.Lf2
 10  .stabs "h.pas",100,0,0,.Lf2
 11 .Lf2:
 12 # End asmlist al_begin
 13 # Begin asmlist al_stabs
 14 
 15 .section .data.n_H
 16  .balign 4
 17 .globl DEBUGINFO_$P$H
 18  .type DEBUGINFO_$P$H,@object
 19 DEBUGINFO_$P$H:
 20 # Defs - Begin unit SYSTEM has index 1
 21  .stabs "void:t2=2",128,0,0,0
 22  .stabs "SMALLINT:t1=@s16;r1;-32768;32767;",128,0,0,0
 23 # Defs - End unit SYSTEM has index 1
 24 # Defs - Begin unit FPINTRES has index 2
 25 # Defs - End unit FPINTRES has index 2
 26 # Defs - Begin unit F1 has index 3
 27 # Defs - End unit F1 has index 3
 28 # Defs - Begin unit UNIXTYPE has index 8
 29 # Defs - End unit UNIXTYPE has index 8
 30 # Defs - Begin unit BASEUNIX has index 5
 31 # Defs - End unit BASEUNIX has index 5
 32 # Defs - Begin unit UNIXUTIL has index 9
 33 # Defs - End unit UNIXUTIL has index 9
 34 # Defs - Begin unit SYSCALL has index 10
 35 # Defs - End unit SYSCALL has index 10
 36 # Defs - Begin unit UNIX has index 6
 37 # Defs - End unit UNIX has index 6
 38 # Defs - Begin unit TERMIO has index 7
 39 # Defs - End unit TERMIO has index 7
 40 # Defs - Begin unit CRT has index 4
 41 # Defs - End unit CRT has index 4
 42 # Defs - Begin unit SI_PRC has index 10
 43 # Defs - End unit SI_PRC has index 10
 44 # Defs - Begin Staticsymtable
 45 # Defs - End Staticsymtable
 46 # Syms - Begin Staticsymtable
 47  .stabs "X:S1",38,0,5,U_$P$H_$$_X
 48  .stabs "V:S1",38,0,5,U_$P$H_$$_V
 49 # Syms - End Staticsymtable
 50 # End asmlist al_stabs
 51 # Begin asmlist al_procedures
 52 
 53 .section .text.n_main
 54  .balign 16,0x90
 55 .globl PASCALMAIN
 56  .type PASCALMAIN,@function
 57 PASCALMAIN:
 58 .globl main
 59  .type main,@function
 60 main:
 61  .stabs "main:F2",36,0,5,main
 62 .Lc1:
 63  .stabs "h.pas",132,0,0,.Lf1
 64 .Lf1:
 65  .stabn 68,0,6,.Ll1 - main
 66 .Ll1:
 67 # [h.pas]
 68 # [6] begin
 69  pushl %ebp
 70 .Lc3:
 71 .Lc4:
 72  movl %esp,%ebp
 73 .Lc5:
 74  pushl %ebx
 75  call fpc_initializeunits
 76  .stabn 68,0,8,.Ll2 - main
 77 .Ll2:
 78 # [8] x := max(5,6);
 79  movw $6,%dx
 80  movw $5,%ax
 81  call F1_$$_MAX$SMALLINT$SMALLINT$$SMALLINT
 82  movw %ax,U_$P$H_$$_X
 83  .stabn 68,0,9,.Ll3 - main
 84 .Ll3:
 85 # [9] v := min(5,6);
 86  movw $6,%dx
 87  movw $5,%ax
 88  call F1_$$_MIN$SMALLINT$SMALLINT$$SMALLINT
 89  movw %ax,U_$P$H_$$_V
 90  .stabn 68,0,10,.Ll4 - main
 91 .Ll4:
 92 # [10] Writeln('hello free pascal, max: ', x);
 93  call fpc_get_output
 94  movl %eax,%ebx
 95  movl $_$H$_Ld1,%ecx
 96  movl %ebx,%edx
 97  movl $0,%eax
 98  call fpc_write_text_shortstr
 99  call fpc_iocheck
100  movswl U_$P$H_$$_X,%ecx
101  movl %ebx,%edx
102  movl $0,%eax
103  call fpc_write_text_sint
104  call fpc_iocheck
105  movl %ebx,%eax
106  call fpc_writeln_end
107  call fpc_iocheck
108  .stabn 68,0,11,.Ll5 - main
109 .Ll5:
110 # [11] Writeln('hello free pascal, min: ', v);
111  call fpc_get_output
112  movl %eax,%ebx
113  movl $_$H$_Ld2,%ecx
114  movl %ebx,%edx
115  movl $0,%eax
116  call fpc_write_text_shortstr
117  call fpc_iocheck
118  movswl U_$P$H_$$_V,%ecx
119  movl %ebx,%edx
120  movl $0,%eax
121  call fpc_write_text_sint
122  call fpc_iocheck
123  movl %ebx,%eax
124  call fpc_writeln_end
125  call fpc_iocheck
126  .stabn 68,0,12,.Ll6 - main
127 .Ll6:
128 # [12] end.
129  call fpc_do_exit
130  popl %ebx
131  movl %ebp,%esp
132  popl %ebp
133  ret
134 .Lc2:
135 .Le0:
136  .size main, .Le0 - main
137  .stabn 192,0,0,0
138  .stabn 224,0,0,.Lt1-main
139 .Lt1:
140 
141 .section .text
142 
143 .section .fpc.n_links
144  .long DEBUGINFO_$P$H
145  .long DEBUGSTART_$P$H
146  .long DEBUGEND_$P$H
147 # End asmlist al_procedures
148 # Begin asmlist al_globals
149 
150 .section .bss
151  .balign 2
152 # [5] var x, v: integer;
153  .type U_$P$H_$$_X,@object
154  .size U_$P$H_$$_X,2
155 U_$P$H_$$_X:
156  .zero 2
157 
158 .section .bss
159  .balign 2
160  .type U_$P$H_$$_V,@object
161  .size U_$P$H_$$_V,2
162 U_$P$H_$$_V:
163  .zero 2
164 
165 .section .data.n_INITFINAL
166  .balign 4
167 .globl INITFINAL
168  .type INITFINAL,@object
169 INITFINAL:
170  .long 3,0
171  .long INIT$_$SYSTEM
172  .long 0
173  .long INIT$_$UNIX
174  .long FINALIZE$_$UNIX
175  .long INIT$_$CRT
176  .long FINALIZE$_$CRT
177 .Le1:
178  .size INITFINAL, .Le1 - INITFINAL
179 
180 .section .data.n_FPC_THREADVARTABLES
181  .balign 4
182 .globl FPC_THREADVARTABLES
183  .type FPC_THREADVARTABLES,@object
184 FPC_THREADVARTABLES:
185  .long 1
186  .long THREADVARLIST_$SYSTEM$indirect
187 .Le2:
188  .size FPC_THREADVARTABLES, .Le2 - FPC_THREADVARTABLES
189 
190 .section .data.n_FPC_RESOURCESTRINGTABLES
191  .balign 4
192 .globl FPC_RESOURCESTRINGTABLES
193  .type FPC_RESOURCESTRINGTABLES,@object
194 FPC_RESOURCESTRINGTABLES:
195  .long 0
196 .Le3:
197  .size FPC_RESOURCESTRINGTABLES, .Le3 - FPC_RESOURCESTRINGTABLES
198 
199 .section .data.n_FPC_WIDEINITTABLES
200  .balign 4
201 .globl FPC_WIDEINITTABLES
202  .type FPC_WIDEINITTABLES,@object
203 FPC_WIDEINITTABLES:
204  .long 0
205 .Le4:
206  .size FPC_WIDEINITTABLES, .Le4 - FPC_WIDEINITTABLES
207 
208 .section .data.n_FPC_RESSTRINITTABLES
209  .balign 4
210 .globl FPC_RESSTRINITTABLES
211  .type FPC_RESSTRINITTABLES,@object
212 FPC_RESSTRINITTABLES:
213  .long 0
214 .Le5:
215  .size FPC_RESSTRINITTABLES, .Le5 - FPC_RESSTRINITTABLES
216 
217 .section .fpc.n_version
218  .balign 8
219  .type __fpc_ident,@object
220 __fpc_ident:
221  .ascii "FPC 3.1.1 [2017/03/27] for i386 - Linux"
222 .Le6:
223  .size __fpc_ident, .Le6 - __fpc_ident
224 
225 .section .data.n___stklen
226  .balign 4
227 .globl __stklen
228  .type __stklen,@object
229 __stklen:
230  .long 8388608
231 .Le7:
232  .size __stklen, .Le7 - __stklen
233 
234 .section .data.n___heapsize
235  .balign 4
236 .globl __heapsize
237  .type __heapsize,@object
238 __heapsize:
239  .long 0
240 .Le8:
241  .size __heapsize, .Le8 - __heapsize
242 
243 .section .data.n___fpc_valgrind
244  .balign 4
245 .globl __fpc_valgrind
246  .type __fpc_valgrind,@object
247 __fpc_valgrind:
248  .byte 0
249 .Le9:
250  .size __fpc_valgrind, .Le9 - __fpc_valgrind
251 
252 .section .data.n_FPC_RESLOCATION
253  .balign 4
254 .globl FPC_RESLOCATION
255  .type FPC_RESLOCATION,@object
256 FPC_RESLOCATION:
257  .long 0
258 .Le10:
259  .size FPC_RESLOCATION, .Le10 - FPC_RESLOCATION
260 # End asmlist al_globals
261 # Begin asmlist al_typedconsts
262 
263 .section .rodata.n__$H$_Ld1
264  .balign 4
265 .globl _$H$_Ld1
266 _$H$_Ld1:
267  .ascii "\030hello free pascal, max: \000"
268 .Le11:
269  .size _$H$_Ld1, .Le11 - _$H$_Ld1
270 
271 .section .rodata.n__$H$_Ld2
272  .balign 4
273 .globl _$H$_Ld2
274 _$H$_Ld2:
275  .ascii "\030hello free pascal, min: \000"
276 .Le12:
277  .size _$H$_Ld2, .Le12 - _$H$_Ld2
278 # End asmlist al_typedconsts
279 # Begin asmlist al_dwarf_frame
280 
281 .section .debug_frame
282 .Lc6:
283  .long .Lc8-.Lc7
284 .Lc7:
285  .long -1
286  .byte 1
287  .byte 0
288  .uleb128 1
289  .sleb128 -4
290  .byte 8
291  .byte 12
292  .uleb128 4
293  .uleb128 4
294  .byte 5
295  .uleb128 8
296  .uleb128 1
297  .balign 4,0
298 .Lc8:
299  .long .Lc10-.Lc9
300 .Lc9:
301  .long .Lc6
302  .long .Lc1
303  .long .Lc2-.Lc1
304  .byte 4
305  .long .Lc3-.Lc1
306  .byte 14
307  .uleb128 8
308  .byte 4
309  .long .Lc4-.Lc3
310  .byte 5
311  .uleb128 5
312  .uleb128 2
313  .byte 4
314  .long .Lc5-.Lc4
315  .byte 13
316  .uleb128 5
317  .balign 4,0
318 .Lc10:
319 # End asmlist al_dwarf_frame
320 # Begin asmlist al_end
321 
322 .section .text.z_DEBUGEND_$P$H
323  .balign 4,0x90
324 .globl DEBUGEND_$P$H
325  .type DEBUGEND_$P$H,@object
326 DEBUGEND_$P$H:
327  .stabs "",100,0,0,.Lf3
328 .Lf3:
329 # End asmlist al_end
330 .section .note.GNU-stack,"",%progbits

從 h.s L92 可以得知, Writeln 被換成很多 call XXX, 直到 call fpc_writeln_end 時, 才會在終端機印出字串。

我不知道 Writeln 是不是被稱為 function, 但至少和 c function 是完全不同的概念, 有點類似語言內建的 '指令'。而我寫的 function max, 被換成 F1_$$_MAX$SMALLINT$SMALLINT$$SMALLINT 這樣的名稱, 呼叫 max 時, 也只有
call    F1_$$_MAX$SMALLINT$SMALLINT$$SMALLINT
符合想像的那樣。這就是語言內建 IO, 而 C 語言沒有內建 IO 的意思, C 的 IO 得靠 printf function 來完成, 語言不提供這樣的 '指令'。

free pascal compiler 在編譯時, 會產生 ppas.sh link.res。
ppas.sh - 編譯 script
link.res - linker script

 1 #!/bin/sh
 2 DoExitAsm ()
 3 { echo "An error occurred while assembling $1"; exit 1; }
 4 DoExitLink ()
 5 { echo "An error occurred while linking $1"; exit 1; }
 6 echo Linking h
 7 OFS=$IFS
 8 IFS="
 9 "
10 /usr/bin/ld -b elf32-i386 -m elf_i386      -L. -o h -T link.res -e _start
11 if [ $? != 0 ]; then DoExitLink h; fi
12 IFS=$OFS

link.res
  1 SEARCH_DIR("/usr/lib/i386-linux-gnu/")
  2 SEARCH_DIR("/lib/")
  3 SEARCH_DIR("/usr/lib/")
  4 SEARCH_DIR("/usr/lib/ruby/")
  5 SEARCH_DIR("/usr/lib/modules-load.d/")
  6 SEARCH_DIR("/usr/lib/ipxe/")
  7 SEARCH_DIR("/usr/lib/dbus-1.0/")
  8 SEARCH_DIR("/usr/lib/libgksu/")
  9 SEARCH_DIR("/usr/lib/pkgconfig/")
 10 SEARCH_DIR("/usr/lib/gcr/")
 11 SEARCH_DIR("/usr/lib/caja/")
 12 SEARCH_DIR("/usr/lib/exim4/")
 13 SEARCH_DIR("/usr/lib/apache2/")
 14 SEARCH_DIR("/usr/lib/graphviz/")
 15 SEARCH_DIR("/usr/lib/libblas/")
 16 SEARCH_DIR("/usr/lib/python3/")
 17 SEARCH_DIR("/usr/lib/pymodules/")
 18 SEARCH_DIR("/usr/lib/mate-utils/")
 19 SEARCH_DIR("/usr/lib/atril/")
 20 SEARCH_DIR("/usr/lib/python2.6/")
 21 SEARCH_DIR("/usr/lib/gpt/")
 22 SEARCH_DIR("/usr/lib/lognorm/")
 23 SEARCH_DIR("/usr/lib/arm-none-eabi/")
 24 SEARCH_DIR("/usr/lib/tc/")
 25 SEARCH_DIR("/usr/lib/vlc/")
 26 SEARCH_DIR("/usr/lib/udisks2/")
 27 SEARCH_DIR("/usr/lib/python2.7/")
 28 SEARCH_DIR("/usr/lib/locale/")
 29 SEARCH_DIR("/usr/lib/mate-applets/")
 30 SEARCH_DIR("/usr/lib/dconf/")
 31 SEARCH_DIR("/usr/lib/crash/")
 32 SEARCH_DIR("/usr/lib/wine/")
 33 SEARCH_DIR("/usr/lib/x86_64-linux-gnu/")
 34 SEARCH_DIR("/usr/lib/ghc/")
 35 SEARCH_DIR("/usr/lib/mime/")
 36 SEARCH_DIR("/usr/lib/man-db/")
 37 SEARCH_DIR("/usr/lib/glib-networking/")
 38 SEARCH_DIR("/usr/lib/locate/")
 39 SEARCH_DIR("/usr/lib/openssh/")
 40 SEARCH_DIR("/usr/lib/gvfs/")
 41 SEARCH_DIR("/usr/lib/mozilla/")
 42 SEARCH_DIR("/usr/lib/clang/")
 43 SEARCH_DIR("/usr/lib/gnome-keyring/")
 44 SEARCH_DIR("/usr/lib/gtags/")
 45 SEARCH_DIR("/usr/lib/iceweasel/")
 46 SEARCH_DIR("/usr/lib/libreoffice/")
 47 SEARCH_DIR("/usr/lib/utempter/")
 48 SEARCH_DIR("/usr/lib/os-prober/")
 49 SEARCH_DIR("/usr/lib/ghostscript/")
 50 SEARCH_DIR("/usr/lib/lazarus/")
 51 SEARCH_DIR("/usr/lib/node_modules/")
 52 SEARCH_DIR("/usr/lib/klibc/")
 53 SEARCH_DIR("/usr/lib/mate-panel/")
 54 SEARCH_DIR("/usr/lib/gnome-shell/")
 55 SEARCH_DIR("/usr/lib/systemd/")
 56 SEARCH_DIR("/usr/lib/grub/")
 57 SEARCH_DIR("/usr/lib/ssl/")
 58 SEARCH_DIR("/usr/lib/at-spi2-core/")
 59 SEARCH_DIR("/usr/lib/gjs/")
 60 SEARCH_DIR("/usr/lib/accountsservice/")
 61 SEARCH_DIR("/usr/lib/odbc/")
 62 SEARCH_DIR("/usr/lib/mpage/")
 63 SEARCH_DIR("/usr/lib/menu-cache/")
 64 SEARCH_DIR("/usr/lib/gcc/")
 65 SEARCH_DIR("/usr/lib/p7zip/")
 66 SEARCH_DIR("/usr/lib/gcj/")
 67 SEARCH_DIR("/usr/lib/X11/")
 68 SEARCH_DIR("/usr/lib/python3.4/")
 69 SEARCH_DIR("/usr/lib/emacsen-common/")
 70 SEARCH_DIR("/usr/lib/gnupg2/")
 71 SEARCH_DIR("/usr/lib/jvm-exports/")
 72 SEARCH_DIR("/usr/lib/samba/")
 73 SEARCH_DIR("/usr/lib/groff/")
 74 SEARCH_DIR("/usr/lib/eject/")
 75 SEARCH_DIR("/usr/lib/policykit-1/")
 76 SEARCH_DIR("/usr/lib/os-probes/")
 77 SEARCH_DIR("/usr/lib/cdbs/")
 78 SEARCH_DIR("/usr/lib/traceevent_4.9/")
 79 SEARCH_DIR("/usr/lib/grub-legacy/")
 80 SEARCH_DIR("/usr/lib/pulse-10.0/")
 81 SEARCH_DIR("/usr/lib/mc/")
 82 SEARCH_DIR("/usr/lib/ldscripts/")
 83 SEARCH_DIR("/usr/lib/kexec-tools/")
 84 SEARCH_DIR("/usr/lib/apt/")
 85 SEARCH_DIR("/usr/lib/llvm-3.6/")
 86 SEARCH_DIR("/usr/lib/fpc/")
 87 SEARCH_DIR("/usr/lib/cli/")
 88 SEARCH_DIR("/usr/lib/lp_solve/")
 89 SEARCH_DIR("/usr/lib/xine/")
 90 SEARCH_DIR("/usr/lib/xorg/")
 91 SEARCH_DIR("/usr/lib/mutter/")
 92 SEARCH_DIR("/usr/lib/gettext/")
 93 SEARCH_DIR("/usr/lib/lmbench/")
 94 SEARCH_DIR("/usr/lib/libvte-2.91-0/")
 95 SEARCH_DIR("/usr/lib/emacs/")
 96 SEARCH_DIR("/usr/lib/gnome-settings-daemon-3.0/")
 97 SEARCH_DIR("/usr/lib/evolution-data-server/")
 98 SEARCH_DIR("/usr/lib/rtkit/")
 99 SEARCH_DIR("/usr/lib/ccache/")
100 SEARCH_DIR("/usr/lib/rsyslog/")
101 SEARCH_DIR("/usr/lib/gold-ld/")
102 SEARCH_DIR("/usr/lib/evolution/")
103 SEARCH_DIR("/usr/lib/python3.5/")
104 SEARCH_DIR("/usr/lib/libpeas-1.0/")
105 SEARCH_DIR("/usr/lib/git-core/")
106 SEARCH_DIR("/usr/lib/linux-kbuild-4.8/")
107 SEARCH_DIR("/usr/lib/ispell/")
108 SEARCH_DIR("/usr/lib/mono/")
109 SEARCH_DIR("/usr/lib/perf_4.9-core/")
110 SEARCH_DIR("/usr/lib/spice-protocol/")
111 SEARCH_DIR("/usr/lib/debug/")
112 SEARCH_DIR("/usr/lib/sudo/")
113 SEARCH_DIR("/usr/lib/rustlib/")
114 SEARCH_DIR("/usr/lib/stardict/")
115 SEARCH_DIR("/usr/lib/lapack/")
116 SEARCH_DIR("/usr/lib/upower/")
117 SEARCH_DIR("/usr/lib/coreutils/")
118 SEARCH_DIR("/usr/lib/tcltk/")
119 SEARCH_DIR("/usr/lib/linux-kbuild-4.9/")
120 SEARCH_DIR("/usr/lib/VBoxGuestAdditions/")
121 SEARCH_DIR("/usr/lib/bcc/")
122 SEARCH_DIR("/usr/lib/girepository-1.0/")
123 SEARCH_DIR("/usr/lib/sysctl.d/")
124 SEARCH_DIR("/usr/lib/cgi-bin/")
125 SEARCH_DIR("/usr/lib/qemu/")
126 SEARCH_DIR("/usr/lib/pyshared/")
127 SEARCH_DIR("/usr/lib/dpkg/")
128 SEARCH_DIR("/usr/lib/mate-screensaver/")
129 SEARCH_DIR("/usr/lib/compat-ld/")
130 SEARCH_DIR("/usr/lib/engrampa/")
131 SEARCH_DIR("/usr/lib/valgrind/")
132 SEARCH_DIR("/usr/lib/aspell/")
133 SEARCH_DIR("/usr/lib/llvm-3.8/")
134 SEARCH_DIR("/usr/lib/gnupg/")
135 SEARCH_DIR("/usr/lib/pluma/")
136 SEARCH_DIR("/usr/lib/tasksel/")
137 SEARCH_DIR("/usr/lib/security/")
138 SEARCH_DIR("/usr/lib/ogdi/")
139 SEARCH_DIR("/usr/lib/texinfo/")
140 SEARCH_DIR("/usr/lib/linux-boot-probes/")
141 SEARCH_DIR("/usr/lib/mate-power-manager/")
142 SEARCH_DIR("/usr/lib/gnome-settings-daemon/")
143 SEARCH_DIR("/usr/lib/sasl2/")
144 SEARCH_DIR("/usr/lib/tmpfiles.d/")
145 SEARCH_DIR("/usr/lib/qt/")
146 SEARCH_DIR("/usr/lib/binfmt.d/")
147 SEARCH_DIR("/usr/lib/bonobo/")
148 SEARCH_DIR("/usr/lib/w3m/")
149 SEARCH_DIR("/usr/lib/xserver-xorg-video-intel/")
150 SEARCH_DIR("/usr/lib/telepathy/")
151 SEARCH_DIR("/usr/lib/nautilus/")
152 SEARCH_DIR("/usr/lib/GraphicsMagick-1.3.23/")
153 SEARCH_DIR("/usr/lib/nodejs/")
154 SEARCH_DIR("/usr/lib/dkms/")
155 SEARCH_DIR("/usr/lib/perl5/")
156 SEARCH_DIR("/usr/lib/xscreensaver/")
157 SEARCH_DIR("/usr/lib/binfmt-support/")
158 SEARCH_DIR("/usr/lib/colord/")
159 SEARCH_DIR("/usr/lib/jvm/")
160 SEARCH_DIR("/usr/lib/tar/")
161 SEARCH_DIR("/usr/lib/policykit-1-gnome/")
162 SEARCH_DIR("/usr/lib/mate-notification-daemon/")
163 SEARCH_DIR("/usr/lib/gcc/x86_64-linux-gnu/5/32/")
164 SEARCH_DIR("/usr/local/lib/fpc/3.1.1/units/i386-linux/rtl/")
165 SEARCH_DIR("/usr/local/lib/fpc/3.1.1/")
166 INPUT(
167 /usr/local/lib/fpc/3.1.1/units/i386-linux/rtl/si_prc.o
168 h.o
169 /usr/local/lib/fpc/3.1.1/units/i386-linux/rtl/system.o
170 f1.o
171 /usr/local/lib/fpc/3.1.1/units/i386-linux/rtl-console/crt.o
172 /usr/local/lib/fpc/3.1.1/units/i386-linux/rtl/baseunix.o
173 /usr/local/lib/fpc/3.1.1/units/i386-linux/rtl/unix.o
174 /usr/local/lib/fpc/3.1.1/units/i386-linux/rtl/termio.o
175 /usr/local/lib/fpc/3.1.1/units/i386-linux/rtl/unixtype.o
176 /usr/local/lib/fpc/3.1.1/units/i386-linux/rtl/unixutil.o
177 )
178 SECTIONS
179 {
180   .fpcdata           :
181   {
182     KEEP (*(.fpc .fpc.n_version .fpc.n_links))
183   }
184   .threadvar : { *(.threadvar .threadvar.* .gnu.linkonce.tv.*) }
185 }
186 INSERT AFTER .data;

link.res L167 ~ 176 連結了不少 .o 檔案。

來看看 benchmark Pascal Free Pascal programs versus Java, 還真的不怎麼樣。

ref:

2017年9月9日 星期六

[開箱不敗家] 這不是 brompton


  • 20170815 訂購於淘寶, 3631 rmb = 16600 nt, 18kg, 國際運費 14*18 rmb = 1160 nt (管家集運), 總計費用 16600+1160 = 17760nt。
  • 20170820 管家集運發台灣
  • 20170824 收到
订单号: 46640506133669095513单车配件②店
3688.00
3577.36
1
投诉卖家
3681.12
3631.00
(含运费:¥14.00)
等待买家付款
102.00
89.76
1
投诉卖家

怎麼把這個大東西運回來讓我商透腦筋, 原本用的集貨會改用體積計算, 費用太貴, 在買賣討論區上, 我找到了管家集運, 根據重量計費, 18kg * 14rmb = 1160nt。

我喜歡單車上有土除、貨架、腳架, 這都是我覺得在單車上要有的必備品, 但我目前用有的單車很少全部符合, carryme 勉強符合這些條件。這次購買的這台, 則沒有腳架, 停車時會很不方便, 我知道可以用折疊方式來停車, 但折折疊疊實在煩人, 又我放置了馬鞍袋的時候又該怎麼辦?

本來想購買綠色, 最後挑了還不錯看的米黃色。

嚴格來說, 這台單車不能算是縱向折疊, 前輪部份依舊是橫向折疊, 為了良好的折疊性, 也只能這樣取捨, 這也是我猶豫不決的部份, 所以一直沒下手買 brompton, 二手 brompton 也不便宜, 所以最後購入此車。

剛收到車時, 輪胎是沒有氣的, 這是為了運輸的安全, 帶上飛機就得要這麼做, 將輪胎放氣, 所以一開始得先打飽氣才行。

這不是什麼問題, 對於一個單車愛好者來說, 有個打氣筒並不是什麼奇怪的事情, 氣很快就打飽, 馬上來試車。

嗯! 16 吋輪胎果然騎起來要稍微適應一下, 雖然不像 carryme 那樣, 但和一般單車比起來也是有所不同, 操控性差了點, 能用 brompton 騎山路的人真是好腳力。

規格:

车身折叠参数:长600MM 高600MM 厚26MM
车体净重:12.5KG左右,带纸箱包装毛重15KG
纸箱包装参数:长71CM高64CM宽:35CM



單車有 3 段變速, 老實說功用不大, 這台單車的定位是和大眾運輸系統一起使用, 和 carryme 一樣, 不是 moulton tsr, birdy 的運動風設定, 我沒那麼好的腿力可以靠它騎長距離或是山路, 至少要給我 8 段變速我才能這麼做。

易行輪很好推, 折疊時推起來很順手, 和 birdy 的手感不同, birdy 的後貨架折疊後太不好推了。

這台不是 brompton, 就叫它「小黃」好了。

brompton 據聞專利過期, 開始有車廠在製作這樣折疊機構的單車, 有的是鋁的材質, 但我想要買鐵的版本。

哎呀! 台灣就有了《【單車倉庫 MiNi M3 變速縱向摺疊車】3sixty Mini m3摺疊車 媲美英國小布BROMPTON摺疊體積輕巧》(15900) 我幹麻還去淘寶買, 耍笨了。


早知道就不用這樣折騰了, 價錢比較貴又要等上好幾天, 又要擔心有沒被摔壞, 看了一些評價, 有的單車被摔到了, 貨架歪了, 我這台運氣好, 看起來沒什麼異狀。疑! 加上後貨架 (1500) 和豬鼻子 (590) 之後, 就比我從淘寶買還貴一點。



體積輕巧 縱向小折 「折疊車」 3sixty mini
型號
單車倉庫 MiNi M3 變速縱向摺疊車
車架
鉻鉬鋼Cr-mo
車把
鋁合金把手
變速手把
Sturmey ArcherSUNRACE  (內三速)
鏈條
KMC 防鏽 鏈條
大齒盤
 大盤齒數47T
車圈
6061鋁合金+T6熱處理 雙層輪圈
輪胎
16 外胎 
煞車
鋁合金 C
握把
牛皮舒適握把
花鼓
前鋁合金陽極花鼓  Sturmey ArcherSUNRACE內變速花鼓
踏板
摺疊式腳踏
重量
約11.5公斤  


(金點單車) LaBici 縱向摺疊車 米蘭青色 / 小布 Brompton Birdy (12800), 這是鋁合金車架的版本。

「小黃」也是鐵的材質, 車身沒有任何字樣, 這樣乾乾淨淨也不錯, 嗯! 應該是有特殊原因吧! 騎起來不算好騎, 但也不差, 買便當絕對夠用, 騎長程可能會有點累人。

這款車可能會威脅到 ori, 我曾經有想過買 ori, 但現在有了這台後, 已經不會想買 ori 了, 不過我認為 ori 的騎乘性能還是好過這台的。

試騎一陣子後發現車子後方會有怪聲, 不知道是什麼原因, 出現後過一下子又會消失, 好在這台車設定為短距離騎乘, 可以暫時忽略, 但螺絲掉下來就不能忽略了。

掉下來的螺絲是後貨架的螺絲, 總共有 4 個, 果然都沒鎖緊, 鎖緊後就好多了, 由於折疊會和後貨架有關, 這裡沒鎖緊折疊起來還蠻危險的。

小黃與玻弟
這台車只要是折疊單車愛好者應該都會知道, 沒什麼好介紹, 特規零件比較麻煩, 例如花鼓的部份, sunrace 的內變也還可以, 暫時沒有想做什麼改裝, 3 段變速我得用上最輕的那段, 所以幾乎只有一段, 頂多改成無變速吧!



LM 區是一個很好的騎車地點, 就到那裡試車吧! 小黃的鏈條出了點狀況, 脫離一個齒輪, 沒問題, 把鏈條掛回該齒輪即可。

短程看來沒什麼問題, 可以和大眾交通工具接駁是我最需要的部份。

至於豬鼻子 (就是用來掛前菜籃的一個裝置) 還沒裝上, 暫時沒有什麼心得。

ref:
捷世樂® 單車休閒 ~ BROMPTON 特殊色【灰色地帶】 ,經銷商試乘車,試乘亮相

2017年9月1日 星期五

function reentrant, thread-safe, memory ordering (memory barrier)

the 1st edition: 20130301
the 2nd edition: 20151001
the 3rd edition: 20170727
這篇是個雜記, 紀錄這三個惱人的東西還有一些自己的問題。

http://tw.myblog.yahoo.com/blue-comic/article?mid=253&prev=257&l=f&fid=26
http://blog.xuite.net/jackie.xie/bluelove/46644355

在寫怎麼樣的程式時, 需要考慮 reentrant?
在寫怎麼樣的程式時, 需要考慮 thread-safe?

在 multi-thread 程式中, 需要的是 reentrant 還是 thread-safe?

Fast Atomic Counters With the x86 LOCK Prefix:
http://www.codemaestro.com/reviews/8

問題很混亂, 不過看了 pthread 多緒程式設計 (p237), 有了比較清楚的概念:

簡單提一下 (詳細請參閱該書籍):

thread safe: 使用 mutex 來保護 global data, 而 data 不一定是變數, 可能是檔案, 可能是某個週邊。由於用了 lock, 很有可能會有 dead lock 的問題。

reentrant function: 不可使用 static, global data, 一定可以正常執行, 也不會有 dead lock 的問題。

那 reentrant function 裡頭用 mutex 保護共用資源算 thread safe 嗎? 算, 不過那就不是 reentrant function 了, 請參考 malloc() is non-reentrant but thread-safe? [duplicate] 提到 reentrant, thread-safe 是如何的不同?

malloc 是 thread safe, 但不是 reentrant, 該怎麼解釋呢? reentrant 和 thread 的行為有點微妙的不同 (感謝 tg 的朋友解惑):

list 1. thread
1 thread:
2 void thread_1()
3 {
4   malloc()
5   {
6     lock();
7 
8     set_list();
9 
10     unlock();
11   }
12 }
 
13 void thread_2()
14 {
15   malloc()
16   {
17     lock();
18 
19     set_list();
20 
21    unlock();
    }
   }

thread_1() 執行到 set_list() 時, 換 thread_2 執行, list 1 L17 會卡住 thread_2, 等到執行權切回 list 1 L8 後, 執行 L10 之後, unlock, 若這時候切回 thread_2, thread_2 得以繼續執行。

reentrant 會和中斷扯上關係:

list 2. reentrant
01 void func_1()
02 {
03   malloc()
04   {
05     lock();
06 
07     set_list();
08 
09     unlock();
10   }
11 }
 
12 void isr_1()
13 {
14   malloc()
15   {
16     lock();
17 
18     set_list();
19 
20     unlock();
     }
   }

func_1 執行到 set_list() 時, 中斷發動了, 改為執行 isr_1(), isr_1 卡在 list 2 L16, 但程式再也回不到 list 2 L7 那行了, 在中斷 isr 中, 沒有做完是切不回去 func_1 的, 這是和 thread 最不一樣的行為, 所以會一直卡在 list 2 16 那行。

所以才說, malloc 是 thread safe, 但不是 reentrant。

Reentrancy and Thread-Safety
Hence, a thread-safe function is always reentrant, but a reentrant function is not always thread-safe.

Linux环境编程:从应用到内核 (Linux/Unix技术丛书) 0.4.5 可重入函数

可重入函数一定是线程安全的, 而线程安全函数则不一定是可重入函数。

以上兩個說法完全相反, 該聽誰的呢?

我自己是贊同《Linux 环境编程》的說法。但不用太拘泥名詞解釋, 了解本質上的不同是比較重要的。

reentrant.cpp
 1 #include <signal.h>
 2 #include <pthread.h>
 3 
 4 #include <chrono>
 5 #include <iostream>
 6 using namespace std;
 7 using namespace std::chrono;
 8 
 9 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
10 
11 volatile int i=5;
12 
13 void thread_safe_func()
14 {
15   pthread_mutex_lock(&mutex);
16 
17   for (int i=0 ; i < 200000 ; ++i)
18     for (int i=0 ; i < 10000 ; ++i)
19     {
20     }
21   i = 1;
22 
23   pthread_mutex_unlock(&mutex);
24 }
25 
26 void sig_handle(int sig)
27 {
28   thread_safe_func();
29 }
30 
31 int main(int argc, char *argv[])
32 {
33   signal(SIGINT, sig_handle);
34   thread_safe_func();
35   #if 0
36   time_point<steady_clock> tp1 = steady_clock::now();
37 
38 
39   time_point<steady_clock> tp2 = steady_clock::now();
40 
41   typedef duration<unsigned long long, ratio<1,1000000>> unit;
42   unit d = duration_cast<unit>(tp2 - tp1);
43   cout << d.count() << endl;
44   #endif
45   return 0;
46 }

reentrant.cpp 是我模仿 malloc 而來, 在程式執行後, 按下 ctrl+c SIGINT, 這時候用 ps 觀察

ps aux |grep reentrant
descent   7210  0.2  0.0  15232  1640 pts/68   S+   10:12   0:01 ./reentrant

PROCESS STATE CODES
  Here are the different values that the s, stat and state output specifiers 
  (header "STAT" or "S") will display to describe the state of a process:

          D    uninterruptible sleep (usually IO)
          R    running or runnable (on run queue)
          S    interruptible sleep (waiting for an event to complete)
          T    stopped, either by a job control signal or because it is being traced
          W    paging (not valid since the 2.6.xx kernel)
          X    dead (should never be seen)
          Z    defunct ("zombie") process, terminated but not reaped by its parent

  For BSD formats and when the stat keyword is used, additional characters may be displayed:

          <    high-priority (not nice to other users)
          N    low-priority (nice to other users)
          L    has pages locked into memory (for real-time and custom IO)
          s    is a session leader
          l    is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
          +    is in the foreground process group

處在 interruptible sleep (waiting for an event to complete), 程式無法正常結束, 應該是卡在 L15。

reentrant.cpp L17, L18 的 for loop 單純用來 delay, 在我系統上, 大概是 3 秒, 為的是讓 SIGINT 有時間可以在

15   pthread_mutex_lock(&mutex);
            ...
23   pthread_mutex_unlock(&mutex);

這中間的時候送出。

而程式也無法正常結束。

ref What does the “interruptible sleep” state indicate?


ref:



再來是 memory ordering (memory barrier) 這問題:

我也被這問題困惑很久, 在 os 下寫應用程式, 幾乎沒用過這種指令, 有需要在 os 下的應用程式使用這些指令嗎? 還是被 os api 給包裝起來了呢?

這是 linux 在 x86 下使用的 memory barrier 指令。
#define mb() asm volatile("mfence":::"memory")
#define rmb() asm volatile("lfence":::"memory")
#define wmb() asm volatile("sfence" ::: "memory")

arm cortex m3 memory barrier 指令: isb, dsb

Binary Hacks - 駭客秘傳技巧一百招 #94 在談這個, 看完就清楚了, 可是我不知道怎麼寫程式測試這個情形。

不過這樣的話, 在 os 下寫應用程式也會遇到這問題才是, 可是卻沒看過在 os 下的 c 應用程式用上這種指令。

multi-thread 程式要突破兩關:
  1. lock 機制 (使用 test-and-set 硬體指令實作)
  2. memory ordering (使用 memory barrier 指令)
才有正確的結果。

1 通常由 os api 提供 (x86 可用 bts/xchg 實作, arm cortex M3 可用 ldrex/strex 來實作)。
2 我就沒看過在 os 下的應用程式要怎麼用, 只看過在 driver 階段有使用過。這篇提到 memory barrier pthread 已經實作在裡頭了。

thread 程式的共享變數還需要用 volatile 來宣告, 僅僅加上 lock 是不夠的。
ref:
http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=1920275
http://www.parallellabs.com/2010/12/04/why-should-we-be-care-of-volatile-keyword-in-multithreaded-applications/

ref memory barrier (ptt damody provides):

下一個類似的問題是:
mutex/semaphore and spin lock, 你知道的, 我要的不是教科書的解釋, 我想要實作這些東西出來, 能實作出來就真能代表懂了。

目前我在 arm cortex-m3 使用 ldrex/strex 實作出 spin lock。

Linux内核同步机制之(一):原子操作
這篇做了很好的解釋。

Linux内核同步机制之(三):memory barrier

蜗窝科技出版的文章, 質/量都很好。