C++ 跨平台 内联汇编集成 (MacOS,Linux,Windows)
本帖最后由 Vvvvvoid 于 2022-6-25 11:19 编辑## 前记
原本是想了解在 mac 下开发 C++ 内联汇编 与 Windows 下的区别,
后来发现我的 Mac (x86) 架构 与 公司的 Mac m1(arm架构) 在汇编这块是有很大的差别的, 索性 arm把 也简单了解了下.
后来, 发现 用 cmake 可以做到跨平台编译, 索性又把 windows的写法也加进来了...
## ENV
```shell
# cmake (VERSION >=3.22)
yum install -y cmake
# gcc/g++
yum install -y gcc-c++
```
## Settings
### Windows
Windows 下依赖 Visual Studio, 用 VS 启动项目时,要自己配置下 cmake, 因为vs集成的cmake版本有点低
Visual Studio (Configuring the cmake environment)
### MacOS
Mac 用 Xcode 开发的话, Release 模式要设置下Optimization Level ,否则手写 asm, 编译器开启自动优化会有意想不到的异常发生. (大神忽略)
Debug 模式下 是默认关闭 自动优化的, 所以Debug模式可以忽略
Xcode (Build Settings > Optimization Level > Release > None[-O0])
建议Mac 用 Clion 开发...
## Code
### C++ 定义变量/函数
- 首先用 C++ 代码,定义了一个方法 fun_dev
- 然后 定义了四个变量 , a, b, result_ra , result_rc
```cpp
void fun_dev() {
printf("this is fun_dev\r\n");
}
```
```cpp
uintptr_t a = 10;
uintptr_t b = 20;
uintptr_t result_ra = 0;
uintptr_t result_rc = 0;
```
### inlie_asm 实现
- 实现1: 计算 a+b, 并且 将结果给 result_ra , 同时讲 0x20 赋值给 result_rc
- 实现2: 汇编代码 执行 上面定义的 函数 fun_dev
#### Arm架构
```
#if defined__aarch64__
// 32 位为 寄存器为 r1-rn, 64为x1-xn
// armv8 , 要给寄存器赋值一个64位的数 居然是这种写法吗?
// __asm__ __volatile__ (
// "mov x1, #0x9012\n" // long = 0x123456789012 (16) / 20015998341138 (10);
// "movk x1, #0x5678, lsl #16\n"
// "movk x1, #0x1234, lsl #32\n"
// "mov %,x1\n"
// :"=r"(tmp)
// :
// :"cc","memory"
// );
std::cout << "this is arm cpu" << std::endl;
__asm__ __volatile__ (
"add %, %, %\n\t"
"mov %, #0x20\n\t"
:"=r"(result_ra),"=r"(result_rc)
:"r"(a), "r"(b)
:"cc","memory"
);
__asm__ __volatile__ (
"blr %\n\t"
:
:"r"(fun_dev)
:"cc","memory"
);
#endif
```
#### __x86_64__架构
```
#if defined__x86_64__
// 这个起码寄存器我都认识...
// __asm__ __volatile__ (
// "mov $0x1234,%%rax\n\t"
// );
std::cout << "this is x86 cpu" << std::endl;
__asm__ __volatile__(
"add %,%\n\t"
"movq %, %\n\t"
"movq $0x20, %\n\t"
:"=a"(result_ra),"=c"(result_rc) // result_ra 赋值给 rax,result_rc 赋值给 rcx
: "a"(a), "b"(b) // value_a 赋值给 rax , value_b 赋值给 rbx
:"cc","memory"
);
__asm__ __volatile__ (
"callq *%\n\t" // 在内联汇编语句中使用寄存器 eax 时,寄存器名前应该加两个 ‘%’,即 %%eax
:
:"a"(fun_dev) // mov fun_dev_addr,rax; call rax; // :"%eax"
:"cc","memory"
);
#endif
```
#### Win x64/x32
> Win64 需要单独添加**一个 asm文件**,并导入函数
```cpp
#if defined _WIN32
#if defined(_M_X64)
EXTERN_C ULONG64 asm_win64_add(ULONG64 u1, ULONG64 u2,DWORD_PTR u3);
EXTERN_C ULONG64 asm_win64_call(DWORD_PTR u1);
#endif
#endif
```
> win64.asm
```asm
.data
.CODE;
vt_Add PROC;
add rcx,rdx ;
mov rax,rcx ;
ret ;
vt_Add ENDP;
asm_win64_add PROC;
push rbp
sub rsp,30h ; params is rcx,rdx,r8,r9,rsp+20,rsp+28..
mov qword ptr ,r8
mov qword ptr ,rdx
mov qword ptr ,rcx
add rcx,rdx
mov rax,rcx
mov qword ptr,20h
add rsp,30h
pop rbp
ret
asm_win64_add ENDP;
asm_win64_call PROC;
push rbp
sub rsp,30h ;
mov qword ptr ,rcx
call qword ptr
add rsp,30h
pop rbp
ret
asm_win64_call ENDP;
END ;
```
```cpp
#if defined _WIN32
#if defined(_M_X64)
std::cout << "this is _WIN64 " << std::endl;
result_ra = asm_win64_add(a,b, (DWORD_PTR)&result_rc);
asm_win64_call((DWORD_PTR)&fun_dev);
#else
std::cout << "this is _WIN32 " << std::endl;
__asm {
mov eax, a;
mov ebx, b;
add eax, ebx;
mov result_ra, eax;
mov result_rc, 0x20;
}
__asm {
call fun_dev;
}
#endif
#endif
```
### 执行输出
```cpp
printf("asm result (%lu + %lu ) => %lu ?, (0x20) => %lu ?\r\n",
a,b,
result_ra,
result_rc
);
```
```
# fun_dev addr ->0x1009cc120// 函数地址
# this is x86 cpu // 这一行 会随着操作系统而变动,后面的执行结果都一样
# this is fun_dev // asm 执行 call 打印记录
# asm result (10 + 20 ) => 30 ?, (0x20) => 32 ?
```
## Build&Run
### Linux/Mac/Unix
```shell
rm -rf build/*
cmake . -B build
make -C build
./build/bin_asm_cpp
```
### Windows
```bat
### 构建前,请先清除 build 的cache目录
rmdir /s /q build
### win64
cmake -A x64 -B build .
cmake --build ./build --config Release
### win32
cmake -A Win32 -B build .
cmake --build ./build --config Release
### 然后自己去 build/Release 目录下找 exe 执行程序吧
### 因为是 控制台程序, 所以得在 cmd下执行, 才能看到效果..
```
### MakeFile
> win64 依赖一个 .asm 的文件, 所以这里只需要判断 win64 下 开启 **ASM_MASM** 既可 , 别的地方都一样
```cpp
cmake_minimum_required(VERSION 3.22)
project(asm_cpp)
set(CMAKE_CXX_STANDARD 14)
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(CURRENT_OS "Linux")
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(CURRENT_OS "Windows")
elseif(CMAKE_SYSTEM_NAME MATCHES "Apple")
set(CURRENT_OS "Apple")
endif()
if(CMAKE_CL_64)
set(CURRENT_BIT "x64")
else(CMAKE_CL_64)
set(CURRENT_BIT "x86")
endif(CMAKE_CL_64)
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "aarch64")
set(CURRENT_ARCH "aarch64")
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64")
set(CURRENT_ARCH "x86_64")
elseif (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "AMD64")
set(CURRENT_ARCH "AMD64")
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
if(CMAKE_CL_64)
enable_language(ASM_MASM)
message("enable_language(ASM_MASM)")
endif()
endif()
message(STATUS "Current OS is ${CURRENT_OS}/${CURRENT_ARCH}/${CURRENT_BIT}")
include_directories(asm_cpp)
add_executable(bin_asm_cpp
asm_cpp/win64.asm
asm_cpp/httplib.h
asm_cpp/main.cpp)
```
## Supported Platform
| Platform | 32 | 64 |
|---------------------|----|----|
| MacOS(__x86_64__) | ❌ | ✅ |
| MacOS(Arm) | ❌ | ✅ |
| Linux(Arm) | ❌ | ✅ |
| Linux(__x86_64__) | ❌ | ✅ |
| Windowx(__x86_64__) | ✅ | ✅ |
## 完整源码
Gayhub : https://github.com/marlkiller/asm_cpp.git
## 后记
汇编菜鸟一枚, 请提宝贵意见, 勿喷~
See Also:
- https://gcc.gnu.org/
- http://ftp.gnu.org/gnu/gcc/gcc-11.2.0/
- https://cmake.org/download/
- https://github.com/Kitware/CMake/releases/tag/v3.24.0-rc1
最后贴个图: xcode 下 关闭编译优化后的 汇编代码差异 还是挺大的 ( 左为开启优化, 右为关闭):
谢谢楼主分享,好长时间没关注GCC了,现在最新版本都到12.1了。 包括c跨平台? 很不错哦谢谢 hjw01 发表于 2022-6-25 10:03
包括c跨平台? 很不错哦谢谢
准确的说 , 是 C++ 《++ 跨平台 内联汇编集成 (MacOS,Linux,Windows)》很不错,留个脚印。 感谢分享! 我都忘记了,那个传参和返回值是通过哪个寄存器来着 nanaqilin 发表于 2023-7-29 11:59
我都忘记了,那个传参和返回值是通过哪个寄存器来着
我也就当时记的, 想再也是忘了 😄 有没有会写LOL人工智能的开发
我日出卡量800张一起合作吃肉
页:
[1]