If I have the following C Code
int main()
{
return 77;
}
I can generate asm code with the -S option on clang to get the following (Intel syntax)
$clang -O0 -mllvm --x86-asm-syntax=intel main.c -S
the code then is
.section __TEXT,__text,regular,pure_instructions
.globl _main
.align 4, 0x90
_main: ## #main
.cfi_startproc
## BB#0:
push RBP
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset rbp, -16
mov RBP, RSP
Ltmp4:
.cfi_def_cfa_register rbp
mov EAX, 77
mov DWORD PTR [RBP - 4], 0
pop RBP
ret
.cfi_endproc
.subsections_via_symbols
However neither as, gas or nasm will generate an object file to link with ld... does clang or gcc generate actual good "ready to go" asm? gcc's default assembler is gas (which isn't even installed on mac os x...? isn't it the same for clang).
So how do I manually assemble the asm code and then link it?
Yes, gcc generates correct assembly code.
It's just as.
You should be able to assemble it with as and link with ld:
as -o example.o example.s
ld -macosx_version_min 10.8.0 -o example example.o -lSystem
or, probably more easily, by using gcc or clang as the frontend:
cc -o example example.s
Edit: Complete working example:
$ cat example.c
int main()
{
return 77;
}
$ gcc -S example.c -o example.s
$ as -o example.o example.s
$ ld -macosx_version_min 10.8.0 -o example example.o -lSystem
$ ./example
$ echo $?
77
OK, since you're using clang, you might want to be using its assembler, too. Mixing & matching toolchains usually ends in tears:
$ clang -cc1as -filetype obj -mllvm --x86-asm-syntax=intel -o example.o example.s
$ ld -macosx_version_min 10.8.0 -o example example.o -lSystem
Since you're using clang, why not compile it with clang instead of gcc?
clang main.s -mllvm --x86-asm-syntax=intel -o main
Try calling gcc with the .s file.
Related
Im trying to do a simple excercise in compilation.
I have 1 c file 1 assembly file and a makefile.
when I run the 'make' command I get the following error:
gcc -g -m32 -Wall -o mainAssignment0.o mainAssignment0.c
/tmp/ccXfVxtg.o: In function `main':
/home/caspl202/Desktop/tasks/Assignment0/mainAssignment0.c:12: undefined reference to `do_Str'
collect2: error: ld returned 1 exit status
makefile:10: recipe for target 'mainAssignment0.o' failed
make: * [mainAssignment0.o] Error 1
Meaning that for some reason the c program doesnt recognize the external ASM function.
Whats even weirder is that when I run the same makefile on the same files on a different machine it works like a charm. I would really like someone to shed some light on this thing.
C code:
#include <stdio.h>
#define MAX_LEN 100
extern int do_Str(char*);
int main(int argc, char** argv) {
char str_buf[MAX_LEN];
int counter = 0;
fgets(str_buf, MAX_LEN, stdin);
counter = do_Str (str_buf);
printf("%s%d\n",str_buf,counter);
return 0;
}
ASM code:
section .data
an: dd 0
section .text
global do_Str
do_Str:
push ebp
mov ebp, esp
pushad
mov ecx, dword [ebp+8]
loop:
cmp byte [ecx], 32
jnz noS
inc dword [an]
noS:
cmp byte [ecx], 65
jl noC
cmp byte [ecx], 90
jg noC
add byte [ecx], 32
noC:
inc ecx
cmp byte [ecx], 0
jnz loop
popad
mov eax,[an]
mov esp, ebp
pop ebp
ret
Makefile:
all: exec
libs: asm-lib
asm-lib: asmAssignment0.s
nasm -g -f elf -o asmAssignment0.o asmAssignment0.s
exec: mainAssignment0.c libs
gcc -g -m32 -c -o mainAssignment0.o mainAssignment0.c
gcc -g -m32 -o Assignment0.out mainAssignment0.o asmAssignment0.o
.PHONY: clean
clean:
rm -rf ./*.o Assignment0.out
You don't need to declare the function external.
int do_Str(char*);
Also, a function in C is prefixed with an underscore, so you must name it accordingly in your asm file.
global _do_Str
_do_Str:
The underscore is automatically added by the C compiler, so you don't have to use it in the C module.
The reason for your error you quote here is that your compile line is wrong. You can tell because you're trying to create an object file, but getting errors from the linker, so something is clearly not right:
gcc -g -m32 -Wall -o mainAssignment0.o mainAssignment0.c
...
collect2: error: ld returned 1 exit status
The problem is you forgot to add the -c flag to this compile line, so that the compiler generates an object file.
However, in your makefile the -c is present, so clearly this error you quote is not generated from the makefile you show us.
exec: mainAssignment0.c libs
gcc -g -m32 -c -o mainAssignment0.o mainAssignment0.c
I have main.s file.
extern printf
extern exit
section .data
fmt: db "hi!", 0xa
section .text
global _start
_start:
mov rax, 0
mov rdi, fmt
call printf
call exit
Compile and run
$ yasm -f elf64 main.s -o main.o
$ ld.lld main.o -o main --dynamic-linker /lib/ld-linux-x86-64.so.2
$ ./main
But i got:
ld.lld: error: undefined symbol: printf
ld.lld: error: undefined symbol: exit
ld.lld does not have -lc option like ld linker.
Just use : -L/lib option to tell the linker where to find libc
ld.lld main.o --dynamic-linker /lib/ld-linux-x86-64.so.2 -o main -L/lib -lc
This question already has answers here:
32-bit absolute addresses no longer allowed in x86-64 Linux?
(1 answer)
How to use RIP Relative Addressing in a 64-bit assembly program?
(1 answer)
Closed 5 years ago.
I'm trying to compile a toy example as a homework problem. I've been given a small snippet of code, but it won't compile on my home machine (Ubuntu 17, gcc 7.2.0). The exact same code compiles on school machines (Ubuntu 16, gcc 5.4.1).
When I run make, I get the following error:
/usr/bin/ld: age.o: relocation R_X86_64_32S against `.text' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status
Compiling with the -fPIC flag does not fix the problem.
Here is the sample assembly code:
.string1:
.string "Please enter your name: "
.string2:
.string "Thank you %s!\n"
.string3:
.string "Please enter your year of birth: "
.globl get_age
get_age:
push %rbx
push %rbp # callee saved registers
# local variables:
leaq -8(%rsp), %rsp # - endptr
leaq -24(%rsp), %rsp # - name_str[24]
leaq -24(%rsp), %rsp # - year_of_birth[24]
mov %rsp, %rbp
movq $.string1, %rdi
xorl %eax, %eax
call printf # printf("Please enter your name: ");
leaq 24(%rbp), %rdi
call gets # gets(name_str);
ret
This code is called from a simple main function:
#include <stdio.h>
long get_age(void);
void main () {
long age;
age = get_age();
printf("You are at least %ld years old.\n", age);
return;
printf("You are at least %ld years old and you look like it too.\n", age);
}
Here is the makefile I'm using. Note, the failure occurs when gcc -o x age.o main.o is called:
all: x
x: main.o age.o
gcc -o x age.o main.o
main.o: main.s
gcc -c main.s
age.o: age.s
gcc -g -c age.s
main.s: main.c
gcc -O2 -S main.c
clean:
rm -f x *.o main.s
As previously mentioned, the -fPIC flag does not seem to make any difference (I've tried adding it to every stage of the makefile). Any ideas?
I have one interesting compilation problem.
At first, please see code to be compiled.
$ ls
Makefile main.c sub.c sub.h
$ gcc -v
...
gcc version 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC)
## Makefile
%.o: CFLAGS+=-fPIE #[2]
main.so: main.o sub.o
$(CC) -shared -fPIC -o $# $^
//main.c
#include "sub.h"
int main_func(void){
sub_func();
subsub_func();
return 0;
}
//sub.h
#pragma once
void subsub_func(void);
void sub_func(void);
//sub.c
#include "sub.h"
#include <stdio.h>
void subsub_func(void){
printf("%s\n", __func__);
}
void sub_func(void){
subsub_func();//[1]
printf("%s\n", __func__);
}
And I compile this and got a error as below
$ LANG=en make
cc -fPIE -c -o main.o main.c
cc -fPIE -c -o sub.o sub.c
cc -shared -fPIC -o main.so main.o sub.o
/usr/bin/ld: sub.o: relocation R_X86_64_PC32 against symbol `subsub_func' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
make: *** [main.so] Error 1
And after this, I modified the code(removing a line [1]/using -fPIC instead of -PIE[2]) and then successfully compiled these.
$ make #[1]
cc -fPIE -c -o main.o main.c
cc -fPIE -c -o sub.o sub.c
cc -shared -fPIC -o main.so main.o sub.o
$ make #[2]
cc -fPIC -c -o main.o main.c
cc -fPIC -c -o sub.o sub.c
cc -shared -fPIC -o main.so main.o sub.o
Why did this phenomenon happened?
I have heard that calling a function within an object is done through PLT when it is compiled with -fPIC but done by jumping to the function directly when it si compiled with -fPIE.
I guessed that the function call mechanism with -fPIE refrains from relocation.
But I would like to know exact and accurate explanation of that.
Would you help me?
Thank you, all.
The only code generation difference between -fPIC and -fPIE for the code shown is in the call from sub_func to subsub_func. With -fPIC, that call goes through the PLT; with -fPIE, it's a direct call. In assembly dumps (cc -S), that looks like this:
--- sub.s.pic 2017-12-07 08:10:00.308149431 -0500
+++ sub.s.pie 2017-12-07 08:10:08.408068650 -0500
## -34,7 +34,7 ## sub_func:
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
- call subsub_func#PLT
+ call subsub_func
leaq __func__.2258(%rip), %rsi
leaq .LC0(%rip), %rdi
movl $0, %eax
In unlinked object files, it's a change of relocation type:
--- sub.o.dump.pic 2017-12-07 08:13:54.197775840 -0500
+++ sub.o.dump.pie 2017-12-07 08:13:54.197775840 -0500
## -22,7 +22,7 ##
1f: 55 push %rbp
20: 48 89 e5 mov %rsp,%rbp
23: e8 00 00 00 00 callq 28 <sub_func+0x9>
- 24: R_X86_64_PLT32 subsub_func-0x4
+ 24: R_X86_64_PC32 subsub_func-0x4
28: 48 8d 35 00 00 00 00 lea 0x0(%rip),%rsi # 2f <sub_func+0x10>
2b: R_X86_64_PC32 .rodata+0x14
2f: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 36 <sub_func+0x17>
And, on this architecture, when you link a shared library using cc -shared, the linker does not allow the input object files to contain R_X86_64_PC32 relocations targeting global symbols, thus the error you observed when you used -fPIE instead of -fPIC.
Now, you are probably wondering why direct calls within a shared library are not allowed. In fact, they are allowed, but only when the callee is not a global. For instance, if you declared subsub_func with static, then the call target would be resolved by the assembler and there would be no relocation at all in the object file, and if you declared it with __attribute__((visibility("hidden"))) then you would get an R_X86_64_PC32 relocation but the linker would allow it because the callee is no longer exported from the library. But in both cases subsub_func would not be callable from outside the library anymore.
Now you're probably wondering what it is about global symbols that means you have to call them through the PLT from a shared library. This has to do with an aspect of the ELF symbol resolution rules that you may find surprising: any global symbol in a shared library can be overridden by either the executable, or an earlier library in the link order. Concretely, if we leave your sub.h and sub.c alone but make main.c read like this:
//main.c
#include "sub.h"
#include <stdio.h>
void subsub_func(void) {
printf("%s (main)\n", __func__);
}
int main(void){
sub_func();
subsub_func();
return 0;
}
so it's now got the an official executable entry point in it but also a second definition of subsub_func, and we compile sub.c into a shared library and main.c into an executable that calls it, and run the whole thing, like this
$ cc -fPIC -c sub.c -o sub.o
$ cc -c main.c -o main.o
$ cc -shared -Wl,-soname,libsub.so.1 sub.o -o libsub.so.1
$ ln -s libsub.so.1 libsub.so
$ cc main.o -o main -L. -lsub
$ LD_LIBRARY_PATH=. ./main
the output will be
subsub_func (main)
sub_func
subsub_func (main)
That is, both the call from main to subsub_func, and the call from sub_func, within the library, to subsub_func, were resolved to the definition in the executable. For that to be possible, the call from sub_func must go through the PLT.
You can change this behavior with an additional linker switch, -Bsymbolic.
$ cc -shared -Wl,-soname,libsub.so.1 -Wl,-Bsymbolic sub.o -o libsub.so.1
$ LD_LIBRARY_PATH=. ./main
subsub_func
sub_func
subsub_func (main)
Now the call from sub_func is resolved to the definition within the library. In this case, using -Bsymbolic allows sub.c to be compiled with -fPIE instead of -fPIC, but I don't recommend you do that. There are other effects of using -fPIE instead of -fPIC, such as changing how access to thread-local storage needs to be done, and those cannot be worked around with -Bsymbolic.
I cannot combine my kernel_entry.asm and main.c. My main.c calls an asm function Sum. Both nasm and gcc compiles respective files. However, the linker gives an error.
Kernel_entry.asm:
[bits 32]
[extern _start]
[global _Sum]
....
_Sum:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ecx, [ebp+12]
add eax, ecx
pop ebp
ret
main.c:
....
extern int Sum();
void start() {
....
int x = Sum(4, 5);
....
}
To compile source files, I use following commands:
nasm kernel_entry.asm -f win32 -o kernel_entry.o
gcc -ffreestanding -c main.c -o main.o
....
ld -T NUL -o kernel.tmp -Ttext 0x1000 kernel_entry.o main.o mem.o port_in_out.o screen.o idt.o
Linker gives following error:main.o:main.c:(.text+0xa82): undifened reference to 'Sum'. I tried everything but couldn't find any solution. When I remove asm function call from main.c, it works.
The TL;DR version of the answer is that mixing nasm's -f win32 generates an object file that is not compatible with the GNU toolchain on Windows - you need to use -f elf if you want to link using ld. That is described in NASM's documentation here under sections 7.5 and 7.9.
The hint for me was that by running nm kernel_entry.o generated:
00000000 a .absolut
00000000 t .text
00000001 a #feat.00
U _start
U _Sum
Which basically shows Sum as an undefined symbol. After compiling as ELF, I got:
U _start
00000000 T _Sum
indicating Sum as a recognised symbol in the text section.