- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
首先:该代码被认为是纯粹的乐趣,请在生产中不要做任何类似的事情。在任何环境下编译并执行这段代码后,对于您,您的公司或您的驯鹿造成的任何伤害,我们概不负责。以下代码不安全,不可移植,并且非常危险。被警告。下面的长帖子。你被警告了。
现在,在免责声明之后:让我们考虑以下代码:
#include <stdio.h>
int fun()
{
return 5;
}
typedef int(*F)(void) ;
int main(int argc, char const *argv[])
{
void *ptr = &&hi;
F f = (F)ptr;
int c = f();
printf("TT: %d\n", c);
if(c == 5) goto bye;
//else goto bye; /* <---- This is the most important line. Pay attention to it */
hi:
c = 5;
asm volatile ("movl $5, %eax");
asm volatile ("retq");
bye:
return 66;
}
fun
,我纯粹是创建该函数作为引用来获取生成的汇编代码。
F
指向不带任何参数并返回一个int的函数。
hi
的地址,这在clang中也适用。然后,我们做一些邪恶的事情,我们创建一个名为f的函数指针
F
并将其初始化为上面的标签。
C
的局部变量,然后我们将其打印出来。
if
,用于检查分配给
c
的值是否实际上是我们需要的值,如果是,请转到
bye
,以便他的应用程序正常退出,退出代码为66。如果可以,则认为这是正常的退出代码。
hi
之后的代码是将5分配给c的值,然后进行两行汇编以将
eax
的值初始化为5并实际从“函数”调用返回。如上所述,有一个引用函数
fun
生成相同的代码。
-O1
,并且
O0
给出相似的结果,尽管更长一些):
# else goto bye is COMMENTED OUT
fun:
mov eax, 5
ret
.LC0:
.string "TT: %d\n"
main:
push rbx
mov eax, OFFSET FLAT:.L3
call rax
mov ebx, eax
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
cmp ebx, 5
je .L4
.L3:
movl $5, %eax
retq
.L4:
mov eax, 66
pop rbx
ret
mov eax, OFFSET FLAT:.L3
,其中
L3
对应于我们的
hi
标签,其后一行:实际调用它的
call rax
。
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 66
TT: 5
-O0
,我们得到由gcc生成的以下程序集:
# else goto bye is UNCOMMENTED
# even gcc -O0 "knows" hi: is unreachable.
fun:
push rbp
mov rbp, rsp
mov eax, 5
pop rbp
ret
.LC0:
.string "TT: %d\n"
main:
push rbp
mov rbp, rsp
sub rsp, 48
mov DWORD PTR [rbp-36], edi
mov QWORD PTR [rbp-48], rsi
mov QWORD PTR [rbp-8], OFFSET FLAT:.L4
mov rax, QWORD PTR [rbp-8]
mov QWORD PTR [rbp-16], rax
mov rax, QWORD PTR [rbp-16]
call rax
mov DWORD PTR [rbp-20], eax
mov eax, DWORD PTR [rbp-20]
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
cmp DWORD PTR [rbp-20], 5
nop
.L4:
mov eax, 66
leave
ret
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 66
printf
,而罪魁祸首是
mov QWORD PTR [rbp-8], OFFSET FLAT:.L4
行,其中
L4
实际上对应于我们的
bye
标签。
hi
添加到生成的代码中之后,该部分中没有一段代码。
c
与5进行比较的代码。
O0
的clang会产生以下噩梦,这种噩梦会崩溃:
# else goto bye is UNCOMMENTED
# clang -O0 also doesn't emit any instructions for the hi: block
fun: # @fun
push rbp
mov rbp, rsp
mov eax, 5
pop rbp
ret
main: # @main
push rbp
mov rbp, rsp
sub rsp, 48
mov dword ptr [rbp - 4], 0
mov dword ptr [rbp - 8], edi
mov qword ptr [rbp - 16], rsi
mov qword ptr [rbp - 24], 1
mov rax, qword ptr [rbp - 24]
mov qword ptr [rbp - 32], rax
call qword ptr [rbp - 32]
mov dword ptr [rbp - 36], eax
mov esi, dword ptr [rbp - 36]
movabs rdi, offset .L.str
mov al, 0
call printf
cmp dword ptr [rbp - 36], 5
jne .LBB1_2
jmp .LBB1_3
.LBB1_2:
jmp .LBB1_3
.LBB1_3:
mov eax, 66
add rsp, 48
pop rbp
ret
.L.str:
.asciz "TT: %d\n"
O1
,我们将从gcc获得:
# else goto bye is UNCOMMENTED
# gcc -O1
fun:
mov eax, 5
ret
.LC0:
.string "TT: %d\n"
main:
sub rsp, 8
mov eax, OFFSET FLAT:.L3
call rax
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
.L3:
mov eax, 66
add rsp, 8
ret
hi
部分(
mov eax, OFFSET FLAT:.L3
脚尖移到
L3
,它对应于我们的
bye
部分),不幸的是,决定在
rsp
之前增加
ret
是一个好主意,以便确保我们在需要的地方完全不同成为。
# else goto bye is UNCOMMENTED
# clang -O1
fun: # @fun
mov eax, 5
ret
main: # @main
push rax
mov eax, 1
call rax
mov edi, offset .L.str
mov esi, eax
xor eax, eax
call printf
mov eax, 66
pop rcx
ret
.L.str:
.asciz "TT: %d\n"
1
? c到底是怎么结束的呢?
if
之后将
if
和
else
都移到同一位置的死代码,但是在这里我的知识和见识就停止了。
else
分支,编译器便决定将这两个标签视为等效,或者为什么clang在其中添加了1,最后但并非最不重要:对C标准有深刻理解的人可能会指出这段代码与正常情况的偏差如此之大,以至于我们最终陷入了这种非常奇怪的情况。
最佳答案
someone with a deep understanding of the C standard could maybe point out where this piece of code deviated so badly from normality that we ended up in this really really weird situation.
goto
标签也是UB。
asm
语句在此处发出
ret
指令来使此工作生效。
if(0) { ... }
不发出任何实现
...
的指令
c=5
中的
hi:
编译时完全禁用了优化(并且
else goto bye
注释了),类似于
movl $5, -20(%rbp)
的asm。即使用调用者的RBP修改调用者堆栈框架中的局部变量。因此,您有一个嵌套函数。
mov
-immediate存储到堆栈的机器代码的可执行蹦床,如果您使用指向嵌套函数的指针!)
asm volatile ("movl $5, %eax");
缺少EAX上的垃圾。如果正常到达此语句,则踩到编译器的脚趾,它将是UB,而不是好像它是一个单独的函数。
cli
(禁用中断)之类的指令,没有涉及整数寄存器的任何内容,并且绝对不是
ret
。
asm("")
,也可以将其用作
__attribute__((naked))
函数的主体。
关于c++ - 谜:将GNU C标签指针转换为函数指针,并使用内联asm将ret放入该 block 中。 block 被优化掉了吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59103667/
有什么区别: 1) !ret 2) ret != 0 ? if (ret != 0 || stack[fd] == NULL || stack[fd][0] == '\0') { if (!ret
我正在使用 java 客户端 (jrets) 来查询 RETS 服务器。此 rets 服务器不支持 OFFSET 功能。 服务器管理员告诉我使用 MODIFIED 字段作为翻阅记录的一种方式。但是,我
我想从 RETS 服务器下载所有属性(property) list ,包括所有照片 URL。我正在使用 DMQL2 和 PHRETS 库。属性和照片对象存储在 RETS 服务器的不同表中。 要获取所有
各位, 我将使用 RETS 进行真实项目。我有一份文件,说明我的经理实际上在寻找什么。但我真的不知道如何开始使用 RETS MLS,因为它有很多这样的文档。 通过一些研发,我几乎没有想出任何有值(va
我正在尝试在 Microsoft visual studio 2013 on C++ 上编译一个为 linux 编写的程序(是 C 和 C++ 的混合体 (#include .h),我将全部转换为 C
当我尝试像这样编写自己的decay_t时: #include template struct auto_decay { auto operator () () noexcept {
我经常看到通过测试小于零而不是显式定义来检查 POSIX 函数错误的代码,通常唯一的错误代码使用 -1。也就是 ret = function(); if (ret < 0) { ... } 对
考虑以下空 C 程序(标准保证编译器执行隐式 return 0): int main(int argc, char* argv[]) {} 您可以将任何逻辑添加到此函数中来操作 argc 和 argv
我在 ubuntu 12.04 和 auctex 11.86 上运行 emacs 23.3.1。每当我去编译 latex 文档(使用 C-c C-c)时,如果没有错误,一切都编译得很好。但是,如果有任
我有 RETS 元数据文件,我想将其转换为数据库模式,这样我就可以查询我的数据库而不是 RETS 服务器。 有谁知道可以使用 xml 并将其转换为数据库模式的工具?或者可能是数据库模式本身? 一切都包
由于 ret 指令是一个间接调用,x86 上的 ret 指令会停止流水线,还是以某种方式优化为更直接的调用? 最佳答案 根据英特尔优化引用手册,分支预测单元包含一个 Return Stack Buff
我有以下头文件: #include #include #include #include #include #include /** **/ // size: 1B typedef en
我目前正在开发一个网站并从 RETS(房地产交易标准)API 中提取列表。我的一切工作正常,但我的问题是在尝试深入挖掘查询时出现的。作为引用,我正在使用 Node RETS Client但我不确定它与
if(isset($_POST['update'])) { $rets_login_url = $Fetch['rets_url']; $rets_username = $Fetch['rets_us
我有这个函数,它主要由内联汇编码成。 long *toarrayl(int members, ...){ __asm{ push esp mov eax, me
int suma(int* array, int len) { asm(" xor %eax, %eax # resultado = 0 \n"
我对汇编很陌生,我不明白在 proc 结束时你用 ret 写一个数字的确切含义是什么。陈述。 像这样: Function Proc push ax cx . ...body... . pop cx a
我正在尝试使用 PHRETS 从 rets 服务器在 WordPress 中添加帖子。不幸的是,正在添加重复的帖子。我已使用 WP 查询使用元键和值检查现有帖子。当我尝试添加 10 或 50 个帖子时
我有以下程序: SECTION .text main: mov ebx, 10 mov ecx, 50 repeat: inc ebx loop repeat
假设我正在 x86 汇编中编写一个例程,例如“add”,它将两个作为参数传递的数字相加。 在大多数情况下,这是一个非常简单的方法: push ebp mov ebp, esp mov eax, [eb
我是一名优秀的程序员,十分优秀!