Linking a compiled assembly and C file with ld - c

I have compiled these programs:
BITS 16
extern _main
start:
mov ax, 07C0h
add ax, 288
mov ss, ax
mov sp, 4096
mov ax, 07C0h
mov ds, ax
mov si, text_string
call print_string
jmp $
text_string db 'Calling Main Script'
call _main
print_string:
mov ah, 0Eh
.repeat:
lodsb
cmp al, 0
je .done
int 10h
jmp .repeat
.done:
ret
times 510-($-$$) db 0
dw 0xAA55
and this as a test just to try linking them
int main()
{
return 0;
}
both compile completely fine on their own using:
gcc -Wall -m32 main.c
nasm -f elf bootloader.asm
however I cannot link them using:
ld bootloader.o main.o -lc -I /lib/Id-linux.so.2
and I get this error:
ld: i386 architecture of input file `bootloader.o' is incompatible with i386:x86-64 output
ld: i386 architecture of input file `main.o' is incompatible with i386:x86-64 output
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
ld: bootloader.o: file class ELFCLASS32 incompatible with ELFCLASS64
ld: final link failed: file in wrong format
Any help would be great thanks

GCC by default already dynamically linking with libc, so if you want linking manually using ld, be sure make your ELF executable static, you can passing with -static flag.
gcc -o <filename> <filename>.c -static -Wall -m32 then link with ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o <filename> -lc <filename>.o
I guess, since assembler like NASM has statically (stand-alone without libc) you can make ELF dynamic executable directly with libc, you can passing with -dynamic-linker flag.
For example :
x86
nasm -f elf32 -o <filename>.o <filename>.asm
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o <filename> -lc <filename>.o
x86_64
nasm -f elf64 -o <filename>.o <filename>.asm
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o <filename> -lc <filename>.o

In case you just want to do some simple assembly programming on your PC, don't actually need 16bit code, and don't want to dive into bootloaders and OS development, you can get started much more easily by writing 32bit (IA32) or 64bit (AMD64) application code. Instead of BIOS interrupts, you'd use (Linux) system calls.
An example "hello world" for i386 would be:
.section .text._start
.global _start
.type _start, %function
_start:
mov $4, %eax
mov $1, %ebx
mov $message, %ecx
mov $14, %edx
int $0x80
mov $1, %eax
xor %ebx, %ebx
int $0x80
.section .rodata.message
.type message, %object
message:
.ascii "Hello, World!\n"
Assemble, link and execute via
as --32 test32.S -o test32.o && ld -m elf_i386 test32.o -o test32 && ./test32
The same thing for AMD64:
.section .text._start
.global _start
.type _start, %function
_start:
mov $1, %rax
mov $1, %rdi
mov $message, %rsi
mov $14, %rdx
syscall
mov $0x3c, %rax
xor %rdi, %rdi
syscall
.section .rodata.message
.type message, %object
message:
.ascii "Hello, World!\n"
Assemble, link and execute via
as --64 test64.S -o test64.o && ld -m elf_x86_64 test64.o -o test64 && ./test64
Just for fun, the same thing for ARM (32bit):
.syntax unified
.arch armv6
.arm
.section .text._start
.global _start
.type _start, %function
_start:
movs r7, #4
movs r0, #1
ldr r1, =#message
movs r2, #14
svc #0
movs r7, #1
movs r0, #0
svc #0
.ltorg
.section .rodata.message
.type message, %object
message:
.ascii "Hello, World!\n"
Assemble, link and execute via (e.g. on a Raspberry PI or Beaglebone):
as testarm.S -o testarm.o && ld testarm.o -o testarm && ./testarm

Related

What is the crt0.o error when cross compiling and how do I fix it? [duplicate]

How to add ctr0.o ?
I get this error:
yagarto-4.7.2/bin/arm-none-eabi-ld: cannot find crt0.o: No such file or directory
collect2: error: ld returned 1 exit status`
while compiling very simple program from here:
/* -- first.s */
/* This is a comment */
.global main /* 'main' is our entry point and must be global */
.func main /* 'main' is a function */
main: /* This is main */
mov r0, #2 /* Put a 2 inside the register r0 */
bx lr /* Return from main */
I have seen these 2 threads and didn't get any full and straight forward answer:
http://www.raspberrypi.org/phpBB3/viewtopic.php?t=50046
What is the rationale behind removing crt0.o from gcc4.7.x?
I have these files, what is the difference between crt0 and crtn can't I use it?
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/crtbegin.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/crtend.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/crti.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/crtn.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/crtbegin.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/crtend.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/crti.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/crtn.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/v6m/crtbegin.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/v6m/crtend.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/v6m/crti.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/v6m/crtn.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/v7m/crtbegin.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/v7m/crtend.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/v7m/crti.o
./yagarto-4.7.2/lib/gcc/arm-none-eabi/4.7.2/thumb/v7m/crtn.o
The SO solution gives a workaround which doesn't work either:
arm-none-eabi-gcc -o first assembler_tutorial/chapter01/first.o -nostartfiles
./yagarto-4.7.2/bin/arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000000008000
vectors.s
.globl _start
_start:
mov sp,#0x8000
bl main
hang: b hang
main.s
.globl main
main:
mov r0,#2
bx lr
memmap (linker script)
MEMORY
{
ram : ORIGIN = 0x8000, LENGTH = 0x10000
}
SECTIONS
{
.text : { *(.text*) } > ram
.bss : { *(.bss*) } > ram
}
commands
arm-none-eabi-as vectors.s -o vectors.o
arm-none-eabi-as main.s -o main.o
arm-none-eabi-ld vectors.o main.o -T memmap -o main.elf
arm-none-eabi-objdump -D main.elf > main.list
arm-none-eabi-objcopy main.elf -O binary main.bin
result
main.elf: file format elf32-littlearm
Disassembly of section .text:
00008000 <_start>:
8000: e3a0d902 mov sp, #32768 ; 0x8000
8004: eb000000 bl 800c <main>
00008008 <hang>:
8008: eafffffe b 8008 <hang>
0000800c <main>:
800c: e3a00002 mov r0, #2
8010: e12fff1e bx lr
If you want to use C instead of asm for main then
main.c
int main ( void )
{
return(2);
}
commands
arm-none-eabi-as vectors.s -o vectors.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c main.c -o main.o
arm-none-eabi-ld vectors.o main.o -T memmap -o main.elf
arm-none-eabi-objdump -D main.elf > main.list
arm-none-eabi-objcopy main.elf -O binary main.bin
result
main.elf: file format elf32-littlearm
Disassembly of section .text:
00008000 <_start>:
8000: e3a0d902 mov sp, #32768 ; 0x8000
8004: eb000000 bl 800c <main>
00008008 <hang>:
8008: eafffffe b 8008 <hang>
0000800c <main>:
800c: e3a00002 mov r0, #2
8010: e12fff1e bx lr
I prefer to use a function name other than main because some compilers add extra baggage when they see that function name.
vectors.s
.globl _start
_start:
mov sp,#0x8000
bl notmain
hang: b hang
main.c
int notmain ( void )
{
return(2);
}
result
main.elf: file format elf32-littlearm
Disassembly of section .text:
00008000 <_start>:
8000: e3a0d902 mov sp, #32768 ; 0x8000
8004: eb000000 bl 800c <notmain>
00008008 <hang>:
8008: eafffffe b 8008 <hang>
0000800c <notmain>:
800c: e3a00002 mov r0, #2
8010: e12fff1e bx lr
I had the same problem with trying to compile for STM32F4xx (Cortex M4).
I ditched Yagarto and switched to using GNU Tools ARM Embedded -
toolchain (4.8_2014q2):
https://launchpad.net/gcc-arm-embedded
Seems to work for me.

logical and physical adress in C code in real mode

Suppose I write boot loader on C. What happens when I create some global variable? What is it's logical address? How does it correspond to physical address? For example if I created some string (global)
const char* s = "some string";
Am I right that s stored in .data section? What would be the physical address of s and what would be a logical one? Should we do some extra work to make this addresses correspond each other.
My OS is Linux and I compile my code like this:
as --32 boot.S -o boot.o
gcc -c -m32 -g -Os -ffreestanding -Wall -Werror -I. -o mbr.o mbr.c
ld -Tlinker.ld -nostdlib -o mbr boot.o mbr.o
boot.S is just where I initilize some registers and call c code:
.code16
.text
.global _start
_start:
cli
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
mov $0x7c00, %sp
ljmp $0, $mmain
mmain -- function in C code. My linker script is:
OUTPUT_FORMAT(binary)
OUTPUT_ARCH(i8086)
ENTRY(_start)
SECTIONS
{
. = 0x7C00;
.text : { *(.text) }
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}

Assembly error when compiling with GCC

I'm getting "no such instruction" errors when compiling a .s file with this command:
$ gcc -s -o scall scall.s
scall.s: Assembler messages:
scall.s:2: Error: no such instruction: `section '
scall.s:4: Error: no such instruction: `global _start'
scall.s:7: Error: unsupported instruction `mov'
scall.s:8: Error: unsupported instruction `mov'
scall.s:11: Error: operand size mismatch for `int'
scall.s:13: Error: no such instruction: `section .data'
scall.s:15: Error: no such instruction: `msglength .word 12'
Here is the code of the file:
section .text
global _start
_start:
mov 4,%eax
mov 1,%ebx
mov $message,%ecx
mov $msglength,%edx
int $0x80
section .data
message: .ascii "Hello world!"
msglength .word 12
How can I get rid of the errors?
I think the following code will compile ("gcc" can compile .s and .S files and link them with C library by default but "as" do the same and don't link code with C library)
as :
.section .text
.global _start
_start:
mov $4,%eax
mov $1,%ebx
mov $message,%ecx
mov msglength,%edx
int $0x80
mov $1, %eax
mov $0, %ebx
int $0x80
.section .data
message: .ascii "Hello world!"
msglength: .word 12
gcc:
.section .text
.global main
main:
mov $4,%eax
mov $1,%ebx
mov $message,%ecx
mov msglength,%edx
int $0x80
mov $1, %eax
mov $0, %ebx
int $0x80
.section .data
message: .ascii "Hello world!"
msglength: .word 12
Correct it as follows and compile it with -c param gcc -c test.s -o test
.text
_start:
.global main
main:
mov 4,%eax
mov 1,%ebx
mov $message,%ecx
mov $msglength,%edx
int $0x80
.data
message: .ascii "Hello world!"
msglength: .word 12

Linkage error when compiling C file and Assembly file

I have .s (x86 assembly at&t syntax) file, .h (header) file with struct defenition and functions decleration which are implemented in the assembly file, and main.c file with function call (from the .s file).
when trying to compile it all together, i get the following error:
main.o: In function `main':
/home/user/workspace/Assembly/main.c:7: undefined reference to `pstrlen'
collect2: error: ld returned 1 exit status
make: *** [a.out] Error 1
pstring.h:
typedef struct {
char len;
char str[255];
} Pstring;
char pstrlen(Pstring* pstr);
main.c:
#include <stdio.h>
#include "pstring.h"
int main() {
Pstring a;
a.len = 4;
printf("Length: %d", pstrlen(&a));
return 0;
}
pstring.s:
.file "pstring.s"
.section .rodata
invalid_input: .string "invalid input!\n"
.text
.type pstrlen, #function
pstrlen:
pushl %ebp
movl %ebp, %esp
movl 8(%ebp), %eax # assign given pstring ptr address to eax
movzbl (%eax), %ecx # set ecx with the value of the first byte (length) of the address at eax
movl %ecx, %eax # set return value as the value at ecx
ret
.type pstrcpy, #function
makefile:
a.out: main.o pstring.o
gcc -m32 -g -o a.out main.o pstring.o
main.o: main.c pstring.h
gcc -m32 -g -c -o main.o main.c
pstring.o: pstring.s
gcc -m32 -g -c -o pstring.o pstring.s
clean:
rm -f *.o a.out
Thank you.
I resolved the problem by declaring pstrlen as global as follows:
.text
.globl pstrlen
.type pstrlen, #function
pstrlen:
pushl %ebp
movl %ebp, %esp
movl 8(%ebp), %eax # assign given pstring ptr address to eax
movzbl (%eax), %ecx # set ecx with the value of the first byte (length) of the address at eax
movl %ecx, %eax # set return value as the value at ecx
ret

Assembly function call from c

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.

Resources