3

我有这个简单的 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;
}

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

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”,因此更改解决了问题。

4

3 回答 3

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大写表示标签在外部可见。

于 2013-04-23T14:14:18.157 回答
4

据我从您的问题中可以看出,您s.o用 C 程序覆盖了来自汇编程序的目标文件。所以你不再有汇编程序了。

你可能应该写

生成s.o目标文件

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

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

 gcc test.c -c 

链接应用程序

 gcc s.o test.o -o testapp 

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

于 2013-04-23T14:28:58.023 回答
0

最好在 c 文件中声明 asm inline。这是我自己的代码中的一个示例:

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 保护您的变量。
于 2013-04-23T14:52:42.103 回答