Home » Posts tagged "compiler"

Windows 上的 Chrome 改用 Clang 編譯

這應該是上個禮拜蠻熱鬧的一件事情 (i.e. 很多人看熱鬧 XD),就是 Google ChromeVisual C++ 改成用 Clang 編譯:「Clang is now used to build Chrome for Windows」。

文章裡推敲效能應該不是主要的因素,因為在不同項目測試下各有千秋,而且差距都不大:

We conducted extensive A/B testing of performance. Performance telemetry numbers are about the same for MSVC-built and clang-built Chrome – some metrics get better, some get worse, but all of them are within 5% of each other.

後面有猜測換過去的原因,可以看出因為 open source 加上 Google Chrome 團隊有強大的技術能力下,用 open source 軟體可以對 compiler 大量客製化各種功能,另外也是因為一個 compiler 就可以吃多個平台,可以省下一些跨平台的力氣 (像是相容性語法)。

而 Visual C++ 在商業支援與文件兩方面比較好的優勢,在這個情況下就顯得不是那麼重要了...

讀書時間:Spectre 的攻擊方式

上次寫了 Meltdown 攻擊的讀書心得 (參考「讀書時間:Meltdown 的攻擊方式」),結果後來中獎狂流鼻水,加上 Spectre 用的手法就更複雜,慢慢看的情況就拖到最近才看完... 這邊就以讀者看過 Meltdown 那篇心得的前提來描述 Spectre。

Spectre 的精華在於 CPU 支援 branch prediction 與 out-of-order execution,也就是 CPU 遇到 branch 時會學習怎麼跑,這個資訊提供給 out-of-order execution 就可以大幅提昇執行速度。可以參考以前在「CPU Branch Prediction 的成本...」提到的效率問題。

原理的部份可以看這段程式碼:

這類型程式碼常常出現在現代程式的各種安全檢查上:確認 x 沒問題後再實際將資料拉出來處理。而我們可以透過不斷的丟 x 值進去,讓 CPU 學到以為都是 TRUE,而在 CPU 學壞之後,突然丟進超出範圍的 x,產生 branch misprediction,但卻已經因為 out-of-order execution 而讓 CPU 執行過 y = ... 這段指令,進而導致 cache 的內容改變。

然後其中讓人最驚豔的攻擊,就是論文示範了透過瀏覽器的 JavaScript 就能打的讓人不要不要的...

圖片裡,上面這段是 JavaScript 程式碼,下面則是 Chrome V8JIT 後轉成的 assembly (這是 AT&T style):

可以從這段程式碼看到,他想要透過這段 JavaScript 取出本來無法存取到的祕密值 index,然後透過 probeTable 得知 cache 的變化。

在這樣的攻擊下,你就可以取得這個 process 裡可以看到的空間,甚至極端的 case 下有可能是 kernel space (配合 Meltdown 的條件)。

不過如果你不能跑 JavaScript 也沒關係,Spectre 的論文裡也提供各種變形方式提供攻擊。像是這樣的程式碼也可以被拿來攻擊:

if (false but mispredicts as true)
    read array1[R1]
read [R2]

其中 R1 是有帶有祕密值的 register,當 array[R1] 有 cache 時,讀 [R2] 就有機會比較快,而沒有 cache 時就會比較慢 (這是因為 memory bus 被佔用的關係),在這個情境下就能夠產生 timing attack:

Suppose register R1 contains a secret value. If the speculatively executed memory read of array1[R1] is a cache hit, then nothing will go on the memory bus and the read from [R2] will initiate quickly. If the read of array1[R1] is a cache miss, then the second read may take longer, resulting in different timing for the victim thread.

所以相同道理,利用乘法器被佔用的 timing attack 也可以產生攻擊:

if (false but mispredicts as true)
    multiply R1, R2
multiply R3, R4

在論文裡面提到相當多的方法 (甚至連 branch target buffers (BTB) 都可以拿來用),就麻煩去論文裡看了。現在用 cache 算是很有效的方式,所以攻擊手法主要都是透過 cache 在取得資訊。

Spectre 論文提到的 mitigation (workaround) 是透過 mfencelfence 強制程式碼的順序,但這表示 compiler 要針對所有的 branch 加上這段,對效能影響應該蠻明顯的:

In addition, of the three user-mode serializing instructions listed by Intel, only cpuid can be used in normal code, and it destroys many registers. The mfence and lfence (but not sfence) instructions also appear to work, with the added benefit that they do not destroy register contents. Their behavior with respect to speculative execution is not defined, however, so they may not work in all CPUs or system configurations.

Google 推出的 Retpoline 則是想要避免這個問題。Google 在「Retpoline: a software construct for preventing branch-target-injection」這邊詳細說明了 Retpoline 的原理與方法,採取的方向是控制 speculative execution:

However, we may manipulate its generation to control speculative execution while modifying the visible, on-stack value to direct how the branch is actually retired.

這個方式是抽換掉 jmpcall 兩個指令,以 *%r11 為例,他將 jmp *%r11call *%r11 改成 jmp retpoline_r11_trampolinecall retpoline_r11_trampoline (這邊的 jmp 指的是所有 jump 系列的指令,像是 jz 之類的):

retpoline_r11_trampoline:
  call set_up_target;
capture_spec:        
  pause;
  jmp capture_spec;
set_up_target:
  mov %r11, (%rsp); 
  ret;

藉由抽換 %rsp 內容跳回正確位置,然後也利用這樣的程式結構控制 CPU 的 speculative execution。

而在效能損失上,已經有測試報告出來了。其實並沒有像 Google 說的那麼無痛,還是會因為應用差異而有不同等級的效能損失... 可以看到有些應用其實還是很痛:「Benchmarking Linux With The Retpoline Patches For Spectre」。

下半年新出的 CPU 應該會考慮這些問題了吧,不過不知道怎麼提供解法 @_@

GCC 的警告參數

在「Useful GCC warning options not enabled by -Wall -Wextra」這邊提到 GCC 如果把這些參數放入 -Wall-Wextra 裡會產生出太多的誤判,但有些參數還是很有用。

不過這邊介紹的參數蠻多都需要 GCC 6+ 甚至 GCC 7+,翻了 Ubuntu 16.04 是 GCC 5.4。

這是文章介紹的參數的整理:

-Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wrestrict -Wnull-dereference -Wold-style-cast -Wuseless-cast -Wjump-misses-init -Wdouble-promotion -Wshadow -Wformat=2

另外一個比較特別的問題是,其實愈來愈多專案搬到 clang 上,這幾年可以看出能量的消長蠻明顯的...

Webkit 推出 B3 JIT Compiler (Bare Bones Backend)

Webkit 推出了 B3 加快 optimization 的速度,取代原來 LLVM 的工作:「Introducing the B3 JIT Compiler」。

在文章後方 Performance Results 的部份可以看到最主要的差異在啟動時間:

另外也可以看到其他各種 performance benchmark 也幾乎都是小勝 LLVM。

接下來會有 ARM64 與其他平台的計畫:

B3 is not yet complete. We still need to finish porting B3 to ARM64. B3 passes all tests, but we haven’t finished optimizing performance on ARM. Once all platforms that used the FTL switch to B3, we plan to remove LLVM support from the FTL JIT.

Android NDK 宣佈向 Clang 靠攏...

Hacker News Daily 上看到 Android NDK 宣佈向 Clang 靠過去的消息:「Changelog for NDK Build 2490520」。

Android NDK 做為效能的加速手段而使用到 C 或是 C++,所以會使用對應的 compiler suite:

The NDK is a toolset that allows you to implement parts of your app using native-code languages such as C and C++. Typically, good use cases for the NDK are CPU-intensive applications such as game engines, signal processing, and physics simulation.

而粗體的兩段則說明了遷移:

PSA: Everyone should be switching to Clang.

以及:

GCC in the NDK is now deprecated.

用統計方法反推 JavaScript 壓縮程式的變數名稱

JavaScript 在正式提供服務時一般都會使用「YUI Compressor」、「UglifyJS」或是「Closure Compiler」壓縮後再拿到正式環境上使用,最主要的目的是為了降低網路傳輸量。

這些工具其中一個特點是,local function 與 local variable 會被較短的名字取代掉,這可以讓想要反組譯的人比較麻煩。

不過今天看到的這個工具可以解決「困擾」:「JSNice」。(在 Slashdot 上的「Machine Learning Used For JavaScript Code De-obfuscation」這篇看到的)

用統計方法去「猜測」這些 local function 與 local variable 應該叫什麼名字,讓人比較好理解。官方對準確度的說法是超過 60%:

In our experiments, we found JSNice to be effective for deobfuscating minified code. On average, more than 60% of the identifiers are recovered to the same name as before the minification process.

接下來會想辦法提供 UI 讓使用者可以選擇另外的名字:

Further, as JSNice computes multiple ranked suggestions, we provide a UI to navigate through these suggestions and select alternative identifier names.

先記錄起來,這網站很有趣,之後要 trace 別人的程式碼應該常常會用到 XDDD

Go 的 self-boot 計畫

Go 的 self-boot 計畫,也就是用 Go compiler 編 Go compiler:「Russ Cox – porting the Go compiler from C to Go」。

其中提到:

The goal is to convert *their* C code (not all C code). They want generated code to be human-readable and maintainable. They want automatic conversion to handle 99+% of code.

第一波想要用機器轉換過去,而且要轉出可維護的程式碼。可以馬上想到的事情是,如果這件事情成功,代表現有軟體的 C code 也有機會轉移?

接下來了幾個版本會開始發展整套機制,有得瞧了 :p

FreeBSD 預設的 compiler 從 GCC 換成 clang

FreeBSD 預設的 compiler (/usr/bin/cc/usr/bin/c++/usr/bin/cpp) 從 GCC 4.2.1 換成 clang 了:「Revision 242624」。

目前只有 CURRENT 裡的 amd64 與 i386 版本換過去,如此一來,FreeBSD 10.0 會是第一個使用 clang 作為預設編輯器的正式版本 (看起來不像會 back port 回 FreeBSD 9)。接下來是 Ports 大混戰了,應該會有一卡車的 ports 用 USE_GCC=yes 來解?

從 2009 年的努力到現在三年多了 (參考「[ANNOUNCE]: clang/llvm can compile booting FreeBSD kernel on i386/amd64」這封公告信),離拔掉 GCC 4.2.1 又進了一大步...

clang 的進展...

OSnews 上看到 clang 2.8 與其他幾個 compiler 的比較:「Compiler Benchmarks of GCC, LLVM-GCC, DragonEgg, Clang」。

以測試結果來說,如果改用 Clang,有些會變得比較快,但也有些變得比較差。不過以 license 以及後勁來看,還蠻看好發展的。六月的時候在 FreeBSD 的 -HEAD 裡面變成 default compiler,預定 9.0-RELEASE 會是 FreeBSD 上第一個用 clang 的版本... (感謝 lwhsu 戳中)

Archives