- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
这是一道作业题。我试图从以下汇编代码(x86 linux 机器,使用 gcc -O2 优化编译)获取信息。我对每个部分都做了评论,以展示我所知道的。我的大部分假设可能是错误的,但我已经做了足够多的搜索,以至于我知道我应该在这里问这些问题。
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "result %lx\n" //Printed string at end of program
.text
main:
.LFB13:
xorl %esi, %esi // value of esi = 0; x
movl $1, %ecx // value of ecx = 1; result
xorl %edx, %edx // value of edx = 0; Loop increment variable (possibly mask?)
.L2:
movq %rcx, %rax // value of rax = 1; ?
addl $1, %edx // value of edx = 1; Increment loop by one;
salq $3, %rcx // value of rcx = 8; Shift left rcx;
andl $3735928559, %eax // value of eax = 1; Value AND 1 = 1;
orq %rax, %rsi // value of rsi = 1; 1 OR 0 = 1;
cmpl $22, %edx // edx != 22
jne .L2 // if true, go back to .L2 (loop again)
movl $.LC0, %edi // Point to string
xorl %eax, %eax // value of eax = 0;
jmp printf // print
.LFE13: ret // return
我应该把它变成下面填空的C代码
#include <stdio.h>
int main()
{
long x = 0x________;
long result = ______;
long mask;
for (mask = _________; mask _______; mask = ________) {
result |= ________;
}
printf("result %lx\n",result);
}
我有几个问题和健全性检查,我想确保我做对了,因为我发现的类似示例都不是针对优化代码的。在我自己编译一些试验时,我得到了一些接近的东西,但 L2 的中间部分总是关闭。
我的理解
开始时,esi 与自身进行异或,结果为 0,用 x 表示。然后将 1 添加到 ecx,这将由变量 result 表示。
x = 0; result = 1;
然后,我相信循环增量变量存储在 edx 中并设置为 0。这将在 for 循环的第三部分(更新表达式)中使用。我还认为这个变量必须是掩码,因为稍后将 1 添加到 edx,表示循环增量(掩码 = mask++),以及在 for 循环的中间部分比较 edx(测试表达式又名掩码!= 22 ).
mask = 0; (in a way)
然后进入循环,rax 被设置为 1。我根本不明白它用在什么地方,因为我没有声明第四个变量,尽管它稍后显示为被安定并归零。
movq %rcx, %rax;
然后循环变量加一
addl $1, %edx;
下一部分对我来说意义不大
我觉得接下来的三个操作构成了循环的主体表达式,但是我不知道如何处理它们。它会导致类似于 result |= x ... 但我不知道还有什么
salq $3, %rcx
andl $3735928559, %eax
orq %rax, %rsi
其余的我觉得我掌握得很好。进行比较(如果 mask != 22,再次循环),并打印结果。
我遇到的问题我不明白几件事。
1) 我不明白如何计算变量。在程序集(rax、rcx、rdx、rsi)中似乎有 3 个硬编码以及一个增量或临时存储变量。我认为 rsi 将是 x ,而 rcx 将是 result,但我不确定 mask 是 rdx 还是 rax,无论哪种方式,最后一个变量是什么是吗?
2) 我不确定的 3 个表达式的作用是什么?我觉得我以某种方式将它们与增量混淆了,但是在不知道变量的情况下我不知道如何解决这个问题。
任何和所有的帮助都会很棒,谢谢!
最佳答案
答案是:
#include <stdio.h>
int main()
{
long x = 0xDEADBEEF;
long result = 0;
long mask;
for (mask = 1; mask != 0; mask = mask << 3) {
result |= mask & x;
}
printf("result %lx\n",result);
}
在程序集中:
rsi
是result
.我们推断这是因为它是获得 OR
的唯一值ed,它是 printf
的第二个参数(在 x64 linux 中,参数按顺序存储在 rdi
、 rsi
、 rdx
和其他一些目录中)。
x
是一个常量,设置为 0xDEADBEEF
.这肯定不能推导,但它是有道理的,因为它似乎在 C 代码中被设置为常量,之后似乎没有被设置。
现在剩下的,它被 GCC 的反优化混淆了。你看,GCC 检测到循环将执行 21 次,并且认为破坏条件并用无用的计数器替换它是聪明的。知道了,我们看到了edx
是无用的计数器,rcx
是mask
.然后我们可以推断出真实情况和真正的“增量”操作。我们可以看到 <<= 3
在程序集中,注意如果你将一个 64 位 int 左移 22 次,它会变成 0(shift 3, 22 次意味着移动 66 位,所以它全部被移出)。
遗憾的是,这种反优化对于 GCC 来说非常普遍。该程序集可以替换为:
.LFB13:
xorl %esi, %esi
movl $1, %ecx
.L2:
movq %rcx, %rax
andl $3735928559, %eax
orq %rax, %rsi
salq $3, %rcx // implicit test for 0
jne .L2
movl $.LC0, %edi
xorl %eax, %eax
jmp printf
它做的事情完全一样,但我们删除了无用的计数器并保存了 3 条汇编指令。它还能更好地匹配 C 代码。
关于c - 将 O2 优化的 for 循环从汇编翻译成 C,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32647068/
我试图在图形模式下打印一个字符。通常当我打印我正在做的一个字符时: mov ah,14 ; ah=14 mov al,'x' int 10h ; print the character 这
我试图通过更改其中的一个字节来修改存储在内存中的字符串。我为此使用了 movb,但由于某种原因,给定内存位置的字节没有改变。 在 gdb 调试器上: 14 movb %al, (%r10) # nex
我一直在阅读一些汇编代码,并且开始发现调用指令实际上是与程序计数器相关的。 但是,每当我使用 Visual Studio 或 Windbg 进行调试时,它总是显示 call 0xFFFFFF ...这
我最近一直在使用 Visual C++ 中的内联汇编,我想知道是否可以直接向堆栈上的局部变量添加值,例如: push 5 add [esp], 7 这样做可以吗?我问这个问题是因为我在执行此操作时随机
我有下一个代码: mov al, -5 add al, 132 add al, 1 据我检查,溢出标志和进位标志将在第一个操作中设置,而在第二个操作中,仅设置溢出。 但我不明白为什么: 在无符号数中,
在 64 位 x86 汇编 nasm 中,如何将单个字节从寄存器移动到 .data 节中定义的内存位置? 我知道这有效 global _main section .data quotient db 0
我的汇编代码有问题。我想打印存储在寄存器 cx 中的数字,但是当我尝试打印它时,它打印的是 ascii 字符而不是 ascii 数字,所以我决定编写一个程序将 ascii char 转换为 ascii
为什么第 1B 行的跳转指令(例如)变成了 EBBD? 我知道“jmp”= EB但是BD是怎么计算的呢? 最佳答案 短跳转使用一个带符号的偏移量添加到 JMP 之后的指令地址。 例如,第一个 JMP
以下两者有什么区别: mov eax, [eax+4] 和 add eax, 4 mov eax, [eax] 如果不是,那么汇编器是否会选择哪个来进行某种优化? 最佳答案 这
看《The Shellcoder's Handbook》中的一些汇编和反汇编代码,发现一条指令的序列操作数是不一样的。 例如,在 assembly 上: mov ebx,0 并且,在反汇编时: mov
我有这个非常简单的汇编代码: start: add ax, 100 ; if ax overflow add to bx 1 jmp start 但我不知道如何检测 ax 寄存器溢出,有人可以帮
在 64 位 x86 汇编 nasm 中,如何将单个字节从寄存器移动到 .data 节中定义的内存位置? 我知道这有效 global _main section .data quotient db 0
我的汇编代码有问题。我想打印存储在寄存器 cx 中的数字,但是当我尝试打印它时,它打印的是 ascii 字符而不是 ascii 数字,所以我决定编写一个程序将 ascii char 转换为 ascii
我正在学习一些关于操作系统开发的教程,我发现了一篇关于多重引导 header 。这些是您必须定义的一些“神奇”值才能使用GRUB2。这些是命令: # Declare constants used f
为什么第 1B 行的跳转指令(例如)变成了 EBBD? 我知道“jmp”= EB但是BD是怎么计算的呢? 最佳答案 短跳转使用一个带符号的偏移量添加到 JMP 之后的指令地址。 例如,第一个 JMP
我正在尝试从内存中复制一些单词并使用汇编将其保存到另一个内存地址。我正在尝试为其编写代码,但我不确定其中的某些部分。我将简要描述我想要做什么。 源地址、目标地址和要复制的字数是函数的输入参数。 最佳答
当我们想要像这样创建一个初始化变量时: name db 'zara ali' 我们创建了一个字节大小变量,但我们在其中存储了一个字符串 这怎么可能?? 当我们使用这条指令时: MOV ecx, nam
我还是汇编的新手,我还不知道汇编中的许多命令代码。我想在 16 位寄存器中进行除法。我想打印它的内容。我知道我需要将寄存器的内容转换为 ASCII 进行打印,但同样,我的问题是除法。请帮我。 比如cx
使用有什么区别: c.eq.s $1, $2 bc1t L2 并使用: beq $1, $2, L2 如果他们做同样的事情,为什么有两种分支方式?如果它们不同,那么它们各自的好处是什么
源代码: int main() { int i; for(i=0, i : push rbp 2. 0x000055555555463b :
我是一名优秀的程序员,十分优秀!