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.
Related
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.
I am including external asm into c, when I try to compile I am getting error.
I am compiling c file like this - g++ testing.c
Error:
cc0FHCkn.o:testing.c:(.text+0xe): undefined reference to helloWorld
collect2.exe: error: ld returned 1 exit status
C code:
#include<stdio.h>
extern "C" int helloWorld();
int main() {
printf("Its - ",helloWorld());
}
ASM code:
.code
helloWorld proc
mov rax, 123
ret
helloWorld endp
end
Note : I use that answer to be able to say more than it is possible through a remark, and using gcc.
First, just doing g++ testing.c g++ is not able to link with the assembler file which is not specified, so of course helloWorld is missing.
If I have the file hw.c :
int helloWorld()
{
return 123;
}
I ask to produce the source assembler through the option -S (I also use -O to reduce the assembler source size), so I do not have to write the assembler file by hand and I am sure it is compatible with gcc :
/tmp % gcc -O -S hw.c
That produced the file hw.s :
/tmp % cat hw.s
.file "hw.c"
.text
.globl helloWorld
.type helloWorld, #function
helloWorld:
.LFB0:
.cfi_startproc
movl $123, %eax
ret
.cfi_endproc
.LFE0:
.size helloWorld, .-helloWorld
.ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)"
.section .note.GNU-stack,"",#progbits
/tmp %
Also having the file m.c :
#include <stdio.h>
extern int helloWorld();
int main()
{
printf("%d\n", helloWorld());
return 0;
}
I can do :
/tmp % gcc m.c hw.s
/tmp % ./a.out
123
I propose you to do the same as, write helloWorld in C then generate the assembler with option -S, doing that you are sure to follow the gcc requirements in the function definition
1.) Create an ELF object file from the assembly file
nasm -f elf64 -o assembly.o assembly.asm
2.) Create an ELF object file of testing.c file
gcc -c testing.c -o testing.o
3.) Link ELF object file together to create final executable file.
gcc -o testing assembly.o testing.o
4.) Run final executable file
./testing
use extern int hellowrold();
I have a function in my C code that is being called implicitly, and getting dumped by the linker. how can I prevent this phenomena?
I'm compiling using gcc and the linker flag -gc-sections, and I don't want to exclude the whole file from the flag. I tried using attributes: "used" and "externally_visible" and neither has worked.
void __attribute__((section(".mySec"), nomicromips, used)) func(){
...
}
on map file I can see that the function has compiled but didn't linked. am I using it wrong? is there any other way to do it?
You are misunderstanding the used attribute
used
This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced...
i.e the compiler must emit the function definition even the function appears
to be unreferenced. The compiler will never conclude that a function is unreferenced
if it has external linkage. So in this program:
main1.c
static void foo(void){}
int main(void)
{
return 0;
}
compiled with:
$ gcc -c -O1 main1.c
No definition of foo is emitted at all:
$ nm main1.o
0000000000000000 T main
because foo is not referenced in the translation unit, is not external,
and so may be optimised out.
But in this program:
main2.c
static void __attribute__((used)) foo(void){}
int main(void)
{
return 0;
}
__attribute__((used)) compels the compiler to emit the local definition:
$ gcc -c -O1 main2.c
$ nm main2.o
0000000000000000 t foo
0000000000000001 T main
But this does nothing to inhibit the linker from discarding a section
in which foo is defined, in the presence of -gc-sections, even if foo is external, if that section is unused:
main3.c
void foo(void){}
int main(void)
{
return 0;
}
Compile with function-sections:
$ gcc -c -ffunction-sections -O1 main3.c
The global definition of foo is in the object file:
$ nm main3.o
0000000000000000 T foo
0000000000000000 T main
But after linking:
$ gcc -Wl,-gc-sections,-Map=mapfile main3.o
foo is not defined in the program:
$ nm a.out | grep foo; echo Done
Done
And the function-section defining foo was discarded:
mapfile
...
...
Discarded input sections
...
...
.text.foo 0x0000000000000000 0x1 main3.o
...
...
As per Eric Postpischil's comment, to force the linker to retain
an apparently unused function-section you must tell it to assume that the program
references the unused function, with linker option {-u|--undefined} foo:
main4.c
void __attribute__((section(".mySec"))) foo(void){}
int main(void)
{
return 0;
}
If you don't tell it that:
$ gcc -c main4.c
$ gcc -Wl,-gc-sections main4.o
$ nm a.out | grep foo; echo Done
Done
foo is not defined in the program. If you do tell it that:
$ gcc -c main4.c
$ gcc -Wl,-gc-sections,--undefined=foo main4.o
$ nm a.out | grep foo; echo Done
0000000000001191 T foo
Done
it is defined. There's no use for attribute used.
Apart from -u already mentioned here are two other ways to keep the symbol using GCC.
Create a reference to it without calling it
This approach does not require messing with linker scripts, which means it will work for hosted programs and libraries using the operating system's default linker script.
However it varies with compiler optimization settings and may not be very portable.
For example, in GCC 7.3.1 with LD 2.31.1, you can keep a function without actually calling it, by calling another function on its address, or branching on a pointer to its address.
bool function_exists(void *address) {
return (address != NULL);
}
// Somewhere reachable from main
assert(function_exists(foo));
assert(foo != NULL); // Won't work, GCC optimises out the constant expression
assert(&foo != NULL); // works on GCC 7.3.1 but not GCC 10.2.1
Another way is to create a struct containing function pointers, then you can group them all together and just check the address of the struct. I use this a lot for interrupt handlers.
Modify the linker script to keep the section
If you are developing a hosted program or a library, then it's pretty tricky to change the linker script.
Even if you do, its not very portable, for example gcc on OSX does not actually use the GNU linker since OSX uses the Mach-O format instead of ELF.
Your code already shows a custom section though, so it's possible you are working on an embedded system and can easily modify the linker script.
SECTIONS {
// ...
.mySec {
KEEP(*(.mySec));
}
}
So I'm trying trying to use a function defined in another C (file1.c) file in my file (file2.c). I'm including the header of file1 (file1.h) in order to do this.
However, I keep getting the following error whenever I try to compile my file using gcc:
Undefined symbols for architecture x86_64:
"_init_filenames", referenced from:
_run_worker in cc8hoqCM.o
"_read_list", referenced from:
_run_worker in cc8hoqCM.o
ld: symbol(s) not found for architecture x86_64
I've been told I need to "link the object files together" in order to use the functions from file1 in file2, but I have no clue what that means :(
I assume you are using gcc, to simply link object files do:
$ gcc -o output file1.o file2.o
To get the object-files simply compile using
$ gcc -c file1.c
this yields file1.o and so on.
If you want to link your files to an executable do
$ gcc -o output file1.c file2.c
The existing answers already cover the "how", but I just wanted to elaborate on the "what" and "why" for others who might be wondering.
What a compiler (gcc) does: The term "compile" is a bit of an overloaded term because it is used at a high-level to mean "convert source code to a program", but more technically means to "convert source code to object code". A compiler like gcc actually performs two related, but arguably distinct functions to turn your source code into a program: compiling (as in the latter definition of turning source to object code) and linking (the process of combining the necessary object code files together into one complete executable).
The original error that you saw is technically a "linking error", and is thrown by "ld", the linker. Unlike (strict) compile-time errors, there is no reference to source code lines, as the linker is already in object space.
By default, when gcc is given source code as input, it attempts to compile each and then link them all together. As noted in the other responses, it's possible to use flags to instruct gcc to just compile first, then use the object files later to link in a separate step. This two-step process may seem unnecessary (and probably is for very small programs) but it is very important when managing a very large program, where compiling the entire project each time you make a small change would waste a considerable amount of time.
You could compile and link in one command:
gcc file1.c file2.c -o myprogram
And run with:
./myprogram
But to answer the question as asked, simply pass the object files to gcc:
gcc file1.o file2.o -o myprogram
Add foo1.c , foo2.c , foo3.c and makefile in one folder
the type make in bash
if you do not want to use the makefile, you can run the command
gcc -c foo1.c foo2.c foo3.c
then
gcc -o output foo1.o foo2.o foo3.o
foo1.c
#include <stdio.h>
#include <string.h>
void funk1();
void funk1() {
printf ("\nfunk1\n");
}
int main(void) {
char *arg2;
size_t nbytes = 100;
while ( 1 ) {
printf ("\nargv2 = %s\n" , arg2);
printf ("\n:> ");
getline (&arg2 , &nbytes , stdin);
if( strcmp (arg2 , "1\n") == 0 ) {
funk1 ();
} else if( strcmp (arg2 , "2\n") == 0 ) {
funk2 ();
} else if( strcmp (arg2 , "3\n") == 0 ) {
funk3 ();
} else if( strcmp (arg2 , "4\n") == 0 ) {
funk4 ();
} else {
funk5 ();
}
}
}
foo2.c
#include <stdio.h>
void funk2(){
printf("\nfunk2\n");
}
void funk3(){
printf("\nfunk3\n");
}
foo3.c
#include <stdio.h>
void funk4(){
printf("\nfunk4\n");
}
void funk5(){
printf("\nfunk5\n");
}
makefile
outputTest: foo1.o foo2.o foo3.o
gcc -o output foo1.o foo2.o foo3.o
make removeO
outputTest.o: foo1.c foo2.c foo3.c
gcc -c foo1.c foo2.c foo3.c
clean:
rm -f *.o output
removeO:
rm -f *.o
Since there's no mention of how to compile a .c file together with a bunch of .o files, and this comment asks for it:
where's the main.c in this answer? :/ if file1.c is the main, how do
you link it with other already compiled .o files? – Tom Brito Oct 12
'14 at 19:45
$ gcc main.c lib_obj1.o lib_obj2.o lib_objN.o -o x0rbin
Here, main.c is the C file with the main() function and the object files (*.o) are precompiled. GCC knows how to handle these together, and invokes the linker accordingly and results in a final executable, which in our case is x0rbin.
You will be able to use functions not defined in the main.c but using an extern reference to functions defined in the object files (*.o).
You can also link with .obj or other extensions if the object files have the correct format (such as COFF).
I'm using an open source library which seems to have lots of preprocessing directives to support many languages other than C. So that I can study what the library is doing I'd like to see the C code that I'm compiling after preprocessing, more like what I'd write.
Can gcc (or any other tool commonly available in Linux) read this library but output C code that has the preprocessing converted to whatever and is also readable by a human?
Yes. Pass gcc the -E option. This will output preprocessed source code.
cpp is the preprocessor.
Run cpp filename.c to output the preprocessed code, or better, redirect it to a file with
cpp filename.c > filename.preprocessed.
-save-temps
This is another good option to have in mind:
gcc -save-temps -c -o main.o main.c
main.c
#define INC 1
int myfunc(int i) {
return i + INC;
}
and now, besides the normal output main.o, the current working directory also contains the following files:
main.i is the desired prepossessed file containing:
# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "main.c"
int myfunc(int i) {
return i + 1;
}
main.s is a bonus :-) and contains the generated assembly:
.file "main.c"
.text
.globl myfunc
.type myfunc, #function
myfunc:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl $1, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size myfunc, .-myfunc
.ident "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0"
.section .note.GNU-stack,"",#progbits
If you want to do it for a large number of files, consider using instead:
-save-temps=obj
which saves the intermediate files to the same directory as the -o object output instead of the current working directory, thus avoiding potential basename conflicts.
The advantage of this option over -E is that it is easy to add it to any build script, without interfering much in the build itself.
Another cool thing about this option is if you add -v:
gcc -save-temps -c -o main.o -v main.c
it actually shows the explicit files being used instead of ugly temporaries under /tmp, so it is easy to know exactly what is going on, which includes the preprocessing / compilation / assembly steps:
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s
Tested in Ubuntu 19.04 amd64, GCC 8.3.0.
CMake predefined targets
CMake automatically provides a targets for the preprocessed file:
make help
shows us that we can do:
make main.i
and that target runs:
Preprocessing C source to CMakeFiles/main.dir/main.c.i
/usr/bin/cc -E /home/ciro/bak/hello/main.c > CMakeFiles/main.dir/main.c.i
so the file can be seen at CMakeFiles/main.dir/main.c.i
Tested on cmake 3.16.1.
I'm using gcc as a preprocessor (for html files.) It does just what you want. It expands "#--" directives, then outputs a readable file. (NONE of the other C/HTML preprocessors I've tried do this- they concatenate lines, choke on special characters, etc.) Asuming you have gcc installed, the command line is:
gcc -E -x c -P -C -traditional-cpp code_before.cpp > code_after.cpp
(Doesn't have to be 'cpp'.) There's an excellent description of this usage at http://www.cs.tut.fi/~jkorpela/html/cpre.html.
The "-traditional-cpp" preserves whitespace & tabs.
Run:
gcc -E <file>.c
or
g++ -E <file>.cpp
Suppose we have a file as Message.cpp or a .c file
Steps 1: Preprocessing (Argument -E)
g++ -E .\Message.cpp > P1
P1 file generated has expanded macros and header file contents and comments are stripped off.
Step 2: Translate Preprocessed file to assembly (Argument -S). This task is done by compiler
g++ -S .\Message.cpp
An assembler (ASM) is generated (Message.s). It has all the assembly code.
Step 3: Translate assembly code to Object code. Note: Message.s was generated in Step2.
g++ -c .\Message.s
An Object file with the name Message.o is generated. It is the binary form.
Step 4: Linking the object file. This task is done by linker
g++ .\Message.o -o MessageApp
An exe file MessageApp.exe is generated here.
#include <iostream>
using namespace std;
//This a sample program
int main()
{
cout << "Hello" << endl;
cout << PQR(P,K) ;
getchar();
return 0;
}