【问题标题】:C function parameter mysteriously drifted?C函数参数神秘漂移?
【发布时间】:2017-04-23 09:40:14
【问题描述】:

我正在使用 Samba 3.6.25 进行项目。 当我按照“smbclient”的源代码尝试构建自己的SMB服务器列表实用程序时,我遇到了一件奇怪的事情:

当我调用一个函数时,它跳过了我的第一个参数并用第二个参数填充它,然后第二个填充第三个,依此类推。

我调用的函数是:cli_pipe.c中的cli_rpc_pipe_open_noauth_transport()。我在其中添加了一些调试代码:

NTSTATUS cli_rpc_pipe_open_noauth_transport(struct cli_state *cli,
                        enum dcerpc_transport_t transport,
                        const struct ndr_syntax_id *interface,
                        struct rpc_pipe_client **presult)
{

    struct rpc_pipe_client *result;
    struct pipe_auth_data *auth;
    NTSTATUS status;

    status = cli_rpc_pipe_open(cli, transport, interface, &result);
    _DEBUG("cli = %p", cli);
    _DEBUG("transport = %p", transport);
    _DEBUG("interface = %p", interface);
    _DEBUG("presult = %p", presult);
    _DEBUG("cli->desthost = %p", cli->desthost);
    _DEBUG("cli->desthost = \"%s\"", cli->desthost);
    if (!NT_STATUS_IS_OK(status)) {
        return status;
    }
……

这就是我调用这个函数的方式:

NTSTATUS _pipe_open_noauth(struct cli_state *cli, const struct ndr_syntax_id *intf, struct rpc_pipe_client **presult)
{
    SMBD_DEBUG("cli = %p", cli);
    SMBD_DEBUG("intf = %p", intf);
    SMBD_DEBUG("presult = %p", presult);
    SMBD_DEBUG("cli->desthost = %p", cli->desthost);
    SMBD_DEBUG("cli->desthost = \"%s\"", cli->desthost);
    return cli_rpc_pipe_open_noauth_transport(cli, 1, intf, presult);
}

这是我在控制台中得到的:

--- SMBD (util_smbclient.c, 117): cli = 0xdb2b20
--- SMBD (util_smbclient.c, 118): intf = 0xda31c0
--- SMBD (util_smbclient.c, 119): presult = 0x7fe9fdc8
--- SMBD (util_smbclient.c, 120): cli->desthost = 0xdd3a50
--- SMBD (util_smbclient.c, 121): cli->desthost = "192.168.1.125"
=== Samba (rpc_client/cli_pipe.c, 2873): cli = 0x1
=== Samba (rpc_client/cli_pipe.c, 2874): transport = 0xda31c0
=== Samba (rpc_client/cli_pipe.c, 2875): interface = 0x7fe9fdc8
=== Samba (rpc_client/cli_pipe.c, 2876): presult = 0xdaf3d0

这没有意义!注意到我传递的四个参数是: 0xdb2b20、0x1、0xda31c0、0x7fe9fdc8 但是 cli_rpc_pipe_open_noauth_transport() 得到的是: 0x1, 0xda31c0, 0x7fe9fdc8, 0xdaf3d0

很明显,第一个参数“0xdb2b20”丢失了,第二个参数取而代之。

有谁知道发生了什么,我该如何解决?

非常感谢您!

—— 附加信息:

我使用的工具链是 mipsel-linux-uclibc-cc/ld/ar。我试图 objdump 几个目标文件,看看发生了什么。

我转储了我自己的程序,这里是调用该函数的程序集。 注意到四个参数是按顺序传递的:a0、a1、a2、a3:

  409bf0:   8fdc0010    lw  gp,16(s8)
  409bf4:   8fc40020    lw  a0,32(s8)
  409bf8:   24050001    li  a1,1            # store “1” in a1
  409bfc:   8fc60024    lw  a2,36(s8)
  409c00:   8fc70028    lw  a3,40(s8)
  409c04:   8f99ab74    lw  t9,-21644(gp)
  409c08:   00000000    nop
  409c0c:   0320f809    jalr    t9
  409c10:   00000000    nop
  409c14:   8fdc0010    lw  gp,16(s8)
  409c18:   03c0e821    move    sp,s8
  409c1c:   8fbf001c    lw  ra,28(sp)
  409c20:   8fbe0018    lw  s8,24(sp)
  409c24:   03e00008    jr  ra
  409c28:   27bd0020    addiu   sp,sp,32

然后我转储了 smbclient,它也调用了 cli_rpc_pipe_open_noauth_transport()。 问题来了:好像a0不是用来传参数的!!!

<cli_rpc_pipe_open_noauth>:
              …
  487840:   8fdc0018    lw  gp,24(s8)
  487844:   8fc2003c    lw  v0,60(s8)
  487848:   00000000    nop
  48784c:   afa20010    sw  v0,16(sp)
  487850:   02002021    move    a0,s0
  487854:   8fc50034    lw  a1,52(s8)
  487858:   24060001    li  a2,1    # Here, the number “1” was stored in a2 instead of a1!!!
  48785c:   8fc70038    lw  a3,56(s8)
  487860:   8f99aed0    lw  t9,-20784(gp)
  487864:   00000000    nop
  487868:   0320f809    jalr    t9  # cli_rpc_pipe_open_noauth_transport()
  48786c:   00000000    nop
  487870:   8fdc0018    lw  gp,24(s8)
  487874:   02001021    move    v0,s0
  487878:   03c0e821    move    sp,s8
  48787c:   8fbf0028    lw  ra,40(sp)
  487880:   8fbe0024    lw  s8,36(sp)
  487884:   8fb00020    lw  s0,32(sp)
  487888:   03e00008    jr  ra
  48788c:   27bd0030    addiu   sp,sp,48

最后我转储 cli_rpc_pipe_open_noauth_transport() 本身,似乎它以 smbclient 的方式工作:

0053bdac <cli_rpc_pipe_open_noauth_transport>:
  53bdac:   3c1c0087    lui gp,0x87
  53bdb0:   279c3624    addiu   gp,gp,13860
  53bdb4:   0399e021    addu    gp,gp,t9
  53bdb8:   27bdffc0    addiu   sp,sp,-64
  53bdbc:   afbf0038    sw  ra,56(sp)
  53bdc0:   afbe0034    sw  s8,52(sp)
  53bdc4:   afb00030    sw  s0,48(sp)
  53bdc8:   03a0f021    move    s8,sp
  53bdcc:   afbc0018    sw  gp,24(sp)
  53bdd0:   afc40040    sw  a0,64(s8)
  53bdd4:   afc50044    sw  a1,68(s8)
  53bdd8:   afc60048    sw  a2,72(s8)
  53bddc:   afc7004c    sw  a3,76(s8)
  53bde0:   8f848080    lw  a0,-32640(gp)
  53bde4:   00000000    nop
  53bde8:   24844da0    addiu   a0,a0,19872
  53bdec:   24050b39    li  a1,2873
  53bdf0:   8fc60044    lw  a2,68(s8)
  53bdf4:   8f99cab0    lw  t9,-13648(gp)
  53bdf8:   00000000    nop
  53bdfc:   0320f809    jalr    t9              <—— invoke cli_rpc_pipe_open()
  53be00:   00000000    nop

附加信息 No.2 -- 我如何编译我的程序

  1. 我从官方 FTP 服务器下载了 Samba。
  2. 配置和制作(交叉编译)
  3. 在“source3”目录中找到所有 .o 文件,然后将它们全部归档到一个 .a 文件中。
  4. 创建我自己的应用程序,调用 Samba 函数,就像它自己的程序“smbclient”一样,有许多 -I 选项确保编译正常
  5. 链接 Samba 官方提供的 libsmbclient.a 和我自己在步骤 3 中归档的 .a。

附加信息3号

此存储库的完整来源: https://github.com/Andrew-M-C/SMB-CIFS_discovery

【问题讨论】:

  • 确实看起来像一个编译器错误。如果减少测试用例,错误会消失吗?下一步可能是向构建工具链的人提出错误。除非您准备通过 gcc 进行跟踪 :)
  • 你应该把你的 _DEBUG 放在 cli_rpc_pipe_open() 上面,以便在函数调用之前显示堆栈。
  • @Holger 我已将 _DEBUG 放在 cli_rpc_pipe_open() 之上,该函数也出现了同样的错误。我把它放在后面只是为了让我更容易定位参数传递(否则会有很多令人困惑的“jalr”指令)
  • @JonChesterfield 感谢您的评论。但是我想知道您所说的“减少测试用例”是什么意思?你是说那些 _DEBUG 指令吗?起初,我没有 _DEBUG,但出现“段错误”错误。因此我添加了很多 _DEBUG 试图定位哪里出了问题。
  • 在调用约定方面似乎存在分歧

标签: c function assembly parameter-passing mips


【解决方案1】:

来自提问者的回答:

我解决了。实际上我并没有解决问题本身,而是尝试了另一种方法来实现我的目标。

我构建项目的旧方式是:首先构建 Samba,然后将 Samba 对象和静态库文件链接到 Samba 存储库之外。这是我遇到问题的时候。

我改变了构建方式:首先用我自己的代码覆盖原始的 Samba“smbclient”代码,然后构建 Samba。 Samba Makefile 将我的源文件识别为 smbclient 源文件并顺利编译和链接它们。终于成功了。

请参考我在问题中提到的 GitHub 存储库。

【讨论】: