> ## Content Index
> Fetch the complete content index at: https://blog.hacking.pub/llms.txt
> Use this file to discover other available public pages before exploring further.

# 一道变量compare题的延伸
- URL: https://blog.hacking.pub/2019/07/10/untitled-4/
- Published: 2019-07-10T08:59:47.000Z
- Updated: 2019-07-10T11:19:32.000Z
- Author: chainhelen

我遇到的问题是第4道例子，建议读者先不要看答案，因为代码本身非常简单。  
先[下载题目](http://img.hacking.pub/share/go%5Fbuild2.pdf?ref=blog.hacking.pub)，看看一共5道例子能不能完全做对，这样看到答案的时候会感到更有趣。 

![](https://blog.hacking.pub/qiniu/share/share.jpg)

判断下面的例子， `a`和`b`是否相等

#### 例子一

```c
// compare_1.c
#include <stdio.h>

int main() {
	char a[11] = "helloworld";
	char b[11] = "helloworld";

	if (a == b) {
		printf("equal\n");
	} else {
		printf("not equal\n");
	}

	return 0;
}

```

答案是`not equal`，这里面正常`gcc`的版本直接优化掉了流程，`.rodata` 和`.data`都没有`equal`

而且`.text`发现了`helloworld`立即数，证据如下

```
objdump -s -j .rodata compare_1                                          
Contents of section .rodata:
 0790 01000200 6e6f7420 65717561 6c00      ....not equal.  

objdump -s -j .data compare_1                                                             
Contents of section .data:
 201000 00000000 00000000 08102000 00000000  .......... .....

objdump -s -j .text compare_1  
...
400570 68656c6c 6f776f72 488945e2 66c745ea  helloworH.E.f.E.
...

```

> 如果定义成char a\[11\] = "helloworld\\0"，对于常量字符串的存储不一样，但是对于结果优化一致

#### 例子二

```c
// compare_2.c
#include <stdio.h>

int main () {
	char a[11];
	char b[11];

	scanf("%s", a); // 输入 helloworld
	getchar(); //吞掉回车
	scanf("%s", b); //输入 helloworld

	if (a == b) {
		printf("equal\n");
	} else {
		printf("not equal\n");
	}

	return 0;
}

```

答案是 `not equal`

> 使用ida调试的时候，./linux\_serverx64 < input.txt 来保证标准输入

```
0000000000400607 <main>:
  400607:	55                   	push   %rbp
  400608:	48 89 e5             	mov    %rsp,%rbp
  40060b:	48 83 ec 20          	sub    $0x20,%rsp
  40060f:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  400616:	00 00 
  400618:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  40061c:	31 c0                	xor    %eax,%eax
  40061e:	48 8d 45 e2          	lea    -0x1e(%rbp),%rax
  400622:	48 89 c6             	mov    %rax,%rsi
  400625:	48 8d 3d d8 00 00 00 	lea    0xd8(%rip),%rdi        # 400704 <_IO_stdin_used+0x4>
  40062c:	b8 00 00 00 00       	mov    $0x0,%eax
  400631:	e8 da fe ff ff       	callq  400510 <__isoc99_scanf@plt>
  400636:	e8 c5 fe ff ff       	callq  400500 <getchar@plt>
  40063b:	48 8d 45 ed          	lea    -0x13(%rbp),%rax
  40063f:	48 89 c6             	mov    %rax,%rsi
  400642:	48 8d 3d bb 00 00 00 	lea    0xbb(%rip),%rdi        # 400704 <_IO_stdin_used+0x4>
  400649:	b8 00 00 00 00       	mov    $0x0,%eax
  40064e:	e8 bd fe ff ff       	callq  400510 <__isoc99_scanf@plt>
  400653:	48 8d 3d ad 00 00 00 	lea    0xad(%rip),%rdi        # 400707 <_IO_stdin_used+0x7>
  40065a:	e8 81 fe ff ff       	callq  4004e0 <puts@plt>
  40065f:	b8 00 00 00 00       	mov    $0x0,%eax
  400664:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
  400668:	64 48 33 14 25 28 00 	xor    %fs:0x28,%rdx
  40066f:	00 00 
  400671:	74 05                	je     400678 <main+0x71>
  400673:	e8 78 fe ff ff       	callq  4004f0 <__stack_chk_fail@plt>
  400678:	c9                   	leaveq 
  400679:	c3                   	retq   
  40067a:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)
  

```

#### 例子三

```c
// compare_3.c
#include <stdio.h>

int main() {
	char *a = "helloworld";
	char *b = "helloworld";

	if (a == b) {
		printf("equal\n");
	} else {
		printf("not equal\n");
	}

	return 0;
}

```

答案是`equal`

```
objdump -d -s -j .text compare_3
00000000004004e7 <main>:
  4004e7:	55                   	push   %rbp
  4004e8:	48 89 e5             	mov    %rsp,%rbp
  4004eb:	48 83 ec 10          	sub    $0x10,%rsp
  4004ef:	48 8d 05 be 00 00 00 	lea    0xbe(%rip),%rax        # 4005b4 <_IO_stdin_used+0x4>
  4004f6:	48 89 45 f0          	mov    %rax,-0x10(%rbp)
  4004fa:	48 8d 05 b3 00 00 00 	lea    0xb3(%rip),%rax        # 4005b4 <_IO_stdin_used+0x4>
  400501:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  400505:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
  400509:	48 3b 45 f8          	cmp    -0x8(%rbp),%rax
  40050d:	75 0e                	jne    40051d <main+0x36>
  40050f:	48 8d 3d a9 00 00 00 	lea    0xa9(%rip),%rdi        # 4005bf <_IO_stdin_used+0xf>
  400516:	e8 d5 fe ff ff       	callq  4003f0 <puts@plt>
  40051b:	eb 0c                	jmp    400529 <main+0x42>
  40051d:	48 8d 3d a1 00 00 00 	lea    0xa1(%rip),%rdi        # 4005c5 <_IO_stdin_used+0x15>
  400524:	e8 c7 fe ff ff       	callq  4003f0 <puts@plt>
  400529:	b8 00 00 00 00       	mov    $0x0,%eax
  40052e:	c9                   	leaveq 
  40052f:	c3                   	retq   

objdump -s -j .rodata compare_3                                                           
Contents of section .rodata:
 4005b0 01000200 68656c6c 6f776f72 6c640065  ....helloworld.e
 4005c0 7175616c 006e6f74 20657175 616c00    qual.not equal. 

```

这里面优化的是，字符串常量只有一个，所以都指向了同一个

#### 例子四

```go
// compare_4.go
package main

import "fmt"

func main() {
	a := []byte("helloworld")
	b := []byte("helloworld")

	if a == b {
		fmt.Printf("equal\n")
	} else {
		fmt.Printf("not equal\n")
	}
}

```

答案是编译错误，因为`[]byte`只可以与`nil`比较

#### 例子五

```go
// compare_5.go
package main

import "fmt"

func main() {
	a := [10]byte{'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'}
	b := [10]byte{'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'}

	if a == b {
		fmt.Printf("equal\n")
	} else {
		fmt.Printf("not equal\n")
	}
}

```

答案是`equal`

```
objdump -s -j .rodata  compare_5   | grep "equal"                                        
 489fa0 00000565 7175616c 00000565 72617365  ...equal...erase
 
静态编译产生的数据太多了，所以用了过滤

```

![share2.png](https://blog.hacking.pub/qiniu/share/share2.png)

这个`equal`答案真的是这么简单的吗？

```
go tool compile -S compare_5.go

	0x00e0 00224 ($GOROOT/src/fmt/print.go:208)	PCDATA	$2, $1
	0x00e0 00224 ($GOROOT/src/fmt/print.go:208)	MOVQ	os.Stdout(SB), AX
	0x00e7 00231 ($GOROOT/src/fmt/print.go:208)	PCDATA	$2, $2
	0x00e7 00231 ($GOROOT/src/fmt/print.go:208)	LEAQ	go.itab.*os.File,io.Writer(SB), CX
	0x00ee 00238 ($GOROOT/src/fmt/print.go:208)	PCDATA	$2, $1
	0x00ee 00238 ($GOROOT/src/fmt/print.go:208)	MOVQ	CX, (SP)
	0x00f2 00242 ($GOROOT/src/fmt/print.go:208)	PCDATA	$2, $0
	0x00f2 00242 ($GOROOT/src/fmt/print.go:208)	MOVQ	AX, 8(SP)
	0x00f7 00247 ($GOROOT/src/fmt/print.go:208)	PCDATA	$2, $1
	0x00f7 00247 ($GOROOT/src/fmt/print.go:208)	LEAQ	go.string."equal\n"(SB), AX
	0x00fe 00254 ($GOROOT/src/fmt/print.go:208)	PCDATA	$2, $0
	0x00fe 00254 ($GOROOT/src/fmt/print.go:208)	MOVQ	AX, 16(SP)
	0x0103 00259 ($GOROOT/src/fmt/print.go:208)	MOVQ	$6, 24(SP)
	0x010c 00268 ($GOROOT/src/fmt/print.go:208)	MOVQ	$0, 32(SP)
	0x0115 00277 ($GOROOT/src/fmt/print.go:208)	XORPS	X0, X0
	0x0118 00280 ($GOROOT/src/fmt/print.go:208)	MOVUPS	X0, 40(SP)
	0x011d 00285 ($GOROOT/src/fmt/print.go:208)	CALL	fmt.Fprintf(SB)

```

![share3.png](https://blog.hacking.pub/qiniu/share3.png)

![](https://blog.hacking.pub/qiniu/share/share4.png)

```sh
 objdump -s -j .rodata compare_5 | grep "qual"                                     
 489fa0 00000565 7175616c 00000565 72617365  ...equal...erase
 4b9f70 7175616c 0a657272 6e6f206f 626a6563  qual.errno objec

```

#### 编译错误

最近还发现一件事，`nsq`依赖的`github.com/BurntSushi/toml`，使用`go mod`时候，下载在`$GOPATH/pkg/mod/github.com/'!burnt!sushi'/toml`,`大写`变成了`!小写`

那么看看编译在哪？

![share.svg](https://blog.hacking.pub/qiniu/share/share.svg)

而对于上面例子四的编译错误，那么看图的`go/src/cmd/go/internal/work/build.go`

![](https://blog.hacking.pub/qiniu/share/share2.svg)

**备注：**

编译最好加上`-no-pie`，高版本`gcc`默认开启`pie`编译方式

```shell
gcc -no-pie  compare_1.c -o compare_1   
readelf -h compare_1                                                                                              
ELF 头：
  Magic：   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码，小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              EXEC (可执行文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址：               0x400470
  程序头起点：          64 (bytes into file)
  Start of section headers:          6440 (bytes into file)
  标志：             0x0
  本头的大小：       64 (字节)
  程序头大小：       56 (字节)
  Number of program headers:         9
  节头大小：         64 (字节)
  节头数量：         29
  字符串表索引节头： 28

```

否则会编译出来`DYN`类的可执行程序，主要是内存地址无关方式，还不清楚何如做到加载的

```
gcc compare_1.c -o compare_1   
readelf -h compare_1  

ELF 头：
  Magic：   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF64
  数据:                              2 补码，小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              DYN (共享目标文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址：               0x5a0
  程序头起点：          64 (bytes into file)
  Start of section headers:          6504 (bytes into file)
  标志：             0x0
  本头的大小：       64 (字节)
  程序头大小：       56 (字节)
  Number of program headers:         9
  节头大小：         64 (字节)
  节头数量：         29
  字符串表索引节头： 28

```