我使用以下代码创建了一个简单的 C++ 源文件:
int main() {
int a = 1;
int b = 2;
if(a < b) {
return 1;
}
else if(a > b) {
return 2;
}
else {
return 3;
}
}
Run Code Online (Sandbox Code Playgroud)
我使用objdump命令来获取上述源代码的汇编代码。
该行int b = 2;已转换为mov DWORD PTR [rbp-0x4], 0x2.
其对应的机器码为C7 45 FC 02 00 00 00(十六进制格式)。
我想知道如何将汇编代码转换为二进制代码。我浏览了 x86-64 的英特尔参考手册,但我无法理解它,因为我是低级编程的新手。
假设我们使用的是 x86-64 机器,这意味着它的通用寄存器是 64 位长,它的数据总线一次可以处理 64 位,它的 ALU 可以处理最大 64 位数量(对吗?)。
有一个简单的指令,例如
MOV $5, %eax
Run Code Online (Sandbox Code Playgroud)
通过 64 位数据总线将 32 位数字移入 CPU 寄存器。
我已阅读以下内容:
An x86-64 instruction may be at most 15 bytes in length.
Run Code Online (Sandbox Code Playgroud)
我的问题是,如果数据总线最大为 64 位,这怎么可能?它如何处理 120 位的指令。CPU是否会在多个周期中获取它?
我的第二个问题是,是否有长度更大的特殊寄存器来存储所有 120 位?
assembly x86-64 instruction-set cpu-architecture cpu-registers
我正在使用 Microsoft Visual Studio 2010,并且我正在尝试支持我的项目的 64 位构建。据我所知,64 位架构不支持 __asm 关键字。
以下是当项目仅支持 32 位构建时它的工作原理。
void*
Class1::Class2::operator new(size_t size)
{
void *pCaller;
__asm mov edx, [ebp+4]
__asm mov pCaller, edx
char *pMem = (char *) malloc (sizeof(Class2) + size);
doSomething(pMem, pCaller);
void *ptr = (void *) (pMem + sizeof(Class2));
return(ptr);
}
Run Code Online (Sandbox Code Playgroud)
我可以使用预处理器指令使上述函数取决于建筑类型。
void*
Class1::Class2::operator new(size_t size)
{
#ifndef _WIN64
void *pCaller;
__asm mov edx, [ebp+4]
__asm mov pCaller, edx
char *pMem = (char *) malloc (sizeof(Class2) + size);
doSomething(pMem, pCaller);
void *ptr …Run Code Online (Sandbox Code Playgroud) 我正在研究低级位黑客,并想为每个黑客编写一个汇编程序。这是我检查数字是否偶数的方法:
is_even:
# check if an integer is even.
# This is the same as seeing if its a multiple of two, i.e., & 1<<n - 1
# rdi stores the number
xor %eax, %eax
test $0b1, %rdi
setz %al
ret
_start:
mov $5, %rdi
call is_even
Run Code Online (Sandbox Code Playgroud)
有什么方法可以改进上述内容或使其更具可读性?是否可以is_even使用 2 条指令而不是 3 条指令进行检查,因为第一条xor和第二条指令setz似乎可能会转换为一条(如果可能的话)。
我试图查看 MSVC 如何分配其 32 字节的影子空间,但似乎它只分配 8 字节的影子空间。
// Test.c
int main() {int var1 = 1;}
Run Code Online (Sandbox Code Playgroud)
上面的程序生成以下 .asm 文件:
var1$ = 0
main PROC
; Test.c
sub rsp, 24 ; allocates 24 bytes
mov DWORD PTR var1$[rsp], 1
xor eax, eax
add rsp, 24
ret 0
main ENDP
Run Code Online (Sandbox Code Playgroud)
它只分配24个字节。当我声明 4 个变量时,它会分配相同的数量,并且由于每个变量都是 4 个字节,因此必然意味着 24 个字节中的 16 个字节用于声明的变量,留下 8 个字节用于影子空间。
仅当声明 5 个变量时,它才会分配 40 字节的影子空间。为什么它只分配8字节的影子空间?
我使用命令编译了程序CL Test.c /Fa
我有以下一段C代码:
#include <stdio.h>
int main()
{
int i;
for(i=0; i<10; i++)
puts("hello, friend");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我这样编译的:gcc firstprog.c -o firstprog
然后,当使用gdb拆卸它时,我看到:
(gdb) disassemble
Dump of assembler code for function main:
=> 0x0000555555555149 <+0>: endbr64
0x000055555555514d <+4>: push rbp
0x000055555555514e <+5>: mov rbp,rsp
0x0000555555555151 <+8>: sub rsp,0x10
0x0000555555555155 <+12>: mov DWORD PTR [rbp-0x4],0x0
0x000055555555515c <+19>: jmp 0x55555555516e <main+37>
0x000055555555515e <+21>: lea rdi,[rip+0xe9f] # 0x555555556004
0x0000555555555165 <+28>: call 0x555555555050 <puts@plt>
0x000055555555516a <+33>: add DWORD PTR [rbp-0x4],0x1
0x000055555555516e <+37>: cmp …Run Code Online (Sandbox Code Playgroud) 我正在从《计算机系统:程序员的视角》学习 x86-64 汇编,并且遇到了一个练习,要求将一行 C 代码转换为(两个)等效的汇编指令。该代码是关于使用指针将一种类型的变量复制到另一种类型的变量。
指针变量声明如下:
src_t *sp; //src_t and dest_t are typedefs
dest_t *dp;
Run Code Online (Sandbox Code Playgroud)
需要翻译的C代码是:
*dp = (dest_t)*sp;
Run Code Online (Sandbox Code Playgroud)
假设指针sp和分别dp存储在寄存器%rdi和%rsi中,并且我们应该设置%rax(例如 、%eax或%ax)的“适当部分”%al来进行中间数据复制(因为 x86-64 不允许源和目标同时复制)是内存引用)。
现在,当src_tisunsigned char和dest_tis 时long,我为其编写了以下汇编代码:
movzbq (%rdi), %rax //move a byte into %rax with zero extension
movq %rax, (%rsi) //move 8 bytes of 'long' data
Run Code Online (Sandbox Code Playgroud)
但这本书以及Godboltgcc (与 一起使用-O3)都说它应该是
movzbl (%rdi), %eax …Run Code Online (Sandbox Code Playgroud) 抱歉,我一直不明白这里的规则。我已经删除了所有重复的帖子。这是第一个相关问题。\n请不要将此帖子标记为我另一篇帖子的重复(执行次数减少 3 倍,但执行效率几乎不变。在 C 中),即使代码有些相似,他们提出了截然不同的问题。这也是我同一天发现的两个问题。类似的帖子因“误判”而被重复,然后被关闭。可能是我没有把这个问题说清楚。我真的很希望得到答案,所以我重新发布了它。希望大家能够看清问题,非常感谢!
\n在下面的C代码中,我在第一次测试时间的循环中添加了一个“if”语句,执行时间完全相同。从理论上讲,它应该更慢。尽管分支预测可以使它们的性能几乎相同,但它实际上变得更快。这是什么原理呢?我尝试使用clang和gcc编译器分别在Mac和Linux环境中运行,并尝试了各种优化级别。为了防止缓存受到影响,我让速度较快的先执行,但有冗余代码的循环执行得更快。
\n如果您认为我的描述不可信,请将以下代码编译到您的计算机中并运行。希望有人能为我回答这个问题\xef\xbc\x8c谢谢。
\nC代码:
\n#include <stdio.h>\n#include <time.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define TLen 300000000\n#define SLen 10\n\nint main(int argc, const char * argv[]) {\n srandom((unsigned)time(NULL));\n \n // An array to increase the index,\n // the range of its elements is 1-256\n int rand_arr[128];\n for (int i = 0; i < 128; ++i)\n rand_arr[i] = random()%256+1;\n \n // A random text(very long), the range of its elements is 0-127\n char *tex = malloc((sizeof *tex) * …Run Code Online (Sandbox Code Playgroud) 为什么会出现分段错误?
我用的nasm -f elf64 t.asm -o t.o ld t.o -o t是linux下的编译。
我已经做了我能想到的一切。
section .data:
variable_int db 1
variable_string db "yaaaa", 10
section .text:
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, variable_string
mov rdx, 14
syscall
mov rax, 60
mov rdi, 0
syscall
Run Code Online (Sandbox Code Playgroud) 我将其插入 Godbolt,并惊喜地发现这两个函数调用a()和在除(使用大多数主要编译器)b()之外的任何其他情况下都是等效的:-O0
#include <cmath>
struct A {
int a,b,c;
float bar() {
return sqrt(a + b + c);
}
};
struct B {
int a[3];
float bar() {
int ret{0};
for (int i = 0; i<3; ++i) {
ret += a[i];
}
return sqrt(ret);
}
};
float a() {
A a{55,67,12};
return a.bar();
}
float b() {
B b{55,67,12};
return b.bar();
}
Run Code Online (Sandbox Code Playgroud)
Godbolt 输出为:
a():
movss xmm0, DWORD PTR .LC0[rip]
ret
b():
movss xmm0, DWORD …Run Code Online (Sandbox Code Playgroud)