Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

14题解 #229

Closed
wants to merge 701 commits into from
Closed

14题解 #229

wants to merge 701 commits into from

Conversation

BlueArc0
Copy link
Contributor

^_^

Mq-b and others added 30 commits October 26, 2023 09:32
关于 `constexpr` 函数非良构不要求诊断的规则描述更正
Create 第10题/伊凡Gogh.cpp
@BlueArc0
Copy link
Contributor Author

BlueArc0 commented Jan 12, 2024

测试编译器:x86-64 gcc 13.2,x86-64 clang 17.0.1,MinGW gcc 13.2.0,x64 msvc v19.37.32824

@BlueArc0
Copy link
Contributor Author

BlueArc0 commented Jan 12, 2024

适配编译器适配的头大>_<

@Mq-b Mq-b self-requested a review January 12, 2024 16:46
Copy link
Owner

@Mq-b Mq-b left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

稍微介绍一下此代码的原理、你的想法、测试平台情况。

@BlueArc0
Copy link
Contributor Author

BlueArc0 commented Jan 13, 2024

call_todo函数将该函数的返回地址(调用该函数时call指令压入栈中的地址)传递给todotodo从该地址开始搜索以8B开头的mov指令,并计算出ss::a的地址(ss::a的引用即[i+[i+2]+6],其中i是该条mov指令的开始位置,[i+2]mov指令中存储的偏移地址,6是该条指令的长度)。
由于std::cout << ss::a << '\n';代码读取了ss::a,我就希望从这段代码入手获取ss::a的地址,经过测试发现,读取ss::a的通常是一条以8B开头的mov指令,于是就有了todo函数;
call_todo是为了避免CWG2811中不得使用main函数的规定,通过返回地址间接获取到main函数中mov指令前的地址,其中函数参数void*、局部变量void* p2是用来对齐不同编译器栈中内存布局不同,此时*(&p+3)即该函数的返回地址。
由于编译器不一定把读取ss::a编译成以8B开头的mov指令,以及由于栈中内存布局不同而导致*(&p+3)并不是函数的返回地址,所以这段代码不支持所有编译器,以下是测试能够正确运行的编译器:
x86-64 gcc 13.2,x86-64 clang 17.0.1,MinGW gcc 13.2.0,x64 msvc v19.37.32824

@BlueArc0
Copy link
Contributor Author

BlueArc0 commented Jan 13, 2024

最后把那个0xCC_b改成0xC3_b是因为在gcc上函数结束使用nop(90)而不是msvc的int 3(CC),所以改成了ret(C3)。这是失败路径防止意外修改的代码。

@MrShinshi
Copy link
Contributor

思路挺个性,但你最好把这玩意儿写项目里去

@Mq-b
Copy link
Owner

Mq-b commented Jan 13, 2024

我其实根本不懂这写法,不过看起来你写的很不错。

Copy link
Collaborator

@rsp4jack rsp4jack left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#229 (comment) 作为注释加进去。

@Mq-b
Copy link
Owner

Mq-b commented Jan 13, 2024

call_todo函数将该函数的返回地址(调用该函数时call指令压入栈中的地址)传递给todotodo从该地址开始搜索以8B开头的mov指令,并计算出ss::a的地址(ss::a的引用即[i+[i+2]+6],其中i是该条mov指令的开始位置,[i+2]mov指令中存储的偏移地址,6是该条指令的长度)。 由于std::cout << ss::a << '\n';代码读取了ss::a,我就希望从这段代码入手获取ss::a的地址,经过测试发现,读取ss::a的通常是一条以8B开头的mov指令,于是就有了todo函数; call_todo是为了避免CWG2811中不得使用main函数的规定,通过返回地址间接获取到main函数中mov指令前的地址,其中函数参数void*、局部变量void* p2是用来对齐不同编译器栈中内存布局不同,此时*(&p+3)即该函数的返回地址。 由于编译器不一定把读取ss::a编译成以8B开头的mov指令,以及由于栈中内存布局不同而导致*(&p+3)并不是函数的返回地址,所以这段代码不支持所有编译器,以下是测试能够正确运行的编译器: x86-64 gcc 13.2,x86-64 clang 17.1.0,MinGW gcc 13.2.0,x64 msvc v19.37.32824

Markdown 别写一坨,稍微列举分类一下,我看着都麻烦。

@BlueArc0
Copy link
Contributor Author

等我整理一下注释,没想好怎么塞代码里

@Matrix-A
Copy link
Contributor

在 WSL 和虚拟机上测试未通过,系统均为 Ubuntu22.04 LTS ,编译器为 G++11.4

(gdb) p &p
$1 = (void **) 0x7fffffffdb98
(gdb) x /10x &p
0x7fffffffdb98: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdba8: 0xe7777300      0xc38ba66e      0xffffdbc0      0x00007fff
0x7fffffffdbb8: 0x55555305      0x00005555
(gdb) p main
$2 = {int (void)} 0x5555555552d5 <main()>
(gdb)

}
int main() {
//todo(reinterpret_cast<std::byte*>(main));
call_todo(nullptr);//使用`call_todo`是为了避免[CWG2811](https://cplusplus.github.io/CWG/issues/2811.html)中不得使用`main`函数的规定。
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

考虑到 #229 (comment) 这个 regression bug,revert 掉这个修复也是可以考虑的。

@rsp4jack
Copy link
Collaborator

rsp4jack commented Jan 14, 2024

由于整个仓库的 git history 被改写,请更新 fork(rebase)。

@rsp4jack rsp4jack added the treasure best luse label Jan 14, 2024
@BlueArc0
Copy link
Contributor Author

在 WSL 和虚拟机上测试未通过,系统均为 Ubuntu22.04 LTS ,编译器为 G++11.4

(gdb) p &p
$1 = (void **) 0x7fffffffdb98
(gdb) x /10x &p
0x7fffffffdb98: 0x00000000      0x00000000      0x00000000      0x00000000
0x7fffffffdba8: 0xe7777300      0xc38ba66e      0xffffdbc0      0x00007fff
0x7fffffffdbb8: 0x55555305      0x00005555
(gdb) p main
$2 = {int (void)} 0x5555555552d5 <main()>
(gdb)

看起来把*(&p+3)改成*(&p+4)就能解决,这个位置确实挺玄学的;或者直接使用todo(reinterpret_cast<std::byte*>(main));而不是call_todo(nullptr);

@Mq-b
Copy link
Owner

Mq-b commented Jan 14, 2024

@rsp4jack 我其实很好奇,你改写了啥,让这个 pr 直接有了 701 个提交。

@rsp4jack
Copy link
Collaborator

@Mq-b 从整个 git history 里删了一些很大的文件,然后从上传那个文件的 commit 到 HEAD 之间的所有 commit 都被修改了。

@Mq-b
Copy link
Owner

Mq-b commented Jan 14, 2024

哦,,,你直接删了 Git 的历史是吧。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
treasure best luse
Projects
None yet
Development

Successfully merging this pull request may close these issues.