【问题标题】:_cgo_topofstack@@Base in a stripped binary_cgo_topofstack@@Base 在剥离的二进制文件中
【发布时间】:2021-07-08 07:34:15
【问题描述】:

_cgo_topofstack@@Base 在来自 Go 的剥离二进制文件的上下文中是什么意思?

$ cat simple.go
package main
import
(
    "net"
    "time"
    "strconv"
)

func main() {
    tcpAddr, _ := net.ResolveTCPAddr("tcp4", ":7777")
    listener, _ := net.ListenTCP("tcp", tcpAddr)
    conn, _ := listener.Accept()
    daytime := time.Now().String()+strconv.Itoa(0xdeadface)
    conn.Write([]byte(daytime))
}

代码应该被剥离 - _cgo_topofstack@@Base 是什么意思?

$ go build -gcflags=-l -ldflags "-s -w" -o simple_wo_symbols simple.go
$ objdump -D -S simple_wo_symbols > simple_wo_symbols.human
$ sed -n "198899,198904p" simple_wo_symbols.human
  4b9860:   e8 db c1 fb ff          callq  475a40 <_cgo_topofstack@@Base+0xe4c0>
  4b9865:   48 8b 44 24 18          mov    0x18(%rsp),%rax
  4b986a:   48 89 44 24 70          mov    %rax,0x70(%rsp)
  4b986f:   48 8b 4c 24 20          mov    0x20(%rsp),%rcx
  4b9874:   48 89 4c 24 40          mov    %rcx,0x40(%rsp)
  4b9879:   ba ce fa ad de          mov    $0xdeadface,%edx

EDIT(更好地说明问题):

  • 为什么这个符号存在于剥离二进制文件中?
  • 批准peter-cordes 声明:调用的函数与_cgo_topofstack@@Base 处的函数完全无关,添加此(不相关且冗余)信息是objdump(奇怪?)的事情
  • 可能与this(?)有关:有Go惯用的剥离方式吗?!

【问题讨论】:

  • 我对 Go 不太了解,但 _cgo_topofstack@@Base 是一个仍然存在的符号。该调用是对地址0xe4c0 之外的地址,无论那里存在什么功能。
  • 您还想了解什么?实际函数_cgo_topofstack@@Base 与您调用的内容无关,但是从实际目标地址向后搜索时,这是找到的最新objdump。如果您对_cgo_topofstack@@Base 之类的函数会做什么感到好奇,它可能会返回 RSP(或者不会,鉴于 VonC 的回答;“顶部”和“底部”对于向下增长的堆栈并不总是明确的术语)。如果您想知道 @@ 恰好是符号名称的一部分,您应该编辑您的问题。
  • 我编辑了我的问题以便更好地解释它。

标签: go x86 disassembly strip objdump


【解决方案1】:

_cgo_topofstack@@Base 是一个符号,由于某种原因在您剥离的二进制文件中仍然存在。您的呼叫是到地址 0xe4c0 之外的地址,无论那里存在什么功能,与实际的 _cgo_topofstack 代码完全无关。

反汇编程序将地址描述为符号+偏移量是正常的。

这种风格适用于数据数组(例如,如果 global_array 的符号仍然存在,则将类似 x = global_array[10] 的内容编译到来自 global_array+40 的负载中)以及函数内的跳转。对于这样的情况,除了让你看到附近有什么,并且有更少的数字可以查看之外,它通常没有帮助。

与其实现花哨的逻辑来决定是否打印地址的symbol+offset 版本,而不仅仅是数字绝对地址,汇编程序总是更容易(并且没有出错的风险)它。从地址向后搜索并获取找到的第一个符号。或者对于节中第一个符号之前的地址,打印为foo - 0x...。人类可以使用判断和经验来理解输出,尤其是在查看剥离的二进制文件的反汇编时。

(反汇编程序没有一个标志可以查看是否检测到剥离的二进制文件;检测这将是一个启发式的问题,比如注意到大多数直接的call 目标是没有自己符号的地址。)

AFAIK,GNU Binutils objdump 没有选项不打印地址的符号版本。 --no-addresses 做了一些不同的事情。


我不确定@@Base 是关于什么的。不过,它似乎并不是 Go 独有的。在我的 x86-64 Arch GNU/Linux 系统上,objdump -d /bin/ls(这是一个剥离的 PIE 可执行文件)显示了很多地址,例如22d60 &lt;_obstack_memory_used@@Base+0xc2a0&gt;。所以这是该程序的大部分代码之前的最后一个符号。

@@ 的其他情况包括同一二进制文件中的 glibc 符号 ABI 版本控制,例如23298 &lt;optarg@@GLIBC_2.2.5&gt;。这个 Arch Linux 二进制文件是在最新的 Arch Linux 系统上编译的,实际上并没有链接到古老的 glibc 2.2.5,但我认为这意味着 optarg 的类型或自 glibc 2.2.5 以来没有改变.可能不是从更早开始,但 2.2.5 可能是 glibc 开始以这种方式命名符号的时候。对这一段持保留态度,因为我真的不知道libc.so 是如何安排ld 用这些@@ 版本化名称替换符号名称(如stderr),或者这个的历史。

【讨论】:

    【解决方案2】:

    关于_cgo_topofstack是关于什么的,可以看introduced in its current form in Go 1.4original name cgo_topofstack

    (但是,正如the comments 中的Peter Cordes 所指出的,这并不能解释为什么该符号仍会出现在stripped binary 中)

    // Called from cgo wrappers, this function returns g->m->curg.stack.hi.
    // Must obey the gcc calling convention.
    TEXT cgo_topofstack(SB),NOSPLIT,$0
        get_tls(CX)
        MOVL    g(CX), AX
        MOVL    g_m(AX), AX
        MOVL    m_curg(AX), AX
        MOVL    (g_stack+stack_hi)(AX), AX
        RET
    

    这是为了修复golang/go/issue 8771

    cmd/cgo: 如果 C 函数调用复制堆栈的 Go 回调,则返回值的 C 函数会失败

    Cgo 使用一个调用 C 代码的包装函数,传递堆栈帧的地址。
    这个包装函数是GCC编译的,调用的是用户写的真正的函数。

    允许用户的函数调用 Go 回调。
    这些 Go 回调将在原始调用者的堆栈上运行。
    它们可能会导致堆栈复制。

    如果堆栈在 Go 回调期间被复制,则 GCC 编译的包装器的调用者正在不同的位置运行。
    GCC 编译的包装器使用的堆栈帧指针不会更新,因为堆栈复制器当然对 GCC 编译的代码一无所知。
    我认为这不是函数参数的问题;当包装器调用真正的函数时,它们已经从堆栈帧中复制出来了。

    但是,对于返回值的 C 函数来说,这是一个问题。
    包装器将获取 C 函数返回的值,并使用其指向堆栈帧的指针存储它。如果发生堆栈复制,该指针将不会被更新。
    换句话说,包装器可能会将返回值存储在旧堆栈上,而不是新堆栈上。

    CL 144130043 补充:

    cgo:调整返回值位置以考虑堆栈副本。

    在 cgo 调用期间,可以复制堆栈。
    此副本使 cgo 指向返回值区域的指针无效。

    要解决此问题,请传递包含堆栈的位置的地址 最高值(在 G 结构中)。
    对于返回值的 cgo 函数,在 cgo 调用之前和之后读取 stktop 以计算写入返回值所需的调整。

    修改为commit e1364a6


    “@@”部分应该是 option of objdump--symbols 的结果

    显示文件符号表部分中的条目(如果有的话)。
    如果符号有与之关联的版本信息,那么也会显示。

    版本字符串显示为符号名称的后缀,前面有一个@ 字符。例如foo@VER_1

    如果版本是解析对符号的无版本引用时使用的默认版本,则它显示为一个后缀,前面有两个 @ 字符。例如foo@@VER_2

    【讨论】:

    • 但请注意,OP 的代码没有调用它,这恰好是剥离后的二进制文件中仍然存在的最接近目标地址的前导符号,0xe4c0 字节在它之前。 (所以前面有很多页,而且这个功能很小。)这是对 some 问题的有用且有趣的答案,但 this 问题需要进行一些编辑才能成为一个好问题放置这个答案的地方。 (目前还不清楚这是否是询问者想要的。)
    • @PeterCordes 我同意。我专注于可以生成该符号的因素。
    • 我认为您至少应该在答案的顶部注意这不是对该函数的调用,而您只是在解释该符号的含义。 (如果您可以解释符号名称中@@Base 的重要性,那也可能很好。)也许我错过了它,但我也没有明确解释为什么该符号仍会出现在剥离的二进制文件中。
    • 我编辑了我的问题,以便更好地解释我的意思。谢谢。
    猜你喜欢
    • 2014-05-06
    • 2014-03-06
    • 2012-10-22
    • 2010-12-28
    • 1970-01-01
    • 2014-12-16
    • 2013-04-05
    • 1970-01-01
    • 2017-05-29
    相关资源
    最近更新 更多