我有兴趣使用x86-64与gcc的SSE向量指令,并且不想为此使用任何内联汇编.有没有办法在C中做到这一点?如果是这样,有人可以举个例子吗?
我正在写一个工具.该工具的一部分是它能够记录系统调用的参数.好吧,我可以ptrace用于此目的,但ptrace速度很慢.我想到的一种更快的方法是修改glibc.但这变得越来越困难,因为gcc神奇地将自己的内置函数作为系统调用包装器插入,而不是使用glibc中定义的代码.使用-fno-builtin也没有帮助.
所以我提出了编写共享库的想法,其中包括每个系统调用包装器,例如mmap然后在调用实际系统调用包装器函数之前执行日志记录.例如,mmap下面给出了我的样子的伪代码.
int mmap(...)
{
log_parameters(...);
call_original_mmap(...);
...
}
Run Code Online (Sandbox Code Playgroud)
然后我可以使用LD_PRELOAD首先加载这个库.你觉得这个想法会起作用,还是我错过了什么?
这可能完全重复 是否可以通过模式切换在64位进程中执行32位代码?,但这个问题是从一年前开始的,只有一个答案没有给出任何源代码.我希望得到更详细的答案.
我正在运行64位Linux(Ubuntu 12.04,如果重要的话).这里有一些代码可以分配页面,将一些64位代码写入其中,然后执行该代码.
#include <assert.h>
#include <malloc.h>
#include <stdio.h>
#include <sys/mman.h> // mprotect
#include <unistd.h> // sysconf
unsigned char test_function[] = { 0xC3 }; // RET
int main()
{
int pagesize = sysconf(_SC_PAGE_SIZE);
unsigned char *buffer = memalign(pagesize, pagesize);
void (*func)() = (void (*)())buffer;
memcpy(buffer, test_function, sizeof test_function);
// func(); // will segfault
mprotect(buffer, pagesize, PROT_EXEC);
func(); // works fine
}
Run Code Online (Sandbox Code Playgroud)
现在,纯粹是为了娱乐价值,我想做同样的事情,但buffer包含任意32位(ia32)代码,而不是64位代码.此页面意味着您可以通过将CS段描述符的位设置为"长兼容性子模式"来在64位处理器上执行32位代码LMA=1, L=0, D=1.我愿意将我的32位代码包装在执行此设置的序言/结尾中.
但我可以在Linux中以usermode进行此设置吗?(BSD/Darwin的答案也将被接受.)这是我开始对这些概念感到朦胧的地方.我认为解决方案涉及向GDT添加新的段描述符(或者是LDT?),然后通过lcall指令切换到该段.但是所有这些都可以在usermode中完成吗?
这是一个示例函数,在兼容性子模式下成功运行时应返回4,在长模式下运行时应返回8.我的目标是获取指令指针以获取此代码路径并从另一端出来%rax=4,而不会进入内核模式(或仅通过记录的系统调用执行此操作). …
clang/gcc:一些内联汇编操作数可以满足多个约束,例如"rm",当操作数可以满足寄存器或存储器位置时.例如,64 x 64 = 128位乘法:
__asm__ ("mulq %q3" : "=a" (rl), "=d" (rh) : "%0" (x), "rm" (y) : "cc")
Run Code Online (Sandbox Code Playgroud)
生成的代码似乎为参数选择了一个内存约束3,如果我们注册了饥饿就可以了,以避免溢出.显然,x86-64的注册压力要小于IA32.但是,(通过clang)生成的程序集片段是:
movq %rcx, -8(%rbp)
## InlineAsm Start
mulq -8(%rbp)
## InlineAsm End
Run Code Online (Sandbox Code Playgroud)
选择内存约束显然毫无意义!将约束更改为:"r" (y)但是(强制注册)我们得到:
## InlineAsm Start
mulq %rcx
## InlineAsm End
Run Code Online (Sandbox Code Playgroud)
正如所料.这些结果适用于clang/LLVM 3.2(当前Xcode版本).第一个问题:为什么clang会在这种情况下选择效率较低的约束?
其次,有较少广泛使用的,用逗号分隔,多个替代约束语法:
"r,m" (y),这应该评估各方案的成本,并选择导致较少拷贝的一个.这似乎有效,但是clang只选择了第一个 - 如下所示:"m,r" (y)
我可以简单地删除"m"替代约束,但这并不表示可能的合法操作数的范围.这让我想到第二个问题:这些问题是否已在3.3中得到解决或至少得到承认?我试过查看LLVM开发档案,但我宁愿在不必要地进一步限制约束或加入项目讨论等之前征求一些答案.
我在一篇博客文章中读到,最近的X86微体系结构也能够在寄存器重命名器中处理常见的寄存器归零习语(例如将寄存器与自身对齐); 用作者的话来说:
"寄存器重命名器也知道如何执行这些指令 - 它可以将寄存器本身归零."
有人知道这在实践中是如何运作的吗?我知道有些ISA,如MIPS,包含一个在硬件中始终设置为零的架构寄存器; 这是否意味着在内部,X86微体系结构内部具有类似的"零"寄存器,以便在方便时映射到寄存器?或者我的心智模型对于这些东西如何在微体系结构上工作不太正确?
我之所以要问的原因是(从一些观察中)看来mov,在一个循环中,从一个包含零的寄存器到一个目的地,仍然比在循环内通过xor将寄存器归零要快得多.
基本上它发生的是我想根据条件将循环内的寄存器归零; 这可以通过提前分配架构寄存器来存储零(%xmm3在这种情况下),在整个循环期间不进行修改,并在其中执行以下内容来完成:
Run Code Online (Sandbox Code Playgroud)movapd %xmm3, %xmm0
或者用xor技巧代替:
Run Code Online (Sandbox Code Playgroud)xorpd %xmm0, %xmm0
(AT&T语法).
换句话说,选择是在循环之外提升常数零或在每次迭代中将其重新物化在其中.后者将实时架构寄存器的数量减少一个,并且通过处理器假设的特殊情况感知和处理xor成语,它似乎应该像前者一样快(特别是因为这些机器具有更多的物理无论如何,寄存器都比体系结构寄存器更重要,所以它应该能够在内部完成与我在程序集中所做的相同的工作,通过在内部提升常数零甚至更好,完全意识和控制自己的资源).但它似乎不是,所以我很好奇是否有任何具有CPU架构知识的人可以解释是否有一个很好的理论原因.
在这种情况下,寄存器由SSE寄存器发生,机器恰好是Ivy Bridge; 我不确定这些因素有多重要.
当我打破主要时,它看起来像粗线是我正在创建和初始化的地方.我想我错了,我正试图从一本解释x86的书中检查x86_64程序集.这看起来很奇怪,我很确定我只是不明白,因为在本书中他说他会将单词和双字称为4字节.如果我能得到一个解释来帮助我的认知,我将不胜感激.
__CODE__方法:http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-inject
我正在阅读syscall的以下定义:
.text
.globl syscall
.type syscall,%function
.align 16
syscall:
movq %rdi, %rax /* Syscall number -> rax. */
movq %rsi, %rdi /* shift arg1 - arg5. */
movq %rdx, %rsi
movq %rcx, %rdx
movq %r8, %r10
movq %r9, %r8
movq 8(%rsp),%r9 /* arg6 is on the stack. */
syscall /* Do the system call. */
cmpq $-4095, %rax /* Check %rax for error. */
jae __syscall_error /* Branch forward if it failed. */
ret /* Return to caller. */
.size …Run Code Online (Sandbox Code Playgroud) 在尝试优化x86_64上的返回值时,我注意到一件奇怪的事情.即,给出代码:
#include <cstdint>
#include <tuple>
#include <utility>
using namespace std;
constexpr uint64_t a = 1u;
constexpr uint64_t b = 2u;
pair<uint64_t, uint64_t> f() { return {a, b}; }
tuple<uint64_t, uint64_t> g() { return tuple<uint64_t, uint64_t>{a, b}; }
Run Code Online (Sandbox Code Playgroud)
Clang 3.8输出此汇编代码f:
movl $1, %eax
movl $2, %edx
retq
Run Code Online (Sandbox Code Playgroud)
这个用于g:
movl $2, %eax
movl $1, %edx
retq
Run Code Online (Sandbox Code Playgroud)
哪个看起来最佳.但是,使用GCC 6.1编译时,生成的程序集f与Clang输出相同,生成的程序集为g:
movq %rdi, %rax
movq $2, (%rdi)
movq $1, 8(%rdi)
ret
Run Code Online (Sandbox Code Playgroud)
看起来返回值的类型被GCC归类为MEMORY,而Clang归类为INTEGER.我可以确认将Clang代码与GCC代码链接这样的代码可能会导致分段错误(Clang调用GCC编译 …
我想获得在c中添加两个无符号64位整数的进位位。如果需要,我可以使用x86-64 asm。码:
#include <stdio.h>
typedef unsigned long long llu;
int main(void){
llu a = -1, b = -1;
int carry = /*carry of a+b*/;
llu res = a+b;
printf("a+b = %llu (because addition overflowed), carry bit = %d\n", res, carry);
return 0;
}
Run Code Online (Sandbox Code Playgroud) 我正处于一个项目的设计阶段,该项目需要执行大量简单的 256 位整数运算(仅加、子、多、分),并且需要针对这四个操作进行合理优化的东西。
我已经熟悉 GMP、NTL 和大多数其他重量级 bignum 实现。然而,这些实现的开销促使我做我自己的低级实现——我真的不想这样做;众所周知,这东西很难做对。
在我的研究中,我注意到 Clang 中新的扩展整数类型 - 我是 gcc 用户 - 我想知道是否有人对现实生活中的扩展整数有任何经验,愤怒的实现?它们是否针对“明显的”位大小(256、512 等)进行了优化?
我在 linux 下的 x-64 上使用 C 语言(目前是 Ubuntu,但如果需要,可以向其他发行版开放)。我主要使用 gcc 进行生产工作。
编辑添加:@phuclv 确定了以前的答案C++ 128/256-bit fixed size integer types。(感谢@phuclv。)这个q/a 侧重于c++ 支持;我希望确定是否有人对新的 Clang 类型有任何特定的经验。