通过手写汇编调用本机代码

2024-01-23

我正在尝试从托管程序集中调用本机函数。我已经在预编译库上完成了此操作,一切都很顺利。目前我正在建立自己的图书馆,但我无法完成这项工作。

本机 DLL 源代码如下:

#define DERM_SIMD_EXPORT        __declspec(dllexport)

#define DERM_SIMD_API           __cdecl

extern "C" {

    DERM_SIMD_EXPORT void DERM_SIMD_API Matrix4x4_Multiply_SSE(float *result, float *left, float *right);

}

void DERM_SIMD_API Matrix4x4_Multiply_SSE(float *result, float *left, float *right) {
    __asm {
       ....
    }
}

此后,我们将拥有加载库并从函数指针创建委托的托管代码。

public unsafe class Simd
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void MatrixMultiplyDelegate(float* result, float* left, float* right);

    public static MatrixMultiplyDelegate MatrixMultiply;

    public static void LoadSimdExtensions()
    {
        string assemblyPath = "Derm.Simd.dll";

        IntPtr address = GetProcAddress.GetAddress(assemblyPath, "Matrix4x4_Multiply_SSE");

        if (address != IntPtr.Zero) {
            MatrixMultiply = (MatrixMultiplyDelegate)Marshal.GetDelegateForFunctionPointer(address, typeof(MatrixMultiplyDelegate));
        }
    }
}

使用上面的源代码运行时没有错误(获取了函数指针,并且实际创建了委托。

当我调用委托时,问题就出现了:它被执行了(我也可以调试它!),但是在函数退出时,托管应用程序会引发一个系统执行引擎异常(当它没有异常退出时)。

实际的问题是函数的实现:它包含一个asm使用 SSE 指令阻止;如果我删除asm块,代码完美运行。

我怀疑我缺少一些注册表保存/恢复程序集,但我在这方面完全无知。

奇怪的是,如果我将调用约定更改为 __stdcall,调试版本“似乎”可以工作,而发布版本的行为就像使用了 __cdecl 调用约定。

(正因为我们在这里,你能澄清一下调用约定是否重要吗?)


好的,感谢大卫赫夫南我发现导致问题的错误指令如下:

 movups result[ 0], xmm4;
 movups result[16], xmm5;

movups指令将 16 个字节移动到(未对齐的)内存中。

该函数由以下代码调用:

 unsafe {
    float* prodFix = (float*)prod.MatrixBuffer.AlignedBuffer.ToPointer();
    float* m1Fix = (float*)m2.MatrixBuffer.AlignedBuffer.ToPointer();
    float* m2Fix = (float*)m1.MatrixBuffer.AlignedBuffer.ToPointer();

    if (Simd.Simd.MatrixMultiply == null) {
                    // ... unsafe C# code
    } else {
        Simd.Simd.MatrixMultiply(prodFix, m1Fix, m2Fix);
    }
}

Where 矩阵缓冲区是我的一个班级;它的成员对齐缓冲区按以下方式分配:

// Allocate unmanaged buffer
mUnmanagedBuffer = Marshal.AllocHGlobal(new IntPtr((long)(size + alignment - 1)));

// Align buffer pointer
long misalignment = mUnmanagedBuffer.ToInt64() % alignment;
if (misalignment != 0)
    mAlignedBuffer = new IntPtr(mUnmanagedBuffer.ToInt64() + misalignment);
else
    mAlignedBuffer = mUnmanagedBuffer;

也许该错误是由Marshal.AllocHGlobal or IntPtr黑魔法?


这是发现错误的最小来源:

void Matrix4x4_Multiply_SSE(float *result, float *left, float *right)
{
    __asm {
        movups xmm0,    right[ 0];

        movups result, xmm0;
    }
}


int main(int argc, char *argv[])
{
    float r0[16];
    float m1[16], m2[16];

    m1[ 0] = 1.0f; m1[ 4] = 0.0f; m1[ 8] = 0.0f; m1[12] = 0.0f;
    m1[ 1] = 0.0f; m1[ 5] = 1.0f; m1[ 9] = 0.0f; m1[13] = 0.0f;
    m1[ 2] = 0.0f; m1[ 6] = 0.0f; m1[10] = 1.0f; m1[14] = 0.0f;
    m1[ 3] = 0.0f; m1[ 7] = 0.0f; m1[11] = 0.0f; m1[15] = 1.0f;

    m2[ 0] = 1.0f; m2[ 4] = 0.0f; m2[ 8] = 0.0f; m2[12] = 0.0f;
    m2[ 1] = 0.0f; m2[ 5] = 1.0f; m2[ 9] = 0.0f; m2[13] = 0.0f;
    m2[ 2] = 0.0f; m2[ 6] = 0.0f; m2[10] = 1.0f; m2[14] = 0.0f;
    m2[ 3] = 0.0f; m2[ 7] = 0.0f; m2[11] = 0.0f; m2[15] = 1.0f;

    r0[ 0] = 0.0f; r0[ 4] = 0.0f; r0[ 8] = 0.0f; r0[12] = 0.0f;
    r0[ 1] = 0.0f; r0[ 5] = 0.0f; r0[ 9] = 0.0f; r0[13] = 0.0f;
    r0[ 2] = 0.0f; r0[ 6] = 0.0f; r0[10] = 0.0f; r0[14] = 0.0f;
    r0[ 3] = 0.0f; r0[ 7] = 0.0f; r0[11] = 0.0f; r0[15] = 0.0f;

    Matrix4x4_Multiply_SSE(r0, m1, m2);
    Matrix4x4_Multiply_SSE(r0, m1, m2);

    return (0);
}

几乎在第二次之后movups,堆栈改变了resultvalue(存储在堆栈上),并存储以下值xmm0修改后的(和错误的)地址存储在result.

从 *Matrix4x4_Multiply_SSE* 退出后,原始内存不会被修改。

我缺少什么?


对齐校正错误。你需要添加alignment-misalignment以纠正对齐。所以代码应该是这样的:

mAlignedBuffer = 
    new IntPtr(mUnmanagedBuffer.ToInt64() + alignment - misalignment);

但是,我建议您首先在本机设置中测试该功能。一旦您知道它在那里工作,您就可以转移到托管设置,并知道任何问题都是由托管代码引起的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过手写汇编调用本机代码 的相关文章

  • 如何在汇编语言中换行打印多个字符串

    我试图在汇编中的不同行上打印多个字符串 但使用我的代码 它只打印最后一个字符串 我对汇编语言非常陌生 所以请耐心等待 section text global start start mov edx len mov edx len1 mov
  • CALL指令是否总是将EIP指向的地址压入堆栈?

    x86架构中函数调用时是否存在返回地址不入栈的情况 No CALL根据定义 将在跳转到目标地址之前将返回地址压入堆栈 该返回地址是EIP or RIP sizeof call instruction 通常为 5 个字节 英特尔 64 和 I
  • 在 x86 Intel VT-X 非根模式下,是否可以在每个指令边界传递中断?

    除了不将中断传送到虚拟处理器的某些正常指定条件 cli if 0 等 之外 客户机中的所有指令实际上都是可中断的吗 也就是说 当传入的硬件中断先传递给 LAPIC 然后传递给处理器时 据说会发生一些内部魔法 将其转换为虚拟中断给来宾 使用虚
  • NASM 轮班操作员

    您将如何在寄存器上进行 NASM 中的位移位 我读了手册 它似乎只提到了这些操作员 gt gt lt lt 当我尝试使用它们时 NASM 抱怨移位运算符处理标量值 您能解释什么是标量值并举例说明如何使用 gt gt and lt lt 另外
  • 近调用/跳转表并不总是在引导加载程序中工作

    一般问题 我一直在开发一个简单的引导加载程序 并在某些环境中偶然发现了一个问题 在这些环境中 此类指令不起作用 mov si call tbl SI Call table pointer call call tbl Call print c
  • 两个基本的 ANTLR 问题

    我正在尝试使用 ANTLR 来获取简单的语法并生成汇编输出 我在 ANTLR 中选择的语言是 Python 许多教程看起来非常复杂或详细阐述与我无关的事情 我真的只需要一些非常简单的功能 所以我有两个问题 将值从一个规则 返回 到另一规则
  • 用于预乘 ARGB 的 SSE alpha 混合

    我正在尝试编写一个支持 SSE 的 alpha 合成器 这就是我想出的 首先 混合两个 4 像素向量的代码 alpha blend two 128 bit 16 byte SSE vectors containing 4 pre multi
  • Nasm 打印到下一行

    我用 nasm Assembly 编写了以下程序 section text global start start Input variables mov edx inLen mov ecx inMsg mov ebx 1 mov eax 4
  • 有没有办法使用 i387 fsqrt 指令获得正确的舍入?

    有没有办法使用 i387 fsqrt 指令获得正确的舍入 除了改变精确模式在 x87 控制字中 我知道这是可能的 但这不是一个合理的解决方案 因为它存在令人讨厌的重入型问题 如果 sqrt 操作中断 精度模式将出错 我正在处理的问题如下 x
  • 如何知道寄存器是否是“通用寄存器”?

    我试图了解寄存器必须具备什么标准才能被称为 通用寄存器 我相信通用寄存器是一个可以用于任何用途的寄存器 用于计算 将数据移入 移出等 并且是一个没有特殊用途的寄存器 现在我读到了ESP寄存器是通用寄存器 我猜是ESP寄存器可以用于任何事情
  • 大会,你好世界问题

    我正在 Linux 上学习 asm noobuntu 10 04 我得到了以下代码 http asm sourceforge net intro hello html http asm sourceforge net intro hello
  • “mov (%ebx,%eax,4),%eax”如何工作? [复制]

    这个问题在这里已经有答案了 一直在从事装配作业 并且在很大程度上我对装配非常了解 或者至少对于这项任务来说足够好 但这个 mov 的声明让我很困惑 如果有人能解释这个 mov 语句如何操作寄存器值 我将非常感激 mov ebx eax 4
  • 阴影空间示例

    EDIT 我接受了下面的答案 并添加了我自己的代码的最终修订版 希望它向人们展示影子空间分配的实际示例 而不是更多的文字 编辑 2 我还设法在 YouTube 视频 所有内容 的注释中找到了一个调用约定 PDF 的链接 其中有一些关于 Li
  • 如何将 x86 GCC 风格的 C 内联汇编转换为 Rust 内联汇编?

    我在 C 中有以下内联汇编 unsigned long long result asm volatile byte 15 byte 49 shlq 32 rdx orq rdx rax a result rdx return result
  • AVX-512 指令编码 - {er} 含义

    在 Intel x86 指令集参考中 有许多 AVX 512 指令在指令中具有可选的 er 例如 VADDPD 的一种形式定义为 EVEX NDS 512 66 0F W1 58 r VADDPD zmm1 k1 z zmm2 zmm3 m
  • 设置 IRQ 映射

    我正在遵循一些教程和参考文献来尝试设置我的内核 我在教程中遇到了一些不熟悉的代码 但根本没有解释它 这是我被告知映射的代码16 IRQs 0 15 到 ISR 地点32 47 void irq remap void outportb 0x2
  • X86 预取优化:“计算 goto”线程代码

    我有一个相当重要的问题 我的计算图有循环和多个 计算路径 我没有制作一个调度程序循环 其中每个顶点将被一一调用 而是将所有预先分配的 框架对象 放置在堆中 代码 数据 这有点类似于线程代码 甚至更好 CPS 只是在堆中跳转 执行代码 每个代
  • 为什么 clang 使用 -O0 生成低效的 asm(对于这个简单的浮点和)?

    我正在 llvm clang Apple LLVM 版本 8 0 0 clang 800 0 42 1 上反汇编此代码 int main float a 0 151234 float b 0 2 float c a b printf f c
  • 这种没有推送寄存器的交换有多安全?

    我对汇编非常陌生 下面的代码应该通过两个不同的函数交换两个整数 首先使用swap c然后使用swap asm 但我怀疑 我是否需要push 我的意思是保存 汇编代码之前寄存器的每个值和pop稍后 就在返回之前 main 换句话说 如果我返回
  • 是否可以在VM内使用VMX CPU指令?

    VM guest 内部的进程是否有可能使用 VMX AMD V VT x CPU 指令 然后由外部 VMM 处理而不是直接在 CPU 上处理 Edit 假设外部VM使用VMX本身来管理其虚拟客户机 即它在Ring 1中运行 如果可能的话 是

随机推荐

  • 如何在 jupyter markdown 单元格中创建新行

    md S a set of shops I a set of items M wants to get 我想在这两句话之间换一条新线 我们通常在第一个句子之后在新行之前放置 空格 但它在 Jupyter 中不起作用 我怎样才能做到这一点 我
  • 没有名为“requests”的模块 Python 3.5.0

    我需要使用requests在我的代码中 但它说它尚未安装 我收到以下错误 No module named requests 它实际上已在 python 2 7 中安装并运行 Requirement already satisfied use
  • 无法使用存储过程 pyodbc SQL SERVER 创建数据库

    我正在尝试调用一个从 pyodbc 创建数据库的存储过程 以下是代码的最小示例 import pyodbc conn pyodbc connect Driver SQL Server Server SERVERNAME Trusted Co
  • Tomcat 正在运行吗?

    有兴趣了解人们通常如何检查 Tomcat 是否在 Unix 环境上运行 我要么使用以下命令检查进程是否正在运行 ps ef grep java ps ef grep logging 或者我检查端口号是否处于活动状态 netstat a gr
  • 在 MAX(DATE) 上左连接

    我有 2 个表 交易 a 和价格 b 我想从表 b 中检索交易日期有效的价格 表a包含物品交易的历史记录 商店类型 日期 文章 表 b 包含商品价格的历史记录 商店类型 日期 文章 价格 目前我有这个 Select a Store type
  • opencv 更新单应性矩阵以适合双倍大小的图像

    我正在使用光流进行视频稳定 为了使 calcOpticalFlowPyrLK 更快地工作 我将原始图像缩小了 2 倍并在其上运行该函数 如何修改单应矩阵 通过 findHomography 检索 以便能够扭曲透视原始的较大图像 这有点晚了
  • 如何重置/初始化变量?

    我遇到了变量问题 首先看到这段代码 然后我会解释我的问题 if pygame Rect colliderect hammer rect mole rect random locations 100 440 350 440 600 440 1
  • 重新采样环回捕获

    我使用以下代码成功从 Wasapi 捕获声音 IWaveIn waveIn new WasapiLoopbackCapture waveIn DataAvailable OnDataReceivedFromWaveOut 我现在需要做的是将
  • 对于具有动态标头的大部分静态页面来说,最佳的 Rails 缓存选项是什么

    我有一组主要是静态的页面 除了它们的布局包含更加动态的标题之外 我很乐意将其页面缓存相对较长的时间 到目前为止 最有前途的想法似乎是使用不带布局的操作缓存 class SomethingController lt ApplicationCo
  • iOS 脸书集成

    我正在开发简单的 Facebook 集成 允许用户将高分发布到他们的墙上 doodlejump 处理这个问题的方式是我的灵感来源 我可以使用它 但它不是很干净 我不知道该怎么做 我按照以下说明进行操作http developers face
  • 在Python中从目录(有大量文件)中选择随机文件

    我有一个包含大量文件 约 100 万 的目录 我需要从此目录中选择一个随机文件 由于文件太多 os listdir自然需要永恒的时间才能完成 有什么办法可以绕过这个问题吗 也许以某种方式了解目录中的文件数量 不列出它 并选择随机生成 n 的
  • 在项目中包含 image_picker 时出错(Flutter)

    为什么当我执行项目时不断出现此错误 一旦我将 image picker 放入我的 pubspec 中 就会发生这种情况 失败 构建失败并出现异常 什么地方出了错 任务 app processDebugResources 执行失败 Andro
  • R group by 和aggregate - 使用 plyr 返回组内的相对排名

    更新 我有一个数据框 测试 如下所示 session id seller feedback score 1 1 282470 2 1 275258 3 1 275258 4 1 275258 5 1 37831 6 1 282470 7 1
  • R 热图,Y 轴上的标签非常接近

    我正在使用 R 绘制 CSV 文件中数据的热图 10 列 条件和 1000 罗瓦 以下是我正在使用的代码 nba lt read csv 1317754115 csv sep nba matrix lt data matrix nba ce
  • 表动态加载SAPUI5/UI5

    我想在 SAPUI5 Table 组件中显示大量数据 我曾经通过动态加载来实现这些数据表 这意味着该表最初加载了约 50 条记录 用户向下滚动足够远后 下一组 50 条记录将加载到表中 这样我就可以显示包含超过 160 000 个条目的表格
  • 如何调试在客户计算机上崩溃的 Windows 应用商店应用程序?

    我收到一位客户的支持电子邮件 说他的应用程序在启动时崩溃 他收到的只是一条类似以下的消息 应用程序名称 遇到问题您可以向 Microsoft 发送有关以下内容的信息 出了什么问题来帮助改进这个应用程序 将发送给 Microsoft 的文件
  • Android Studio 禁用换行

    我在 Android Studio 上有这个奇怪的换行符 我想禁用它 因为它让我害怕 It s also available under the context menu
  • 放弃对 JRE 1.3 的支持

    我们提供了一个流行的开源 Java FTP 库 称为edtFTPj http www enterprisedt com products edtftpj overview html 我们希望放弃对 JRE 1 3 的支持 这将清理代码库 并
  • 将 Python 字典列表转换为 Postgresql json 数组

    我正在尝试将 jsonb 元素的 Python 2 7 列表插入到具有数据类型列的 Postgresql 9 4 表中 jsonb 这是一些代码 import json anArray name Joe age 51 yob 1964 ge
  • 通过手写汇编调用本机代码

    我正在尝试从托管程序集中调用本机函数 我已经在预编译库上完成了此操作 一切都很顺利 目前我正在建立自己的图书馆 但我无法完成这项工作 本机 DLL 源代码如下 define DERM SIMD EXPORT declspec dllexpo