Vvvvvoid 发表于 2022-6-24 21:03

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 下 关闭编译优化后的 汇编代码差异 还是挺大的 ( 左为开启优化, 右为关闭):


erui 发表于 2022-6-24 22:25

谢谢楼主分享,好长时间没关注GCC了,现在最新版本都到12.1了。

hjw01 发表于 2022-6-25 10:03

包括c跨平台?    很不错哦谢谢

Vvvvvoid 发表于 2022-6-25 11:23

hjw01 发表于 2022-6-25 10:03
包括c跨平台?    很不错哦谢谢

准确的说 , 是 C++

piazini 发表于 2023-1-30 12:24

《++ 跨平台 内联汇编集成 (MacOS,Linux,Windows)》很不错,留个脚印。

PPYLL 发表于 2023-7-28 19:07

感谢分享!

nanaqilin 发表于 2023-7-29 11:59

我都忘记了,那个传参和返回值是通过哪个寄存器来着

Vvvvvoid 发表于 2023-7-29 14:46

nanaqilin 发表于 2023-7-29 11:59
我都忘记了,那个传参和返回值是通过哪个寄存器来着

我也就当时记的, 想再也是忘了 &#128516;

LOLAI666 发表于 2023-7-30 21:37

有没有会写LOL人工智能的开发
我日出卡量800张一起合作吃肉
页: [1]
查看完整版本: C++ 跨平台 内联汇编集成 (MacOS,Linux,Windows)