# 2018q3 Homework4 (assessment)
contributed by < `happyincent` >
## Environment
```
$ cat <(echo "CPU: " `lscpu | grep "Model name" | cut -d':' -f2 | sed "s/ //"`) <(echo "OS: " `lsb_release -d | cut -f2`) <(echo "Kernel: " `uname -a | cut -d' ' -f1,3,14`) <(echo "gcc: " `gcc --version | head -n1`) <(echo "bash: " `bash --version | head -n1`)
CPU: Intel(R) Xeon(R) CPU E5520 @ 2.27GHz
OS: Ubuntu 16.04.5 LTS
Kernel: Linux 4.15.0-36-generic x86_64
gcc: gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
bash: GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
```
## [第 4 週測驗題(上)](https://hackmd.io/s/HyyxpJE5X)
```c
#include <stdint.h>
int64_t abs64(int64_t x) {
int64_t y = x >> (64 - 1);
return (x ^ y) - y;
}
```
#### 運作原理
* x 的 signed bits 為 0 時
* y 為 `0x0000000000000000`
* (x ^ 0) - 0 還是 `x`
* x 的 signed bits 為 1 時
* y 為 `0xffffffffffffffff` (-1) (gcc)
* (x ^ y) - y
* x 做 [1's complement](https://en.wikipedia.org/wiki/Ones%27_complement) 後 - (-1)
```
x + ~x = -1 // 1's complement
~x - (-1) = x // (x ^ y) - y
```
* 與 x 做 [2's complement](https://en.wikipedia.org/wiki/Two%27s_complement) 相同
* Invert all the bits and add one to the result
* `>>` (右移)
* [C99 6.5.7](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf)
> The result of **E1 >> E2** is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2^E2^. ==If E1 has a signed type and a negative value, the resulting value is implementation-defined.==
* [gcc - Integers](https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html)
> Bitwise operators act on the representation of the value including both the **sign** and value bits, where the sign bit is considered immediately above the highest-value value bit. ==Signed ‘>>’ acts on negative numbers by sign extension.==
#### 延伸問題
* 可能的 overflow/underflow 議題
* 絕對值定義 [[wiki](https://en.wikipedia.org/wiki/Absolute_value)]
![](https://i.imgur.com/hgm9sSq.png)
* 從 wiki 的 [2's complement](https://en.wikipedia.org/wiki/Two%27s_complement#Most_negative_number) 得知當 x 為 `int64_t` 中最小的負數 `0x8000000000000000` 時,會發生 ==overflow== (there was a carry into but not out of the most-significant bit)
```c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int64_t abs64(int64_t x) {
int64_t y = x >> (64 - 1);
return (x ^ y) - y;
}
int main()
{
int64_t minInt64 = 0x8000000000000000;
printf("0x%016lx %ld\n", minInt64, minInt64);
printf("0x%016lx %ld\n", abs64(minInt64), abs64(minInt64));
printf("0x%016llx %lld\n", llabs(minInt64), llabs(minInt64));
}
```
```
$ gcc -Wall a.c && ./a.out
0x8000000000000000 -9223372036854775808
0x8000000000000000 -9223372036854775808
0x8000000000000000 -9223372036854775808
```
* 二補數 `int64_t` 中的最小負數 `0x8000000000000000` 經過題目的 abs64 或是 `stdlib.h` 中的 llabs 輸出與原值相同,都不符合絕對值定義
* 0 經過 abs64、llabs 輸出 (0, +overflow) 與原值相同,但符合絕對值定義
* 搭配下方 pseudo-random number generator (PRNG) 和考量到前述 (1),撰寫 `abs64` 的測試程式,並探討工程議題
```C=
static uint64_t r = 0xdeadbeef
int64_t rand64() {
r ^= r >> 12;
r ^= r << 25;
r ^= r >> 27;
return (int64_t) (r * 2685821657736338717);
}
```
* 能否在有限時間內對 int64_t 數值範圍測試完畢?
* 可以,根據 [xorshift wiki](https://en.wikipedia.org/wiki/Xorshift#xorshift*),64-bit generator 最大值為 2^64^ -1 (`0xffffffffffffffff`),因此可以測試完 `int64_t` 的所有數值
## [第 4 週測驗題(中)](https://hackmd.io/s/Syl6me49Q)
考慮以下程式碼:
```C=
int *global_var;
void f(void)
{
int x = 3;
global_var = &x;
...
/* Can the compiler perform TCO here? */
g();
}
```
* `(a)` 編譯器不可能施加 TCO
* `(b)` 編譯器一定可施加 TCO
* `(c)` 只要函式 `g` 沒有對 `global_var` 指標作 dereference,那麼 TCO 就有機會
思考程式註解,在第 8 行能否施加 TCO 呢?選出最適合的解釋。
Ans: None
### TCO 原理
#### Tail call
a tail call is a subroutine call performed as the final action of a procedure [[wiki](https://en.wikipedia.org/wiki/Tail_call)]
[example](http://www.ruanyifeng.com/blog/2015/04/tail-call.html):
* 函式 g 為 f 中最後執行的動作,g 即為 tail call
```
function f(x){
return g(x);
}
```
* 函示 g 之後還有別的動作,因此 g 不是 tail call
```
function f(x){
let y = g(x);
return y;
}
---
function f(x){
return g(x) + 1;
}
```
* stack frame
![](https://i.imgur.com/YVAAI4w.png)
#### Tail call optimization (Elimination)
以 [tco-test](https://github.com/sysprog21/tco-test) 的 No arguments 為例:
> [__builtin_return_address](https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html)
> This function returns the return address of the current function, or of one of its callers.
> A value of 0 yields the return address of the current function.
```c
#include <stdio.h>
void *first_ra;
void *second_ra;
void first_zero (void);
void second_zero (void);
void first_zero (void) { first_ra = __builtin_return_address(0); second_zero (); }
void second_zero (void) { second_ra = __builtin_return_address(0); }
int main()
{
printf("%-45s", "No arguments" ":");
first_zero();
printf(first_ra == second_ra ? "TCO\n" : "no TCO\n");
}
```
以 `-O0` 編譯
```
$ gcc -O0 a.c && ./a.out && gdb -q a.out
No arguments: no TCO
Reading symbols from a.out...(no debugging symbols found)...done.
gdb-peda$ disas first_zero
Dump of assembler code for function first_zero:
0x0000000000400526 <+0>: push rbp
0x0000000000400527 <+1>: mov rbp,rsp
0x000000000040052a <+4>: mov rax,QWORD PTR [rbp+0x8]
0x000000000040052e <+8>: mov QWORD PTR [rip+0x200b0b],rax # 0x601040 <first_ra>
0x0000000000400535 <+15>: call 0x40053d <second_zero>
0x000000000040053a <+20>: nop
0x000000000040053b <+21>: pop rbp
0x000000000040053c <+22>: ret
End of assembler dump.
gdb-peda$ disas second_zero
Dump of assembler code for function second_zero:
0x000000000040053d <+0>: push rbp
0x000000000040053e <+1>: mov rbp,rsp
0x0000000000400541 <+4>: mov rax,QWORD PTR [rbp+0x8]
0x0000000000400545 <+8>: mov QWORD PTR [rip+0x200afc],rax # 0x601048 <second_ra>
0x000000000040054c <+15>: nop
0x000000000040054d <+16>: pop rbp
0x000000000040054e <+17>: ret
End of assembler dump.
```
以 `-O1` 編譯
```
$ gcc -O1 a.c && ./a.out && gdb -q a.out
No arguments: TCO
Reading symbols from a.out...(no debugging symbols found)...done.
gdb-peda$ disas first_zero
Dump of assembler code for function first_zero:
0x0000000000400546 <+0>: mov rax,QWORD PTR [rsp]
0x000000000040054a <+4>: mov QWORD PTR [rip+0x200aef],rax # 0x601040 <first_ra>
0x0000000000400551 <+11>: mov QWORD PTR [rip+0x200af0],rax # 0x601048 <second_ra>
0x0000000000400558 <+18>: ret
End of assembler dump.
```
:::info
從 gdb 的結果中可以看出以`-O1` 做 TCO 最佳化後,`first_zero` 並沒有 call `second_zero`,而是直接將 `rax` assign 給 `second_ra`。
:::
### 題目解釋
改寫題目:
```c=
#include <stdio.h>
void *first_ra;
void *second_ra;
int *global_var;
int g()
{
second_ra = __builtin_return_address(0);
// int a = *global_var;
// global_var = &a;
// return *global_var;
}
void f(void)
{
first_ra = __builtin_return_address(0);
int x = 3;
global_var = &x;
g();
}
int main()
{
f();
printf(first_ra == second_ra ? "TCO\n" : "no TCO\n");
}
```
在以 `-O1` 編譯的情況下
* 只加第 10 行,雖然有對 `global_var` 指標做 dereference,但因為函示 return (0) 時沒有用到 `global_var` 因此輸出為 ==TCO==
* 只加第 11 行,改動到 `global_var` 因此輸出為 ==no TCO==
* 只加第 12 行,return 時用到 `global_var` 因此輸出為 ==no TCO==
## [第 4 週測驗題(下)](https://hackmd.io/s/By7Lwz4qm)
分別考慮以下 4 個程式,探討其行為。
- `ptr1.c`
```C
int main() { return *((int *) 0); }
```
- Ans: 在執行時期會造成 Segmentation fault
- 先將 0 轉型為 NULL pointer,再對 NULL pointer 做 dereference
> C99 - 6.5.3.2 - 4
If an invalid value has been assigned to the pointer, the behavior of the unary * operator is **undefined**.`87)`
`87)` Among the invalid values for dereferencing a pointer by the unary * operator are **a null pointer**, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.
- ==Segmentation fault== 訊息
```
(gdb) disas main
Dump of assembler code for function main:
0x00000000004004d6 <+0>: push rbp
0x00000000004004d7 <+1>: mov rbp,rsp
0x00000000004004da <+4>: mov eax,0x0
0x00000000004004df <+9>: mov eax,DWORD PTR [rax]
0x00000000004004e1 <+11>: pop rbp
0x00000000004004e2 <+12>: ret
End of assembler dump.
(gdb) r
Starting program: /home/ddl/a.out
Program received signal SIGSEGV, Segmentation fault.
0x00000000004004df in main () at a.c:1
```
- 以 gdb 看出在執行 `mov eax, DWORD PTR [rax]` 時發生 Segmentation fault
- Move 4 bytes at memory address rax (0x0) into eax
- the 64-bit version of 'EAX' is called 'RAX' ([x86 architecture](https://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture))
- the caller can expect to find the return value of the subroutine in the register EAX ([x86 Assembly Guide](http://www.cs.virginia.edu/~evans/cs216/guides/x86.html))
- SIGSEGV
- an invalid access to storage, C99 7.14.3
- What happens in OS when we dereference a NULL pointer in C? [[stackoverflow](https://stackoverflow.com/questions/12645647/what-happens-in-os-when-we-dereference-a-null-pointer-in-c)]
- run user-level code in protected mode, which uses paging to convert virtual addresses into physical addresses.
- For each process, the OS keeps a page table which dictates how the addresses are mapped.
- if any memory access generates an invalid address, the processor raises a page fault exception. This triggers a transition from user mode into kernel mode
- The kernel regains control and, based on the information from the exception and the process's page table, figures out what happened.
- Page Fault handler [[source](https://mhackeroni.it/archive/2018/06/29/google-ctf-2018-execve-sandbox.html)]
![](https://i.imgur.com/TLiIKsw.png)
- do_page_fault() Flow Diagram [[source](https://sungju.github.io/kernel/internals/memory_management)]
![](https://i.imgur.com/3KR86mG.png)
- `ptr2.c`
```C
int main() { return &*((int *) 0); }
```
- Ans: 是合法 C 程式,在執行後可透過 echo $? 得到 exit code 為 0
- `&*((int *) 0)` 等於 `((int *) 0)` 等於 a null pointer with the value 0
> C99 6.3.2.3 - 3
An integer constant expression with the **value 0**, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called **a null pointer**
> C99 - 6.5.3.2 - 87)
&*E is equivalent to E (even if E is a null pointer)
- ==warning==: return makes integer from pointer without a cast [-Wint-conversion]
- 與 main 的 return type (int) 不同
- `ptr3.c`
```C
#include <stddef.h>
int main() { return &*NULL; }
```
- Ans: 是合法 C 程式,在執行後可透過 echo $? 得到 exit code 為 0
- 同 `ptr2.c`
> C99 7.17 - 3
The macros are **NULL** which expands to an **implementation-defined null pointer constant**;
- ==warning==:
- dereferencing ‘void *’ pointer
- C99 - 6.5.3.2 - 4
- return makes integer from pointer without a cast
- 與 main 的 return type (int) 不同
- `ptr4.c`
```C
#include <stddef.h>
int main() {
return &*(*main - (ptrdiff_t) **main);
}
```
- Ans: 是合法 C 程式,在執行後可透過 echo $? 得到 exit code 為 0
- `main`、`*main`、`**main` 為 function designator,被轉成 pointer 進行運算
> C99 6.5.3.2 - 4
The unary * operator denotes indirection. If the operand points to a function, the result is a **function designator**;
> C99 6.3.2 - 4
A function designator is an expression that has function type. Except when it is the operand of the sizeof operator or the unary & operator, ++a **function designator** with type ‘‘function returning type’’ is converted to **an expression** that has type ‘‘pointer to function returning type’’.++
- `*main - (ptrdiff_t) **main` 運算完還是 pointer
> C99 7.17 - 2
The types are **ptrdiff_t** which is the **signed integer type** of the result of subtracting two pointers
> C99 6.5.6 - 8
When an expression that has **integer type** is added to or **subtracted from a pointer**, the result has the type of the **pointer** operand
- `&*(*main - (ptrdiff_t) **main)` == `*main - (ptrdiff_t) **main`
> C99 - 6.5.3.2 - 87)
&*E is equivalent to E (even if E is a **null pointer**)
- 小實驗
```c
#include <stdio.h>
#include <stddef.h>
typedef int (*func)();
int main()
{
__auto_type a1 = main - (ptrdiff_t)main;
__auto_type a2 = main - main;
// __auto_type a3 = (ptrdiff_t)main - main;
__auto_type b1 = &*( a1 );
// __auto_type b2 = &*( a2 );
printf("%d\n", __builtin_types_compatible_p( typeof(a1), func )); // 1
printf("%d\n", __builtin_types_compatible_p( typeof(a2), long int )); // 1
printf("%d\n", __builtin_types_compatible_p( typeof(b1), func )); // 1
/*
error: invalid operands to binary - (have ‘long int’ and ‘int (*)()’)
__auto_type a3 = (ptrdiff_t)main - main;
^
error: invalid type argument of unary ‘*’ (have ‘long int’)
__auto_type b2 = &*( a2 );
^
*/
}
```
> C99 6.2.5 - 18
Integer and floating types are collectively called **arithmetic types**
> C99 6.5.6
For subtraction, one of the following shall hold:
— both operands have arithmetic type;
— both operands are pointers to qualified or unqualified versions of compatible object types; or
==—== the left operand is a pointer to an object type and the right operand has integer type.
> C99 6.5.3.2 - 2
The operand of the unary * operator shall have pointer type.
- ==warning==: return makes integer from pointer without a cast [-Wint-conversion]
- 與 main 的 return type (int) 不同
## 閱讀啟發、學習感想
> 人不付出犧牲,就得不到任何回報。如果要得到什麼,就必須付出同等的代價,這就是鍊金術的基本原則,等價交換。當時我們深信著,這就是這世界的真理。------《鋼之鍊金術師》