gpt4 book ai didi

c - 为什么编译器在这种情况下使用 32 位寄存器将指针传递给 amd64 linux 上的函数

转载 作者:行者123 更新时间:2023-12-04 22:38:13 24 4
gpt4 key购买 nike

enter image description here我正在用修改过的boringssl调试 Chrome 。它总是有一个SegmentFault。
我发现问题是

EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
反汇编代码:
callq EC_KEY_get0_group
mov %eax,%edi
callq EC_GROUP_get_curve_name
EC_KEY_get0_group 的返回类型是 EC_GROUP*指针,但它被传递给 EC_GROUP_get_curve_name通过 32 位寄存器。
指针被截断并导致 SegmentFault。为什么编译器会生成这样的代码?
是否有任何编译器选项可以避免这种情况?

最佳答案

我可以提供跟踪问题的指导,但不能针对您的问题提供具体答案,因为我没有使用 BoringSSL 的修改版本。

如果您没有 C 函数的原型(prototype),那么所有参数和返回值将默认为 int类型。该函数将被视为有未指定数量的参数。
第一个让我印象深刻的是mov $0, %al在每个函数调用之前。这向我表明,这些函数要么是可变参数,要么是无原型(prototype)的。 AMD64 System V ABI 64 位 Linux 使用的 AL 寄存器是这样描述的:

For calls that may call functions that use varargs or stdargs (prototype-lesscalls or calls to functions containing ellipsis (. . . ) in the declaration) %al is usedas hidden argument to specify the number of vector registers used.


我们可以排除它们是可变参数的,因为它们的原型(prototype)应该是这样的:
int EC_GROUP_get_curve_name(const EC_GROUP *group);
const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key);
由于这些函数不是可变参数(不要使用 ... ),因此代码中的某些内容可能无法使这些函数的原型(prototype)可用。

我们可以通过这些简单的 C 函数调用看到同样的行为:
testfunc.c :
#include <stdlib.h>

typedef struct ec_group_st EC_GROUP;
typedef struct ec_key_st EC_KEY;

int EC_GROUP_get_curve_name(const EC_GROUP *group);
const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key);

int testfun()
{
EC_KEY *ec = NULL;
return EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
}
如果我使用 GCC 编译 gcc -Wall -Wextra -c -O3 testfunc.c -o testfunc.o并使用 objdump -D testfunc.o查看生成的代码,它看起来像:
0000000000000000 <testfun>:
0: 48 83 ec 08 sub $0x8,%rsp
4: 31 ff xor %edi,%edi
6: e8 00 00 00 00 callq b <testfun+0xb>
b: 48 83 c4 08 add $0x8,%rsp
f: 48 89 c7 mov %rax,%rdi
12: e9 00 00 00 00 jmpq 17 <testfun+0x17>
上面的代码似乎是正确的,因为第一个函数调用的 64 位返回值(RAX 中的指针)按预期传递给了第二个函数调用。该代码也没有将 AL 设置为零。
但是,如果我采用相同的代码并注释掉函数的原型(prototype),如下所示:
#include <stdlib.h>

typedef struct ec_group_st EC_GROUP;
typedef struct ec_key_st EC_KEY;

/*int EC_GROUP_get_curve_name(const EC_GROUP *group);
const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key);*/

int testfun()
{
EC_KEY *ec = NULL;
return EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
}
我得到这个生成的代码:
0000000000000000 <testfun>:
0: 48 83 ec 08 sub $0x8,%rsp
4: 31 ff xor %edi,%edi
6: 31 c0 xor %eax,%eax
8: e8 00 00 00 00 callq d <testfun+0xd>
d: 48 83 c4 08 add $0x8,%rsp
11: 89 c7 mov %eax,%edi
13: 31 c0 xor %eax,%eax
15: e9 00 00 00 00 jmpq 1a <testfun+0x1a>
现在我们将 RAX(AL 是 RAX 的低 8 位)设置为零,并且第一个函数调用的返回值被视为 32 位 int这与您看到的行为相似。我建议至少使用 -Wall -Wextra 构建你的 C 文件。查看更多种类的警告。对于没有原型(prototype)的代码,我的编译器会抛出这些警告:
testfunc.c: In function ‘testfun’:
testfunc.c:12:12: warning: implicit declaration of function ‘EC_GROUP_get_curve_name’ [-Wimplicit-function-declaratio
]
return EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
^~~~~~~~~~~~~~~~~~~~~~~
testfunc.c:12:36: warning: implicit declaration of function ‘EC_KEY_get0_group’ [-Wimplicit-function-declaration]
return EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
^~~~~~~~~~~~~~~~~
我会在您的构建输出中寻找关于隐式声明的类似警告,并在您的代码中验证函数原型(prototype)是否已正确包含。

关于c - 为什么编译器在这种情况下使用 32 位寄存器将指针传递给 amd64 linux 上的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64429831/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com