argc有时返回正确的值,有时返回 0,有时返回(看似)随机数……所有这些都来自同一个可执行文件。
.section .text
.global _start
_start:
movq $1, %rax
popq %rdi
syscall
Run Code Online (Sandbox Code Playgroud)
例如:
%as -o this.o this.s ; ld -o this this.o
%./this; echo $?
1
%./this 1; echo $?
0
%./this 1 2; echo $?
3
%./this 1 2 a; echo $?
4
%./this 1 2 a f; echo $?
0
%_
Run Code Online (Sandbox Code Playgroud)
我对汇编有点陌生,但我非常有信心获取参数计数就像在 Linux 中将其从堆栈中弹出一样简单,其中 System V ABI 记录 RSP 在argc新execve进程中指向的内容。
是我做错了什么,还是事情真的很糟糕?
从手册:
在GCC 5.1版本中,libstdc ++引入了一个新的库ABI,其中包括std :: string和std :: list的新实现.这些更改对于符合2011 C++标准是必要的,该标准禁止写入时写入字符串并要求列表跟踪其大小.
可以使用_GLIBCXX_USE_CXX11_ABI宏来控制库标头使用旧的或新的ABI,而不管使用哪个"-std".
我想知道使用这种"兼容性ABI"的含义是什么?我猜小串操作的运行时性能会受到影响(我假设是负面的),并且列表大小访问从O(1)(C11 ABI)到O(N)(兼容性ABI).
我正在尝试C++使用 LLVM/Clang 3.7.0 构建一个简单的(“hello world”)程序,该程序是根据工具链的源代码构建的libc++,使用命令行:
clang++ -std=c++14 -stdlib=libc++ -fno-exceptions hello.cpp\nRun Code Online (Sandbox Code Playgroud)\n\n但是,我收到以下错误:
\n\n/usr/bin/ld: warning: libc++abi.so.1, needed by /bulk/workbench/llvm/3.7.0\n/toolchain4/bin/../lib/libc++.so, not found (try using -rpath or -rpath-link)\n/bulk/workbench/llvm/3.7.0/toolchain4/bin/../lib/libc++.so: undefined reference to `__cxa_rethrow_primary_exception\'\n/bulk/workbench/llvm/3.7.0/toolchain4/bin/../lib/libc++.so: undefined reference to `__cxa_decrement_exception_refcount\'\n/bulk/workbench/llvm/3.7.0/toolchain4/bin/../lib/libc++.so: undefined reference to `std::out_of_range::~out_of_range()\'\n[...]\nRun Code Online (Sandbox Code Playgroud)\n\n未LD_LIBRARY_PATH设置,工具链的安装目录已添加到我的工作中PATH中:
export PATH=$PATH:/bulk/workbench/llvm/3.7.0/toolchain4/bin/\nRun Code Online (Sandbox Code Playgroud)\n\n我上线了Ubuntu GNU/Linux 14.04,并且尚未从任何存储库安装任何 LLVM 或 Clang 相关的软件包。
根据libc++ 文档:
\n\n\n在 Linux 上,libc++ 通常只能与 \xe2\x80\x98-stdlib=libc++\xe2\x80\x99 一起使用。然而,某些 libc++ 安装需要用户自己手动链接 libc++abi。如果在使用 …
我比较了返回结构的2个C函数.我们知道在ABI级别上,大型结构将被指针作为第一个函数参数传递.
struct S {
int words[8];
};
struct S fsret() {
struct S s;
s.words[0] = 1;
return s;
}
void fout(struct S* s) {
s->words[0] = 1;
}
Run Code Online (Sandbox Code Playgroud)
对于这些函数,我检查了x86_64 Linux和Windows的程序集.该fsret声明为void @fsret(%struct.S* sret %s).
比较这两种变体,被叫方没有区别.但是,在函数内部,fsret另外将其第一个参数(指向结构的指针)复制到RAX寄存器.为什么?
该X86-64的Windows ABI有一个概念合法收尾,这是可以的功能收尾的一种特殊类型的模拟异常处理过程中,以恢复呼叫者上下文1所描述的在这里:
如果 RIP 在一个结语内 [当异常发生时],那么控制正在离开函数,...并且结语的影响必须继续计算调用者函数的上下文。为了确定 RIP 是否在结语内,检查从 RIP 开始的代码流。如果该代码流可以与合法结语的尾随部分匹配,则它在结语中,结语的其余部分被模拟,上下文记录随着每条指令的处理而更新......
您可以在此处找到对合法结语 的散文描述,其中包括以下示例:
如果函数中未使用帧指针,则结语必须首先释放堆栈的固定部分,弹出非易失性寄存器,并将控制权返回给调用函数。例如,
Run Code Online (Sandbox Code Playgroud)add RSP, fixed-allocation-size pop R13 pop R14 pop R15 ret
它包含一个类似的示例,lea用于在使用帧指针时恢复堆栈。
但是,如果函数内根本没有固定大小的分配怎么办?函数不使用堆栈的情况并不少见。add rsp, 0在这种情况下是否需要插入一个哑元以符合合法结语的规则,还是可以省略?
1例如,恢复可能被调用者破坏的非易失性寄存器。
struct Test {
uint ui;
string s;
}
function test(Test t) public {
emit Log(t.ui, t.s);
}
Run Code Online (Sandbox Code Playgroud)
我对ABI有一些了解。我使用实验性 ABIEncoderV2 选项签订了这份合同。总之,这个函数的签名是0x6056f4cc,我在操作码中找到了这个值。我用 sha3 尝试了一些 case test(uint256,string), test(tuple(uint256,string)), test(tuple), test(tuple[uint256,string])) ...但没有人做出正确的签名。Solidity 如何使用元组进行函数签名?
我最近安装了 LLVM v8.0.0(在 RHEL 7.4 上)。我正在阅读LLVM Kaleidoscope 教程以了解如何使用该系统,但遇到了链接问题。
根据教程(第 2 章结尾),我运行:
clang++ -g -O3 kld.cpp `llvm-config --cxxflags` -o kld
Run Code Online (Sandbox Code Playgroud)
它编译,但链接器失败:
/tmp/kld-f7264f.o:(.data+0x0): undefined reference to `llvm::EnableABIBreakingChecks'
clang-8: error: linker command failed with exit code 1 (use -v to see invocation)
Run Code Online (Sandbox Code Playgroud)
我怀疑这可能是 的问题llvm-config,所以我也尝试使用--ldflags和--system-libs标志,但没有运气。
llvm-config --cxxflags 给出(重新格式化以提高可读性)
-I~/project/llvm-src/include -I~/project/llvm-build/include
-fPIC -fvisibility-inlines-hidden
-std=c++11
-Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual
-Wno-missing-field-initializers -pedantic -Wno-long-long
-Wno-maybe-uninitialized -Wdelete-non-virtual-dtor -Wno-comment
-g
-fno-exceptions -fno-rtti
-D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS
-D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
Run Code Online (Sandbox Code Playgroud)
哪里~/... …
无论如何,就 x86 汇编代码而言。我一直在阅读有关函数调用的内容,但仍然无法完全掌握对基/帧指针 (EBP) 和堆栈指针 (ESP) 的需求。
当我们调用一个函数时,EBP的当前值会被放入栈中,然后EBP获取当前的ESP值。
函数的返回值、函数参数和局部变量的占位符然后将被放置在堆栈中,并且堆栈指针 ESP 值将减少(或增加)指向放置在堆栈上的最后一个占位符之后。
现在我们让 EBP 指向当前堆栈帧的开头,而 ESP 指向堆栈帧的结尾。
由于与 EBP 的恒定偏移量,EBP 将用于访问函数的参数和局部变量。那没关系。我不明白的是,为什么 ESP 不能通过使用其偏移量来访问这些变量。EBP指向栈帧的开头,ESP指向栈帧的结尾。有什么不同?
一旦所有局部变量等有了占位符,ESP 就不应该改变,还是应该改变?
注意:这个问题是关于 x86_64 架构和 Linux ABI。
当程序启动时,会为堆栈分配一些空间。稍后,在程序执行期间,堆栈区域可以调整大小(当需要更多空间时)达到操作系统指定的某个最大值。
让我们以简单的程序为例:
int main() {
char bytes[7 * 1024 * 1024];
}
Run Code Online (Sandbox Code Playgroud)
让我们在 gdb 下运行它并设置断点:main 之前和声明数组之后。
gdb> b *main
gdb> b main
gdb> r
gdb> info proc mapping // breakpoint before pushing stack
Start Addr End Addr Size Offset objfile
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
gdb> c
gdb> info proc mapping // breakpoint after pushing stack
Start Addr End Addr Size Offset objfile
0x7fffff8fe000 0x7ffffffff000 0x701000 0x0 [stack]
Run Code Online (Sandbox Code Playgroud)
所以我们可以看到堆栈实际上被调整了大小。
问题是操作系统如何知道何时必须调整堆栈大小?. 一些互联网资源说操作系统处理page fault …
ABI 标准中与内存布局相关的规范是否通常仅适用于 ABI 边界或例如在翻译单元内,或者如果不是这种情况,编译器通常是否会做出此类额外保证?
如果“一般”太宽泛,请考虑例如带有 System V x64 和 Itanium C++ ABI 的 GCC/Clang。
这里有两个例子来说明我的意思:
System V x64 ABI 指定大小至少为 16 字节的数组具有至少 16 字节的对齐,即使元素类型的对齐更小,因此对齐比alignof建议的更严格。它还指定 的对齐方式long double是16。那么以下在 C++ 标准下具有未定义行为的函数(如果调用)是否可以安全地在 System V x86 ABI 下使用,即使该storage数组从未跨翻译单元边界公开?
void f() {
char storage[16]; // Only guaranteed to have alignment `1` by the C++ standard.
using T = long double;
auto p = new(storage) T;
}
Run Code Online (Sandbox Code Playgroud)Itanium C++ ABI 指定了类的布局。例如:
#include<new>
struct A {
int i;
virtual ~A() …Run Code Online (Sandbox Code Playgroud)