一道变量compare题的延伸

我遇到的问题是第4道例子,建议读者先不要看答案,因为代码本身非常简单。
下载题目,看看一共5道例子能不能完全做对,这样看到答案的时候会感到更有趣。  

判断下面的例子, ab是否相等

例子一

// 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",对于常量字符串的存储不一样,但是对于结果优化一致

例子二

// 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)

例子三

// 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. 

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

例子四

// 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比较

例子五

// 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

这个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

 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

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

备注:

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

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