带有英特尔 x86-32 位汇编的 gcc:访问 C 函数参数

gcc with intel x86-32 bit assembly : accessing C function arguments

我正在做操作系统实现工作。
首先是代码:

//generate software interrupt
void generate_interrupt(int n) {

    asm("mov al, byte ptr [n]");
    asm("mov byte ptr [genint+1], al");
    asm("jmp genint");
    asm("genint:");
    asm("int 0"); 
}

我在 gcc 中使用 -masm=intel 选项编译上面的代码。还, 这不是生成软件中断的完整代码。

我的问题是我收到错误 n undefined,我该如何解决,请帮忙?

而且它在link时提示错误,而不是在编译时提示错误,下面是图像

当您使用 GCC 时,您必须使用 GCC-style extended asm 来访问在 C 中声明的变量,即使您使用的是 Intel 汇编语法。将 C 变量名直接写入程序集插入的能力是 MSVC 的一个特性,GCC 没有复制它。

对于这样的构造,使用 单个 装配插入也很重要,而不是连续插入多个; GCC 可以并且将相对于周围代码重新排列汇编插入,包括相对于其他汇编插入,除非您采取特定步骤来阻止它。

应该写这个特定的结构

void generate_interrupt(unsigned char n)
{
    asm ("mov byte ptr [1f+1], %0\n\t"
         "jmp 1f\n"
         "1:\n\t"
         "int 0"
         : /* no outputs */ : "r" (n));
}

请注意,我已经删除了初始的 mov 和所有涉及 A 寄存器的坚持,而是告诉 GCC 使用 "r" 输入将 n 加载到任何方便的寄存器中约束。汇编插入中最好尽量少做,寄存器的选择尽量交给编译器

我也把n的类型改成了unsigned char来匹配INT指令的实际需求,我用的是1flocal label语法所以如果 generate_interrupt 是一个内联函数,这会正常工作。

说了这么多,我恳请您为您的操作系统找到一个不涉及自修改代码的实施策略。好吧,除非你打算 get a whole lot more use out of the self-modifications,无论如何。

这不是您关于将参数传递到内联程序集的特定问题的答案(请参阅@zwol 的回答)。这解决了为此特定任务不必要地使用自修改代码的问题。


如果中断号在编译时已知的宏方法

使用自修改代码的替代方法是创建一个 C 宏来生成您想要的特定中断。一个技巧是您需要一个将数字转换为字符串的宏。 Stringize 宏非常常见并记录在 GCC documentation.

您可以创建一个如下所示的宏 GENERATE_INTERRUPT

#define STRINGIZE_INTERNAL(s) #s
#define STRINGIZE(s) STRINGIZE_INTERNAL(s)

#define GENERATE_INTERRUPT(n) asm ("int " STRINGIZE(n));

STRINGIZE 将获取一个数值并将其转换为字符串。 GENERATE_INTERRUPT 简单地获取数字,将其转换为字符串并将其附加到 INT 指令的末尾。

你这样使用它:

GENERATE_INTERRUPT(0);
GENERATE_INTERRUPT(3);
GENERATE_INTERRUPT(255);

生成的指令应如下所示:

int    0x0
int3
int    0xff

如果中断号仅在运行时间

已知,则跳转Table方法

如果您需要调用仅在 运行 时已知的中断,那么可以创建 table 中断调用(使用 int 指令),然后是 ret. generate_interrupt 然后会简单地从堆栈中检索中断编号,计算 table 中可以找到特定 int 的位置,然后 jmp 到它。

在下面的代码中,我让 GNU 汇编器生成 256 个中断调用的 table,每个中断调用后跟一个 ret 使用 .rept directive。每个代码片段适合 4 个字节。结果代码生成和 generate_interrupt 函数可能如下所示:

/* We use GNU assembly to create a table of interrupt calls followed by a ret
 * using the .rept directive. 256 entries (0 to 255) are generated.
 * generate_interrupt is a simple function that takes the interrupt number
 * as a parameter, computes the offset in the interrupt table and jumps to it.
 * The specific interrupted needed will be called followed by a RET to return
 * back from the function */

extern void generate_interrupt(unsigned char int_no);
asm (".pushsection .text\n\t"

     /* Generate the table of interrupt calls */
     ".align 4\n"
     "int_jmp_table:\n\t"
     "intno=0\n\t"
     ".rept 256\n\t"
         "\tint intno\n\t"
         "\tret\n\t"
         "\t.align 4\n\t"
         "\tintno=intno+1\n\t"
     ".endr\n\t"

     /* generate_interrupt function */
     ".global generate_interrupt\n"  /* Give this function global visibility */
     "generate_interrupt:\n\t"
#ifdef __x86_64__
     "movzx edi, dil\n\t"              /* Zero extend int_no (in DIL) across RDI */
     "lea rax, int_jmp_table[rip]\n\t" /* Get base of interrupt jmp table */
     "lea rax, [rax+rdi*4]\n\t"        /* Add table base to offset = jmp address */
     "jmp rax\n\t"                     /* Do sepcified interrupt */
#else
     "movzx eax, byte ptr 4[esp]\n\t"    /* Get Zero extend int_no (arg1 on stack) */
     "lea eax, int_jmp_table[eax*4]\n\t" /* Compute jump address */
     "jmp eax\n\t"                       /* Do specified interrupt */
#endif
     ".popsection");

int main()
{
    generate_interrupt (0);
    generate_interrupt (3);
    generate_interrupt (255);
}

如果您查看目标文件中生成的代码,您会发现中断调用 table (int_jmp_table) 看起来类似于:

00000000 <int_jmp_table>:
   0:   cd 00                   int    0x0
   2:   c3                      ret
   3:   90                      nop
   4:   cd 01                   int    0x1
   6:   c3                      ret
   7:   90                      nop
   8:   cd 02                   int    0x2
   a:   c3                      ret
   b:   90                      nop
   c:   cc                      int3
   d:   c3                      ret
   e:   66 90                   xchg   ax,ax
  10:   cd 04                   int    0x4
  12:   c3                      ret
  13:   90                      nop

  ...
  [snip]

因为我使用 .align 4 每个条目都被填充到 4 个字节。这使得 jmp 的地址计算更容易。