从C源代码调用程序集例程

时间:2013-04-23 14:07:41

标签: c assembly

我有这个简单的C源代码:

#include <stdio.h>

extern int Sum(int,int);

int main()
{
  int a,b,s;
  a=1 , b=2;
  s = Sum(a,b);
  return 0;
}

我有这个s.asm定义函数_Sum:

global _Sum

     _Sum:

        push    ebp             ; create stack frame
        mov     ebp, esp
        mov     eax, [ebp+8]    ; grab the first argument
        mov     ecx, [ebp+12]   ; grab the second argument
        add     eax, ecx        ; sum the arguments
        pop     ebp             ; restore the base pointer
        ret

现在,我使用:

编译了.asm
nasm s.asm -f elf -o s.o

使用以下方法编译和链接.c文件:

gcc s.o test.o -o testapp

这是结果:

/tmp/ccpwYHDQ.o: In function `main':
test.c:(.text+0x29): undefined reference to `Sum'
collect2: ld returned 1 exit status

那么问题是什么?

我正在使用Ubuntu-Linux

非常感谢任何帮助,谢谢

[求助]:我用nm检查了test.o文件,它预计会找到符号'Sum'而不是'_Sum',所以改变它就解决了这个问题。

3 个答案:

答案 0 :(得分:7)

在典型的汇编程序中,标签默认为本地标签。要告诉汇编程序使它们对外部例程可见,您必须添加一个声明,例如:

.globl _Sum

此外,在C中正确声明例程。这不是导致链接错误的原因,但可能导致其他问题:

extern int Sum(int, int);

为了完整性,感谢评论者:不要覆盖您的目标文件。您可以汇编,编译和链接:

nasm s.asm -f elf -o s.o
gcc test.c s.o -o test

(这命名可执行文件“test”,您可能必须使用“./test”执行它以区分目录中的“test”和“test”命令。您可能更乐意选择其他名称。 )

出于教育目的:如果您的系统上有nm工具,请执行命令nm s.o。它可能会显示如下:

00000000 t _Sum

t表示_Sum是代码部分中的本地标签。 (代码部分也称为文本部分,因此也称为t。)添加.globl声明并汇总新源后,nm s.o应显示大写T。大写表示标签在外部可见。

答案 1 :(得分:4)

据我所知,你可以用C程序覆盖来自汇编程序s.o的目标文件。所以你不再拥有汇编程序了。

你应该写

生成s.o目标文件

 nasm s.asm -f elf -o s.o

生成test.o(您的命令创建了另一个s.o)

 gcc test.c -c 

关联应用

 gcc s.o test.o -o testapp 

(我选择testapp作为输出二进制文件,因为test通常是一个非常糟糕的程序名称,它与Unix命令test碰撞

答案 2 :(得分:0)

最好在c文件中声明asm内联。以下是我自己的代码中的示例:

bool KxMutex::tryLock_i()
{
#ifdef KX_MUTEX_ASM
   int oldLock;
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
   asm volatile (
     "movl $1,%%eax\n\t"
     "xchg %%eax,%0\n\t"
     "movl %%eax,%1\n\t"
     : "=m" (mLock), "=m" (oldLock)
     :
     : "%eax", "memory"
   );
#elif defined(__GNUC__) && (defined(__ppc__))
   int newLock = 1;
   asm volatile (
     "\n1:\n\t"
     "lwarx  %0,0,%1\n\t"
     "cmpwi  0,%0,0\n\t"
     "bne-   2f\n\t"
     "stwcx. %2,0,%1\n\t"
     "bne-   1b\n\t"
     "isync\n"
     "2:\n\t"
     : "=&r" (oldLock)
     : "r" (&mLock), "r" (newLock)
     : "cr0", "memory"
  );
#endif
   return ( oldLock == 0 );
#else // !KX_MUTEX_ASM
   return ( pthread_mutex_trylock( (pthread_mutex_t*)this ) ? false : true );
#endif // !KX_MUTEX_ASM
}

有许多优点:

  1. 您不必自己管理堆栈框架,返回值等。
  2. 编译器可以在必要时内联函数,因为它控制调用约定
  3. 您可以直接在.asm代码中引用c语言符号
  4. 对于使用相同c宏和定义控制的不同平台,可以更容易地使用不同版本的ASM。
  5. 所有c和c ++函数修饰符都可以工作 - extern,static,inline等。
  6. 编译器仍然可以对函数参数进行类型检查,检查函数是否正确调用等。
  7. 您可以根据需要使用const保护变量。