标签: calling-convention

在未命名的命名空间中定义的C回调函数?

我有一个使用C bison解析器的C++项目.C语法分析器使用函数指针的结构来调用函数,这些函数在生产被bison减少时创建正确的AST节点:

typedef void Node;
struct Actions {
  Node *(*newIntLit)(int val);
  Node *(*newAsgnExpr)(Node *left, Node *right);
  /* ... */
};
Run Code Online (Sandbox Code Playgroud)

现在,在项目的C++部分,我填写了这些指针

class AstNode {
  /* ... */
};
class IntLit : public AstNode { 
  /* ... */
};

extern "C" {
  Node *newIntLit(int val) {
    return (Node*)new IntLit(val);
  }

  /* ... */
}

Actions createActions() {
  Actions a;
  a.newIntLit = &newIntLit;
  /* ... */
  return a;
}
Run Code Online (Sandbox Code Playgroud)

现在我把它们放在其中的唯一原因extern "C"是因为我希望它们具有C调用约定.但最佳的是,我希望他们的名字仍然受到损害.它们永远不会从C代码中调用,因此名称重整不是问题.使它们受损会避免名称冲突,因为有些动作被称为error,并且C++回调函数具有丑陋的名称,如下所示只是为了避免与其他模块的名称冲突.

extern "C" {
  void uglyNameError(char const *str) …
Run Code Online (Sandbox Code Playgroud)

c++ calling-convention linkage

6
推荐指数
1
解决办法
1157
查看次数

如何将指向成员函数的指针传递给 C 函数?

可能的重复:
使用 C++ 类成员函数作为 C 回调函数

我正在使用 C 库 (winpcap) 编写面向对象的库。我需要传递网络数据包到达时调用的回调函数作为函数指针。我想将成员函数指针传递给 winpcap,以保持我的设计面向对象并允许不同的对象接收不同的数据包。然而,据我所知,成员函数有不同的调用约定,因此不能传递给 C 函数。有没有办法来解决这个问题。我对 boost::bind 的实验(除了反复试验之外,我几乎无法使用它)并没有结果。

有没有办法改变成员函数的调用约定?

这是我现在使用的回调函数的定义和实际传递给winpcap的

void pcapCallback( byte* param, const struct pcap_pkthdr* header, const byte* pkt_data );

pcap_loop( adhandle, 0, pcapCallback, NULL );
Run Code Online (Sandbox Code Playgroud)

pcap_loop 只采用函数的名称(目前在全局范围内)。这是函数指针参数(pcap_loop 的第三个参数)的定义。由于这是第三方代码,我无法真正改变它。我必须有一个可以采用这种形式的成员函数:

typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, const u_char *);
Run Code Online (Sandbox Code Playgroud)

据我了解,成员函数将使用 thiscall 而 c 函数指针需要一个 cdecl

c c++ function-pointers winpcap calling-convention

6
推荐指数
1
解决办法
9250
查看次数

从 C 语言在 64 位汇编函数中传递参数。哪个寄存器接收这些参数?

我想将参数从 C 传递给汇编函数。

在类 UNIX 系统上,前六个参数为 rdi、rsi、rdx、rcx、r8 和 r9。

在 Windows 上,前四个参数为 rcx、rdx、r8 和 r9。

现在,我的问题是:在 BIOS 或 DOS 编程级别上,哪些寄存器接收这些参数?如果参数数量超过6个,如何编写程序集来处理这些参数?

c assembly x86-64 calling-convention

6
推荐指数
1
解决办法
1万
查看次数

64位C++传递具有"不同"调用约定作为参数的函数会产生不明确的错误

我的目标是使用__cdecl和__stdcall调用约定轻松提取任意函数的原型.它在32位工作正常.唯一改变的是模板函数参数中的调用约定.

根据维基百科的说法: "在Windows环境中编译x64架构时(无论是使用Microsoft还是非Microsoft工具),只有一个调用约定 - 这里描述的那个,所以stdcall,thiscall,cdecl,fastcall等. ,现在都是一样的."

这打破了64位的代码.即使调用约定相同,将函数作为参数传递仍然需要使用正确的命名法.IE如果函数定义为__stdcall,则必须将其传递给接受__stdcall的包装器.即使__cdecl相同,您仍然必须将定义为__cdecl的函数传递给接受__cdecl的包装器.

以32位工作的示例:

template<typename T, typename... Args>
struct WrapperSTD { typedef T(__stdcall *Functor)(Args...); };

template<typename T, typename... Args>
struct WrapperC { typedef T(*Functor)(Args...); };

template<typename T, typename... Args>
WrapperSTD<T, Args...> wrap(T(__stdcall *func)(Args...)) {
    return WrapperSTD<T, Args...>{};
}
template<typename T, typename... Args>
WrapperC<T, Args...> wrap(T(*func)(Args...)) {
    return WrapperC<T, Args...>{};
}
Run Code Online (Sandbox Code Playgroud)

我的目标是能够运行,例如:

using MsgBoxProto = decltype(wrap(MessageBoxA))::Functor;
Run Code Online (Sandbox Code Playgroud)

这适用于32位.但是,由于__stdcall和__cdecl在x64中显然是相同的,因此它不能在64位中工作并引发错误,表示调用是不明确的.它还告诉我模板已经定义.直观地说,似乎我能够将带有__cdecl的函数传递给这个__stdcall函数,因为编译器认为它们是相同的.但是,这不起作用:

template<typename T, typename... Args>
struct WrapperFC { typedef T(__stdcall *Functor)(Args...); };

template<typename T, typename... Args>
WrapperFC<T, Args...> wrap(T(__stdcall …
Run Code Online (Sandbox Code Playgroud)

c++ 64-bit cdecl calling-convention stdcall

6
推荐指数
1
解决办法
870
查看次数

可变参数和 x64

va_arg\ va_start\ va_list\va_end宏在 x64 中如何工作?

i386 中的调用约定在堆栈上传递参数,因此宏只是增加一些指向堆栈基址的指针并转发它。然而,在 x64 中,所有参数都是通过寄存器传递的......那么那里会发生什么呢?被调用函数如何知道哪些寄存器用于传递参数以确保它不会破坏它们?

c x86-64 calling-convention variadic-macros

6
推荐指数
1
解决办法
3104
查看次数

经常使用r10和r11的可接受性

我最近做了很多x64汇编编程(在Linux上),用于与我的C/C++程序集成.

由于我主要关心效率,我喜欢尽可能少地使用不同的寄存器/存储器地址,以及尝试不创建任何堆栈帧或保留寄存器(每个周期计数).

根据cdecl r10和r11寄存器不保留,我希望在我的函数中使用它们作为临时变量,最好不要保留.它是否会导致任何编译器出现任何无法解决的问题/错误(到目前为止还没有遇到任何问题,但这是一个问题)?

assembly x86-64 abi cpu-registers calling-convention

6
推荐指数
2
解决办法
645
查看次数

为什么在 System V AMD64 ABI 中不使用 RAX 来传递参数?

我不明白不在 RAX 中传递参数有什么好处,因为返回值在 RAX 中,无论如何它都会被被调用者破坏。

有人可以解释一下吗?

x86-64 calling-convention

6
推荐指数
1
解决办法
773
查看次数

优化标签(空结构)函数参数的处理

在某些情况下,我们使用标签来区分函数。标签通常是一个空结构:

struct Tag { };
Run Code Online (Sandbox Code Playgroud)

假设我有一个使用此标签的函数:

void func(Tag, int a);
Run Code Online (Sandbox Code Playgroud)

现在,我们调用这个函数:

func(Tag(), 42);
Run Code Online (Sandbox Code Playgroud)

并检查生成的 x86-64 反汇编,godbolt

mov     edi, 42
jmp     func(Tag, int)            # TAILCALL
Run Code Online (Sandbox Code Playgroud)

没关系,标签得到了完全优化:没有为其分配寄存器/堆栈空间。

但是,如果我查看其他平台,就会发现该标签存在一些。

在 ARM 上,r0用作标签,并且它被归零(似乎没有必要):

mov     r1, #42
mov     r0, #0
b       func(Tag, int)
Run Code Online (Sandbox Code Playgroud)

对于 MSVC,ecx被用作标记,并且它是从堆栈中“初始化”的(同样,似乎没有必要):

movzx   ecx, BYTE PTR $T1[rsp]
mov     edx, 42                             ; 0000002aH
jmp     void func(Tag,int)                 ; func
Run Code Online (Sandbox Code Playgroud)

我的问题是:是否有一种标签技术在所有这些平台上都得到了同样的优化?


注意:我没有找到 SysV ABI 指定可以在参数传递时优化空类的地方...(甚至,Itanium C++ ABI说:“空类的传递与普通类没有什么不同”。)

c++ arm x86-64 abi calling-convention

6
推荐指数
1
解决办法
845
查看次数

PK/Linux 上的 RISC-V ecall 系统调用约定

在 RISC-V 伪内核 (pk) 或 Linux 下运行的程序中系统调用的调用约定是什么?

查看由 riscv-gnu-toolchain 生成的代码,规则似乎是:

  • 系统调用号被传入 a7
  • 系统调用参数被传递a0a5
  • 未使用的参数设置为 0
  • 返回值在 a0

是这个吗?

真的有必要将未使用的参数归零吗?

注册a6呢?这可以用于另一个 sycall 参数吗?

调用exit()系统调用的示例:

li    a0, 1               # argument that is used by the syscall
li    a1, 0               # unused arguments
li    a2, 0
li    a3, 0
li    a4, 0
li    a5, 0
li    a7, 93              # exit syscall number
Run Code Online (Sandbox Code Playgroud)

system-calls calling-convention riscv

6
推荐指数
1
解决办法
1898
查看次数

超大结构如何返回到堆栈上?

据说从函数返回过大的值(而不是返回指向 的指针)会在堆栈上产生不必要的复制我所说的“超大”是指无法放入返回寄存器的值。structstructstruct

不过,引用维基百科

当需要超大的结构返回时,另一个指向调用者提供的空间的指针将作为第一个参数添加到前面,将所有其他参数向右移动一个位置。

返回结构/类时,调用代码分配空间并通过堆栈上的隐藏参数传递指向该空间的指针。被调用函数将返回值写入该地址。

看起来至少在x86架构上,所讨论struct的问题是由被调用者直接写入调用者指定的内存中,那么为什么会有一个副本呢?返回超大的structs 真的会在堆栈上产生副本吗?

c x86 struct abi calling-convention

6
推荐指数
2
解决办法
1172
查看次数