I am freshman, I want to learn about ARM assembly language and using gnu toolchain so I decided to start with small project that mixes arm-assembly file and C file by gnu toolchain. My idea is calling a function that is defined in assembly file.
multi.S:
.globl multi
multi:
str fp,[sp,#-4]!
add fp,sp,#0
sub sp,sp,#12
str r0,[fp,#-8]
str r1,[fp,#-12]
ldr r3,[fp,#-8]
ldr r2,[fp,#-12]
mul r1,r2,r3
mov r3,r1
mov r0,r3
add sp,fp,#0
ldr fp,[sp],#4
bx lr
multi.c:
#include <stdio.h>
unsigned int multi(unsigned int a, unsigned int b);
int main(int argc, char *argv[]){
unsigned int x = multi(3,4);
printf("%u\n",x);
return 0;
}
Then I tried to link them together by using command as follows:
arm-none-eabi-gcc -g -c -o multi-arm.o multi.S
arm-none-eabi-gcc -g -c -o multi.o multi.c
arm-none-eabi-ld multi.o multi-arm.o -o multi.elf
But there's some errors occurred:
warning: can not find entry symbol _start; defaulting to 0000000082000000
=> I solved this problem by adding -lc --entry main and the warning message is gone.
multi.c:6: undefined reference to 'printf'. I am stuck with this error and it took me 2 hours searching for sulution but I still can't not fix it.
Above is my question.
Thank you all for reading.
Try this
arm-none-eabi-as -g -o multi-arm.o multi.S
arm-none-eabi-gcc -g -o multi.elf multi.c multi-arm.o
If you want to use the linker directly then you have to provide the path to the C library on the command line. Gcc knows where the library is (relative to where it executes from and was compiled for) when it calls the linker, but for some strange reason ld does not.
Related
I have the following code with an inline assembly in C:
#include <stdlib.h>
#include <stdio.h>
__attribute__((noinline))
int *get_indecies(int *padding, int pad_size, int size, int alias_type);
int *get_indecies(int *padding, int pad_size, int size, int alias_type){
int *indecies = (int *)malloc(size*sizeof(int));
asm("dsb sy\n\t");
return indecies;
}
At the bottom I the inline is inserted...
When I produce the object code using (cross compilation for aarch64) I have the inline inserted:
.....
1f0: d5033f9f dsb sy
1f4: aa1f03e1 mov x1, xzr
......
when I link this binary with my main file using:
clang verification.c get_indecies.o -I "/home/[name]/gem5/include" -L "/home/[name]/gem5/util/m5/build/arm64/out" -lm5 -lc -O0 -static -target aarch64-linux-gnu -o verfication-base-m5
and then I do an object dump of this to check if the assembly instruction is present using:
aarch64-linux-gnu-objdump verification-base-m5 -S > assembly.s
The inline assembly does not exist.... Any ideas about what is happening in the linking stage that is removing this assembly instruction? The optimisation level is turned to 0 so I am not sure...
Thanks!
I re-ran the below line recently:
clang verification.c get_indecies.o -I "/home/[name]/gem5/include" -L "/home/[name]/gem5/util/m5/build/arm64/out" -lm5 -lc -O0 -static -target aarch64-linux-gnu -o verfication-base-m5
and then object dumped this and the inline assembly was present. Not sure what the bug was during the time but in my case it is resolved.
I'm currently trying to get into the basics regarding C-compilation without the use of an IDE.
As I only learned C- and embedded-programming with an IDE I thought it would be a good idea to learn and give me a better understanding of how the whole build process is working behind the scenes.
I mainly want to learn how to implement a complete IDEless toolchain for an STM32 controller.
So my idea was to start simple and try to understand the C-only build toolchain and its possible configurations. For this purpose I searched for tutorials and found this and this one.
I tried to follow along the first tutorial on my windows system but encountered some problems quite early that I have trouble understanding.
I created the following hello.c testfile:
#include <stdio.h>
#include <stdint.h>
int main ( void )
{
printf("Hello World!\n");
return 0;
}
First I tried the simple full compilation using gcc -o hello.exe hello.c (1.6 from the tutorial)
Everything works fine, so I decided to test the compilation steps one after the other (1.7 from the tutorial)
I called all commands in the following order:
cpp hello.c > hello.i (preprocessing) -> gcc -S hello.i (Compilation) -> as -o hello.o hello.s (Assembly) -> ld -o hello.exe hello.o (Linking)
Every step until the linking seems to work but the linker gives me the following errors:
ld: hello.o:hello.c:(.text+0xa): undefined reference to `__main' ld:
hello.o:hello.c:(.text+0x47): undefined reference to `puts' ld:
hello.o:hello.c:(.text+0x5c): undefined reference to `printf'
Did I do something wrong here? And is there a reason the ">" operator is used for preprocessing and assembling but not if I just compile using gcc -o hello.exe hello.c
Do one even use these steps seperately that often?
I read that instead of cpp hello.c > hello.i I could also use gcc -E main.c > main.i so why use the cpp command, are there any advantages?
Next I set this problem aside and tried to add includes.
For this purpose I created the following 2 files:
myFunc.c:
uint8_t myFunc( uint8_t param )
{
uint8_t retVal = 0;
retVal = param + 1;
return retVal;
}
myFunc.h
#include <stdint.h>
uint8_t myFunc( uint8_t param );
And changed the hello.c to:
#include <stdio.h>
#include <stdint.h>
#include "myFunc.h"
int main ( void )
{
uint8_t testVal = 0;
testVal = myFunc(testVal);
printf("Hello World!\n");
printf("Test Value is %d \n", testVal);
return 0;
}
I first tried the gcc -o hello.exe hello.c but get the error:
undefined reference to `myFunc' collect2.exe: error: ld returned 1 exit status
So I figured I should add the include path (even if it is the same directory).
After a short search and the help of the second site I tried gcc -Wall -v -IC:\Users\User\Desktop\C-Only_Toolchain hello.c -o hello.exe
But get the same error...
Is there something wrong with the way my include paths are added? (obviously yes)
Lastly I tried to test the GNU make command from the tutorial.
I opened the editor and inserted all contents shown in the tutorial.
As the editor saves the file as a .txt editor I tried to just delete the file extension.
The makefile looks like this:
all: hello.exe
hello.exe: hello.o
gcc -o hello.exe hello.o
hello.o: hello.c
gcc -c hello.c
clean:
rm hello.o hello.exe
But if I enter make in my console I get the error that the command "make" is written incorrectly or could not be found.
I used tab for the indentation just as the tutorial suggests but it will not even recognize that there is a makefile.
Is this because it was originally a .txt file before I deleted the extension?
I would be happy if someone could help me with my confusing regarding this rather simple issues...
Furthermore I would be very thankful if you have some good suggestions on how to get into this topic more efficiently or have some good sources to share.
Thank you in advance and stay healthy :)
Best Regards
Evox402
So, these are a lot of questions.
(In the following I use linux, so some outputs are just similar, not identical, like paths and the assembly output, but because of your usage of gcc, it's quite transferable to windows).
I called all commands in the following order: cpp hello.c > hello.i (preprocessing) -> gcc -S hello.i (Compilation) -> as -o hello.o hello.s (Assembly) -> ld -o hello.exe hello.o (Linking)
As a repetition: What are you doing here?
cpp hello.c > hello.i
You run the preprocessor over the C file. It just does a text-replace of macros/ #defines and includes files.
This looks like this. (A bit shortened as it has around 800 lines)
...Snip....
struct _IO_FILE;
typedef struct _IO_FILE FILE;
struct _IO_FILE
{
int _flags;
char *_IO_read_ptr;
char *_IO_read_end;
char *_IO_read_base;
char *_IO_write_base;
char *_IO_write_ptr;
char *_IO_write_end;
char *_IO_buf_base;
char *_IO_buf_end;
char *_IO_save_base;
char *_IO_backup_base;
char *_IO_save_end;
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
int _flags2;
__off_t _old_offset;
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
_IO_lock_t *_lock;
__off64_t _offset;
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
size_t __pad5;
int _mode;
char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
...Snip...
extern int printf (const char *__restrict __format, ...);
...Snip...
int main ( void )
{
printf("Hello World!\n");
return 0;
}
Now all important definitions are included, so the C compiler can run.
gcc -S hello.i.
It just converts your C code to assembly. (It will look a bit different on windows)
.file "hello.c"
.text
.section .rodata
.LC0:
.string "Hello World!"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
call puts#PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Debian 10.2.0-17) 10.2.0"
.section .note.GNU-stack,"",#progbits
Now you have to convert the assembly code to machine code:
as -o hello.o hello.s
This command just generates an so called object file with your code and important metadata, the linker will need.
ld -o hello.exe hello.o
Now you invoke the linker with your object file as argument and hello.exe as output file. It will look for the entry point (_start on linux-like, WinMain for example on windows, or sometimes _main).
But also the functions from the C-standard-library are missing.
But why? You don't say the linker, that you want to include it. If you invoke the linker ld as explicit as you did, you have to pass all libraries you want to include.
You have to add for example -lc to include the stdlib, and so on.
Did I do something wrong here?
You just forgot to add the C library to the libraries the linker should link with your object-file.
And is there a reason the ">" operator is used for preprocessing
> is not from cpp. It is from the shell. Try running without > hello.i. The preprocessor will just output it on the console. The > redirects to the specified file (Here hello.i).
I could also use gcc -E main.c > main.i so why use the cpp command, are there any advantages?
There is no difference. gcc calls the preprocessor internally.
Do one even use these steps seperately that often?
These steps are sometimes used in makefiles, but not as separated as you did, but often only in compiling+linking as two separate steps to reduce compile-time.
first tried the gcc -o hello.exe hello.c but get the error:
It compiles, the C compiler knows, there is at least a definition for myFunc and because of this, it emits valid assembly code.
But the linker, as soon as it resolves the references to functions, it doesn't find it and emits the error.
You have to add the myFunc.c to your commandline:
gcc -o hello.exe hello.c myFunc.c
But if I enter make in my console I get the error that the command "make" is written incorrectly or could not be found. I used tab for the indentation just as the tutorial suggests but it will not even recognize that there is a makefile. Is this because it was originally a .txt file before I deleted the extension?
You have to add the directory of make.exe to the path.
Suppose it has the path:
C:\Foo\bar\baz\make.exe
Then you add it to the path (Execute it in the commandline):
set PATH=%PATH%;C:\Foo\bar\baz
This will only work until you close the commandline, or you can set it permanently as outlined here for example.
I am trying to learn how to interface Haskell and C. To start it out, I wrote Inc.hs, the simplest thing I can conceive:
{-# LANGUAGE ForeignFunctionInterface #-}
module Inc where
import Foreign
import Foreign.C.Types
inc :: Int -> Int
inc = (+1)
foreign export ccall cinc :: CInt -> CInt
cinc :: CInt -> CInt
cinc = fromIntegral . inc . fromIntegral
And compiled it to produce Inc_stub.h:
ghc -c Inc.hs
Worked fine. I then wrote the C file, also trying to be as simple as humanly possible:
#include <stdio.h>
#include "Inc_stub.h"
int main(int argc, char *argv[]) {
int a = 1;
hs_init(&argc, &argv);
a = cinc(a);
hs_exit();
if (a == 2) {
puts("Worked!");
}
return 0;
}
Tried to compile it, got this linker error:
ghc -no-hs-main inc.c Inc -o simplest
Linking simplest.exe ...
inc.o:inc.c:(.text+0x0): multiple definition of `main'
Inc.o:inc.c:(.text+0x0): first defined here
Inc.o:inc.c:(.text+0x31): undefined reference to `cinc'
c:/program files/haskell platform/7.10.2-a/mingw/bin/../lib/gcc/x86_64-w64-mingw32/4.6.3/../../../../x86_64-w64-mingw32/bin/ld.exe: Inc.o: bad reloc address 0x0 in section `.pdata'
collect2: ld returned 1 exit status
Everything was compiled using GHC 7.10.2 on Windows 10 64-bit.
I did the following:
renamed inc.c to inc_main.c because having C object file inc.o might overlap with haskell object
run ghc -c -no-hs-main Inc.hs -o Inc.o
Produce C object file by gcc -O -Wall -I/usr/lib/ghc/include -c inc_main.c
Link to an executable with ghc -no-hs-main Inc.o inc_main.o -o simplest
This is just explanation of what's going on, see #Hakala's answer for the solution.
The problem is that on Windows file names are case insensitive.
When you are executing
$ ghc -no-hs-main inc.c Inc -o simplest
GHC calls GCC to compile inc.c into object file inc.o. But on Windows it also overwrites Inc.o generated by ghc -c Inc.hs. So, it is actually the same as executing
$ ghc -no-hs-main inc.c inc.o -o simplest
And linking inc.o twice will obviously result in "multiple definition" error.
I've started working on a home-brew OS for learning purposes. So it works like this :
Once the kernel is loaded I create a stack and call my kmain()
In kmain I try calling function foo() defined in header.h
//Header.h
#ifndef INCLUDE_HEADER_H
#define INCLUDE_HEADER_H
int foo(char* buf);
int bar();
#endif
Using nm on my kernel I can clearly see that foo() is in the binary but when I disassemble kmain with gdb I see that foo isn't called, instead bar is.
This problem is recurrent on all headers containing multiple functions.
I compile on windows 10 in a Cygwin environment. I use the following arguments passed to nasm/gcc/ld in my makefile
CC = gcc
CFLAGS = -m32 -nostdlib -nostdinc \
-nostartfiles -fno-leading-underscore -nodefaultlibs\
-Wall -Wextra -Wno-unused-variable -Wno-unused-function\
-c
LD = i686-elf-ld
LDFLAGS = -Tlink.ld -melf_i386
AS = nasm
ASFLAGS = -f elf
Any ideas why ?
EDIT :
//screen.h
#ifndef SCREEN_H
#define SCREEN_H
int test();
void print(char c);
#endif
And
//kmain.c
#include "screen.h"
int kmain(){
int b = test();
print('A');
return 0xcafebabe;
}
nm kernel.elf
$ nm kernel.elf
e4524ffe a CHECKSUM
00000000 a FLAGS
0010011c b kernel_stack
00004000 a KERNEL_STACK_SIZE
00100000 T kmain
001000c8 T loader
001000dd t loader.loop
1badb002 a MAGIC_NUMBER
001000b0 T outb
00100072 T print
0010002c T strlen
00100068 T test
0010005c T testFunc
gdb disassembly of kmain:
(gdb) disassemble kmain
Dump of assembler code for function kmain:
0x00100000 <kmain+0>: push %ebp
0x00100001 <kmain+1>: mov %esp,%ebp
0x00100003 <kmain+3>: sub $0x28,%esp
0x00100006 <kmain+6>: call 0x10006b <print+1> ;should call test but calls print instead
0x0010000b <kmain+11>: mov %eax,-0xc(%ebp)
0x0010000e <kmain+14>: movl $0x41,(%esp) ;pushes 'A'
0x00100015 <kmain+21>: call 0x100084 <print+26> ;calls print('A')
0x0010001a <kmain+26>: mov $0xcafebabe,%eax
0x0010001f <kmain+31>: leave
0x00100020 <kmain+32>: ret
0x00100021 <kmain+33>: nop
0x00100022 <kmain+34>: nop
0x00100023 <kmain+35>: nop
End of assembler dump.
0x00100006 <kmain+6>: call 0x10006b <print+1> ;should call test but calls print instead
<print+1> is just the label. This instruction does call the test function as can be seen from the address 0x10006b :
00100068 T test
00100072 T print
It'll be clearer if you look at the disassembly of the compiled "screen.c".
I found that the problem was in the compiler tool-chain I was using. It's what created the weird linking problem.
Here are the instructions I followed to compile a clean new Binutils + Gcc and it's working now !
I have a small program that's made of an assembly function and a C function which calls it.
Now the program compiles and works perfectly on a UNIX system but when using the makefile in cygwin i get the following error:
gcc -m32 -g -c -o main.o main.c
gcc -g -m32 -o ass0 main.o myasm.o
main.o: In function main':
/cygdrive/c/ass0/main.c:15: undefined reference to_strToLeet'
collect2: error: ld returned 1 exit status
makefile:3: recipe for target 'ass0' failed
make: *** [ass0] Error 1
code of the main.c file :
#include <stdio.h>
# define MAX_LEN 100 // Maximal line size
extern int strToLeet (char*);
int main(void) {
char str_buf[MAX_LEN];
int str_len = 0;
printf("Enter a string: ");
fgets(str_buf, MAX_LEN, stdin); // Read user's command line string
str_len = strToLeet (str_buf); // Your assembly code function
printf("\nResult string:%s\nNumber of letters converted to Leet: %d\n",str_buf,str_len);
}
start of assembly code:
section .data ; data section, read-write
an: DD 0 ; this is a temporary var
section .text ; our code is always in the .text section
global strToLeet ; makes the function appear in global scope
extern printf ; tell linker that printf is defined elsewhere
strToLeet: ; functions are defined as labels
push ebp ; save Base Pointer (bp) original value
mov ebp, esp ; use base pointer to access stack contents
pushad ; push all variables onto stack
mov ecx, dword [ebp+8] ; get function argument
makefile code :
all: ass0
ass0: main.o myasm.o
gcc -g -m32 -o ass0 main.o myasm.o
main.o: main.c
gcc -m32 -g -c -o main.o main.c
myasm.o: myasm.s
nasm -g -f elf -l ass0list -o myasm.o myasm.s
help would be most appriciated
Solved by user 'tvin' -
Try to modify your prototype to become extern int strToLeet (char*) asm ("strToLeet"); – tivn