今天你要来点 puzzle 吗?

Intro

  你的解法被允许包含任何可以通过编译的代码, 包括但不限于内联汇编 (不过 puzzle 设计时并不会考虑这种解法), 未指明行为或未定义行为, 但请确保自己知道自己解法的正确性从何而来. 当你的解法依赖 RSI/ABI/编译器特性时, 请以如下配置为参考 (这个配置下的编译结果应该和 NOI Linux 2 是一致的, 如果你发现不一致的情况可以告诉我 qwq):

Target: x86_64-linux-gnu
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04.2)

  全文编译命令如下:

g++ puz_N.cpp ans_N.cpp -Wall -Wextra -Wshadow -Wconversion -Werror -Og -fno-inline -fno-stack-protector -masm=att -o test

其中:

  • -Wall -Wextra -Wshadow -Wconversion -Werror 要求你在写 UB 的时候伪装一下, 不能被编译器发现 (大嘘).
  • -Og 保证你的代码几乎被逐句翻译为可读性较高的汇编指令而不引入过多优化.
  • -fno-inline 显式阻止了函数内联 (这在 -Og 优化级别下仍然可能发生), 以方便你更好地利用过程调用相关的 ABI 约定.
  • -fno-stack-protector 关闭了汇编指令层次的栈保护措施, 我们大概不会做这方面的 puzzle, 关掉这个单纯是为了让函数序言不那么冗长.
  • -masm=att 指定汇编格式. 别问为什么用 AT&T, 因为 ICS 也用这个.

  测试用的头文件如下 (注意它可能会不断更新, 但总是兼容旧版; 你可以在 ans_N.cpp 中直接 include 它).

#pragma once#include <cstdio>
#include <random>
#include <cstdlib>
#include <type_traits>typedef u_int8_t u8;
typedef u_int32_t u32;
typedef __float80 f80; // long double on x86-64 is 80-bit extended precision#define Success() \do { \fprintf(stderr, "Puzzle solved.\n"); \exit(0); \} while (false)#define Require(cond, msg, ...) \do { \if (!(cond)) { \fprintf(stderr, "Failed: " msg "\n", ##__VA_ARGS__); \_Exit(1); \} \} while (false)#define ReqEq_u32(x, y) Require(u32(x) == u32(y), "%u != %u", u32(x), u32(y))

  我们认为你的答案正确, 当且仅当它能通过编译并且使得 test 正常运行并最终返回 0.

  为了方便你测试, 提供一个编译脚本:

#!/bin/bash
id=$1
puzzle=puz_$id.cpp
answer=ans_$id.cpp
FLAGS="-Wall -Wextra -Wshadow -Wconversion -Werror -Og -fno-inline -fno-stack-protector -masm=att"
g++ $FLAGS $puzzle $answer -o test && ./test

你可以将 judge.h, puz_N.cpp, ans_N.cpprun.sh 保存在同一目录下, 然后使用命令

linux> chmod +x run.sh

为脚本添加可执行权限, 此后就能使用

linux> ./run.sh N

来编译并运行第 \(N\) 个 puzzle 的程序.

Puzzle #0

// puz_0.cpp#include "judge.h"u32 guess(void);u32 sample() {return std::random_device()();
}int main() {u32 x = sample();u32 y = guess();ReqEq_u32(x, y);Success();
}

  请在 ans_0.cpp 中实现 u32 guess(void) 函数.

  • 冷知识: 在函数声明时, u32 guess(void);u32 guess(); 并不等价. 前者告诉编译器 "这是一个无参数的函数", 后者告诉编译器 "这是一个函数, 但我不知道它的参数类型".

Discussion

  @zero4338 提供了一种解答 (因为 ta 提供答案时编译命令中的 -Wx 选项没有这么多, 我略修改了代码使得它可以通过编译):

#include "judge.h"u32 guess(void) {int z = 0xdeadbeef; // useless value to escape compiler checkint y = *((u32*)(&z) + 12);return y;
}

然而这个解答并不正确: 除了 guess::z 因为被取地址必须分配在栈上外, 其他三个局部变量 (main::x, main::y, guess::y) 并不一定被编译器安排在栈空间上, 而更有可能被安排为寄存器变量, 这时基于栈偏移的做法就不奏效了.

  不过, 我们可以弱化一下问题, 让基于栈偏移的做法有前途. 我们将所有局部变量加上 volatile 修饰:

  • volatile 是一种类型修饰符, 告诉编译器受修饰变量可能被除当前线程以外的线程, 程序等修改. 从效果上, volatile 强制编译器每次读变量时, 都从内存中读取 (因此它也必须存储在内存中), 而不把它缓存在寄存器中.
    • volatile int x;x++++x-std=c++2a (C++ 20) 起被遗弃 (deprecated, by -Wvolatile), 因为它们隐含地违背了上述原则.
// puz_0.cpp#include "judge.h"u32 guess(void);u32 sample() {return std::random_device()();
}int main() {volatile u32 x = sample();volatile u32 y = guess();ReqEq_u32(x, y);return 0;
}
// ans_0.cpp#include "judge.h"u32 guess(void) {volatile u32 z = 0xdeadbeef; // useless value to escape compiler checkvolatile u32 y = *((u32*)(&z) + 12);return y;
}

这样, 编译后使用命令

linux> objdump -d test
  • objdump 命令用于展示目标文件 (你可以理解作由编译器生成的具有特定组织格式的一类二进制文件) 的信息.
    • -d/--disassemble 指定展示可执行节 (executable section) 的反汇编信息.

并观察 mainguess 的汇编:

main:endbr64 sub    $0x18,%rsp       ; 将栈指针 (总是 rsp) 减去 0x18;   相当于为当前函数分配 0x18 字节的栈帧call   1349             ; call <sample>, 这个函数名 objdump 会帮你标注;   汇编里的数字是 PC 相对寻址的偏移量, 不重要mov    %eax,0xc(%rsp)   ; eax 是 sample() 的 32 位返回值;   所以 main::x 在 rsp+12call   15a7             ; call <guess>;   此指令执行后 rsp<-rsp+8 (压入返回地址),;   紧接着从 guess 标签的下一条指令开始执行...mov    %eax,0x8(%rsp)mov    0xc(%rsp),%edxmov    0x8(%rsp),%eaxcmp    %eax,%edx; ......; ......guess:endbr64 movl   $0xdeadbeef,-0x4(%rsp)  ; guess::z 在 rsp-4mov    0x2c(%rsp),%eax         ; 求出 guess::y 的值. 我们需要调整 0x2c 这个偏移量mov    %eax,-0x8(%rsp)mov    -0x8(%rsp),%eaxret

根据汇编, 正确的 u32 指针偏移量应该是 \((12+8+4)/4=6\), 即 y = *((u32*)(&z) + 6). 此时编译运行成功.

  • (相信大家比较熟悉了.) C/C++ 中, 对指针的加减行为 由指针类型决定. 笼统地说, type* pp + x 意味着 p 偏移 x * sizeof(type). 特别地, 在 C++ 中, 对 void* 的加法是未定义行为, 并且可能无法通过编译. 但 C 的 GNU 扩展中规定 void* 的加法步长为 \(1\), 相当于将它视作 char* 来运算.

  但是, 即使加上 volatile 标记, 这种解法也不尽优美: "main::x%rsp+12 仍然依赖于编译器的规划." 再例如, 我们知道 x86-64 Linux 约定, 在 call 指令之前, %rsp 必须 \(16\) 字节对齐. 因此, 进入 main 函数时, $rsp % 16 == 8, main 函数内部申请了 \(4+4=8\) 字节栈空间, 所以理论上编译器可以只给 main 分配 \(8\) 字节内存 (即代码块第三行改为 sub $0x8,%rsp), 这样也满足栈对齐需求. 选择 \(24\) 字节而非 \(8\) 字节仍然依赖于编译器甚至优化选项.

Solution

TBC...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1182988.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探讨口碑好的去屑洗发水,黛熙梦名列靠谱榜单 - 工业品牌热点

在消费升级与健康意识觉醒的当下,一款口碑好的去屑洗发水不仅是解决头皮困扰的工具,更是守护个人形象与社交自信的隐形护盾。面对市场上琳琅满目的去屑产品,如何找到兼具强力去屑效果、温和配方与高性价比的选择?以…

基于Matlab的说话人识别系统:从代码到GUI的实现

基于matlab的说话人识别系统 1、完整可运行代码&#xff0c; 2、有注释 3、识别率高&#xff0c;操作简单 4、有完整参考资料 5、有gui界面。一、引言 说话人识别在当今数字化时代有着广泛的应用&#xff0c;无论是安全认证还是语音助手等领域&#xff0c;都发挥着重要作用。Ma…

AI智能体终极记忆方案!Graphiti教程从零到精通(建议收藏),一篇就够了!

Graphiti是专为AI智能体设计的开源图框架&#xff0c;解决传统RAG在动态数据管理上的不足。它支持实时增量更新、双时间模型、混合检索和自定义实体类型&#xff0c;能构建动态知识图谱&#xff0c;实现毫秒级响应。通过简单API调用&#xff0c;开发者可快速搭建AI记忆系统&…

收藏!未来5年程序员最优赛道:AI大模型必冲!

毫不夸张地说&#xff0c;未来5年&#xff0c;能引领程序员职业跃迁的核心技术方向&#xff0c;非AI大模型莫属&#xff01;无论是大厂布局还是市场需求&#xff0c;都在印证这一趋势已成定局。 &#x1f449; 华为全力押注Agent技术&#xff0c;实现80%新增业务系统的Agent化覆…

华硕笔记本风扇噪音终极解决方案:告别恼人异响的静音革命

华硕笔记本风扇噪音终极解决方案&#xff1a;告别恼人异响的静音革命 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops. Control tool for ROG Zephyrus G14, G15, G16, M16, Flow X13, Flow X16, TUF, Strix, Scar and other models 项目…

【建议收藏】AI大模型学习路径全解析:掌握这6大模块,轻松实现程序员职业转型与薪资跃迁

AI大模型是未来5年程序员的重要发展方向&#xff0c;文章提供了系统化的学习路线&#xff1a;从基础认知到核心技术(RAG/Prompt/Agent)&#xff0c;再到开发能力、应用场景开发、项目落地和面试准备。掌握正确学习顺序可帮助程序员快速入门大模型应用开发&#xff0c;把握AI风口…

SeedVR视频修复革命:AI技术让模糊记忆重获新生

SeedVR视频修复革命&#xff1a;AI技术让模糊记忆重获新生 【免费下载链接】SeedVR-7B 项目地址: https://ai.gitcode.com/hf_mirrors/ByteDance-Seed/SeedVR-7B 还在为那些承载珍贵回忆的模糊视频而烦恼吗&#xff1f;家庭录像、婚礼庆典、成长记录&#xff0c;这些本…

基于深度学习YOLOv10的番茄成熟度检测系统(YOLOv10+YOLO数据集+UI界面+Python项目源码+模型)

一、项目介绍 项目背景: 在农业生产中&#xff0c;番茄的成熟度检测是决定采摘时机和产品质量的关键环节。传统的成熟度检测方法依赖于人工观察&#xff0c;效率低且主观性强&#xff0c;难以满足大规模种植的需求。随着计算机视觉和深度学习技术的发展&#xff0c;基于图像的…

7步实战:将闲置电视盒子变身高性能Armbian服务器

7步实战&#xff1a;将闲置电视盒子变身高性能Armbian服务器 【免费下载链接】amlogic-s9xxx-armbian amlogic-s9xxx-armbian: 该项目提供了为Amlogic、Rockchip和Allwinner盒子构建的Armbian系统镜像&#xff0c;支持多种设备&#xff0c;允许用户将安卓TV系统更换为功能强大的…

高效流媒体下载:打造个人视频库的完整方案

高效流媒体下载&#xff1a;打造个人视频库的完整方案 【免费下载链接】N_m3u8DL-RE 跨平台、现代且功能强大的流媒体下载器&#xff0c;支持MPD/M3U8/ISM格式。支持英语、简体中文和繁体中文。 项目地址: https://gitcode.com/GitHub_Trending/nm3/N_m3u8DL-RE 在当今数…

游戏辅助工具终极配置手册:从零开始轻松掌握YimMenu

游戏辅助工具终极配置手册&#xff1a;从零开始轻松掌握YimMenu 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi/YimMen…

Mem Reduct终极指南:3步快速提升电脑性能的内存优化工具

Mem Reduct终极指南&#xff1a;3步快速提升电脑性能的内存优化工具 【免费下载链接】memreduct Lightweight real-time memory management application to monitor and clean system memory on your computer. 项目地址: https://gitcode.com/gh_mirrors/me/memreduct …

基于深度学习YOLOv10的PCB电路板缺陷检测系统(YOLOv10+YOLO数据集+UI界面+Python项目源码+模型)

一、项目介绍 项目背景: 在电子制造业中&#xff0c;印刷电路板&#xff08;PCB&#xff09;的质量检测是确保电子产品性能可靠性的关键环节。传统的PCB缺陷检测方法依赖于人工目检或自动化光学检测&#xff08;AOI&#xff09;设备&#xff0c;效率低且成本高。基于计算机视觉…

机器人自修复“肌肉”的技术突破

工程师开发出机器人的自修复“肌肉” 一项内布拉斯加大学林肯分校的工程团队最近在软体机器人和可穿戴系统领域取得了新进展&#xff0c;该系统能够模仿人类和植物皮肤检测及自我修复损伤的能力。 工程师埃里克马尔科维卡与研究生伊桑克林斯和帕特里克麦克马尼加尔&#xff0c;…

基于深度学习YOLOv10的脑肿瘤检测系统(YOLOv10+YOLO数据集+UI界面+Python项目源码+模型)

一、项目介绍 项目背景: 脑肿瘤是一种严重的医学病症&#xff0c;早期检测和诊断对于患者的治疗和康复至关重要。传统的脑肿瘤检测方法依赖于医学影像的人工分析&#xff0c;这不仅耗时且容易受到主观因素的影响。随着深度学习技术的发展&#xff0c;基于计算机视觉的自动检测…

DownKyi文章仿写创作指南:打造差异化内容

DownKyi文章仿写创作指南&#xff1a;打造差异化内容 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xff09;。 项…

ComfyUI-Ollama实战指南:零基础搭建智能创作工作流

ComfyUI-Ollama实战指南&#xff1a;零基础搭建智能创作工作流 【免费下载链接】comfyui-ollama 项目地址: https://gitcode.com/gh_mirrors/co/comfyui-ollama 还在为AI模型复杂的部署流程而头疼吗&#xff1f;想要在可视化界面中直接调用大语言模型吗&#xff1f;Com…

基于深度学习YOLOv10的玉米幼苗杂草检测系统(YOLOv10+YOLO数据集+UI界面+Python项目源码+模型)

一、项目介绍 项目背景: 在农业生产中&#xff0c;杂草是影响作物生长的重要因素之一。杂草与作物竞争养分、水分和阳光&#xff0c;导致作物减产。传统的杂草识别和清除方法依赖于人工操作&#xff0c;效率低且成本高。随着计算机视觉和深度学习技术的发展&#xff0c;基于目…

从“思考-行动-观察“到“无限迭代“:AI Agent两大范式详解,收藏级学习资料

本文详细介绍AI Agent两大范式&#xff1a;ReAct的"思考-行动-观察"经典闭环和Ralph Loop的"无限自主迭代"新范式。ReAct适合短任务和动态规划&#xff0c;但存在上下文爆炸和过早停止问题&#xff1b;Ralph Loop通过强制持续迭代&#xff0c;解决长任务和…

腾讯HunyuanPortrait:单图生成超自然人像动画!

腾讯HunyuanPortrait&#xff1a;单图生成超自然人像动画&#xff01; 【免费下载链接】HunyuanPortrait 腾讯HunyuanPortrait是基于扩散模型的人像动画框架&#xff0c;通过预训练编码器分离身份与动作&#xff0c;将驱动视频的表情/姿态编码为控制信号&#xff0c;经注意力适…