3

I have this simple C source code :

#include <stdio.h>

extern int Sum(int,int);

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

and i have this s.asm which defines the function _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

now , i compiled the .asm using :

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

and compiled and linked the .c file using :

gcc s.o test.o -o testapp

this is the outcome :

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

So what is the problem ?

I'm using Ubuntu-Linux

Any help would be greatly appreciated , Thanks

[SOLVED] : i checked with nm the test.o file and it expected to find the symbol 'Sum' not '_Sum' so changing that solved the problem.

7
  • 2
    Just off of the top of my head, your prototype for sum is incorrect. extern int Sum(int,int);
    – RageD
    Commented Apr 23, 2013 at 14:10
  • 2
    You probably need to put a global _Sum somewhere in the asm side
    – user555045
    Commented Apr 23, 2013 at 14:12
  • i tried global _Sum instead of just _Sum , i got the same error message @harold
    – hshihab
    Commented Apr 23, 2013 at 14:15
  • i also changed the prototype to extern int Sum(int,int); instead of the old one , and i got the same error message @RageD
    – hshihab
    Commented Apr 23, 2013 at 14:16
  • 2
    Isn't the "-o" option to gcc for specifying the output file ? In that case compilation of test.c might be overwriting the s.o file ?
    – woodleg.as
    Commented Apr 23, 2013 at 14:16

3 Answers 3

8

In typical assemblers, labels are by default local. To tell the assembler to make them visible to external routines, you must add a declaration, such as:

.globl _Sum

Additionally, declare the routine correctly in C. This is not the cause of your link error but can cause other problems:

extern int Sum(int, int);

For completeness, with thanks to the commenters: Do not overwrite your object files. You can assemble, compile, and link with:

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

(This names the executable “test”, and you will probably have to execute it with “./test” to distinguish the “test” in your directory from the “test” command. You may be happier choosing another name.)

For educational purposes: If you have the nm tool on your system, execute the command nm s.o. It may show you something like:

00000000 t _Sum

The t means that _Sum is a local label in the code section. (The code section is also called the text section, hence the t.) Once you add the .globl declaration and assemble the new source, nm s.o should show you an uppercase T instead. Uppercase indicates the label is externally visible.

7
  • @hshihab: The syntax depends on your assembler. I would try “.globl” and “.global”, then refer to the documentation. Commented Apr 23, 2013 at 14:51
  • @hshihab: Programs like this are generally not installed in the system. They are more commonly executed by typing their path as the command name. If your current directory contains the executable file, you can use “./name” as the command. Commented Apr 23, 2013 at 14:52
  • Great! .. i just tried nm ./s.o and it produced 00000000 T _Sum. so now i can be sure that my function is declared as global. why am i still getting the error message though ?
    – hshihab
    Commented Apr 23, 2013 at 14:57
  • @hshihab: Have you modified the “gcc” command line to avoid overwriting s.o? Commented Apr 23, 2013 at 15:03
  • i edited my command lines and the .asm file in the post to reflect the new changes.
    – hshihab
    Commented Apr 23, 2013 at 15:10
4

As far as I can see from your question, you overwrite your object file that came from the assembler s.o by the C program. So you don't have the assembler routine any more.

You should probably write

Generate the s.o object file

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

Generate the test.o (your command created another s.o)

 gcc test.c -c 

Link the app

 gcc s.o test.o -o testapp 

(I chose testapp as output binary because test is often a very bad name for a program, it collides with the Unix command test)

3
  • ok, i made the two changes that were mentioned above (the prototype change and the global declaration) now my first 2 lines in the .asm files is like this global _Sum _Sum: and i compiled and linked as u suggested , it still generates the same error
    – hshihab
    Commented Apr 23, 2013 at 14:47
  • Are you sure you need to prepend the identifier with a an underscore? I haven't done assembler for a long time but I remember that it was not always the case and that it depended on defaults of the environment or parameter. You can check with nm what the symbol the C code expects really looks like. Commented Apr 23, 2013 at 14:56
  • i just checked it with nm and it produced 00000000 T _Sum
    – hshihab
    Commented Apr 23, 2013 at 14:59
0

It is better to declare the asm inline in c files. Here is an example from my own code:

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
}

There are many advantages:

  1. You don't have to manage the stack frame, return values etc yourself.
  2. The compiler can inline the function when necessary, since it controls the calling convention
  3. You can reference c language symbols directly in the .asm code
  4. It is easier to have different versions of ASM for different platforms controlled from the same c macros and definitions.
  5. All the c and c++ function modifiers work - extern, static, inline etc.
  6. The compiler can still do type checking on your function arguments, check that the function is called correctly etc.
  7. You can protect your variables with const as needed.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.