ld makes all my functions link to the last one in header file - c

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 !

Related

LLDB on linux cannot step into function of shared library

I have build lldb from llvm tag 14.0.4 on linux (tested on archlinux latest and centos 7), but it cannot step into function of shared library but gdb could.
This is a minimal example
// main.cpp
#include "mylib.h"
int main(int argc, char *argv[]) {
add(argc, argc ^ 0xFF);
add(argc, argc ^ 0xFF);
return 0;
}
// mylib.h
int add(int a, int b);
// mylib.cpp
int add(int a, int b) {
return a ^ b & b;
}
The makefile is
all:
clang++ mylib.cpp -O0 -g3 -shared -fpic -o mylib.so
clang++ main.cpp -O0 -g3 mylib.so -o main
After building the program, you can use LD_LIBRARY_PATH=. lldb main to load the program into lldb and reproduce the problem with the following steps
b main to set a breakpoint at main function
r to start the program
and now you reached to line add(argc, argc ^ 0xFF);, press s to step into source, but you will get into assemble, a demo output will like
(lldb) s
Process 15758 stopped
* thread #1, name = 'main', stop reason = step in
frame #0: 0x0000000000400500 main`add(int, int)
main`add:
-> 0x400500 <+0>: jmp qword ptr [rip + 0x200b22] ; _GLOBAL_OFFSET_TABLE_ + 40
0x400506 <+6>: push 0x2
0x40050b <+11>: jmp 0x4004d0
main`_start:
0x400510 <+0>: xor ebp, ebp
If you use gdb to debug the program, gdb will step you into add function rather than assemble codes.
I have tried lldb on macOS 12.5, it works fine.
This problem was reported to the lldb bug tracker:
https://github.com/llvm/llvm-project/issues/54250
It seems to have been a bug in some of the 14.x versions of lldb on Linux, but the originator of that bug said it was fixed in 15.0 and the TOT lldb's.

Call an assembler function from C code in linux [duplicate]

This question already has answers here:
32-bit absolute addresses no longer allowed in x86-64 Linux?
(1 answer)
What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
(1 answer)
Assembling 32-bit binaries on a 64-bit system (GNU toolchain)
(2 answers)
Closed 3 years ago.
I want to call a print function from my C program.
assembler prog:
#test.s
.text
.global _start
.global print
.type print, #function
_start:
call print
# and exit.
movl $0,%ebx # first argument: exit code.
movl $1,%eax # system call number (sys_exit).
int $0x80 # call kernel.
print:
# write our string to stdout.
movl $len,%edx # third argument: message length.
movl $msg,%ecx # second argument: pointer to message to write.
movl $1,%ebx # first argument: file handle (stdout).
movl $4,%eax # system call number (sys_write).
int $0x80 # call kernel.
mov $0, %eax
ret
.data
msg:
.ascii "Hello, world!\n" # the string to print.
len = . - msg # length of the string.
I can assemble and link it using:
$as test.s -o test.o
$ld test.o -o test
And I can execute it as a program, and it outputs "Hello, world!"
But when I tried to call a print from C code like this:
#include <stdio.h>
extern int print();
int main(){
int g;
g = print();
printf("Hello from c!, %d\n", g);
}
It was compiled using:
$gcc -c main.c test
It just prints "Hello from c, 13", that means that the function was called and return a number of chars, but does not print anything!
What am I doing wrong?
P.S.
When I trying to compile prog like this:
$as test.s -o test.o
$gcc -c main.c -o main.o
$gcc main.c test.o
I have a error:
/usr/bin/ld: test.o: in function `_start':
(.text+0x0): multiple definition of `_start'; /usr/lib/gcc/x86_64-pc-linux-gnu/9.2.0/../../../../lib/Scrt1.o:(.text+0x0): first defined here
/usr/bin/ld: test.o: relocation R_X86_64_32 against `.data' can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: final link failed: nonrepresentable section on output
collect2: error: ld returned 1 exit status
Ok, done! Thanks clearlight
I can compile all use
$as test.s -o test.o
$gcc -c main.c -o main.o
$gcc -no-pie main.c test.o
And all will work fine!

How to compile assembly and C together as bin format

I am trying to make my own operating system from scratch and am making my own boot loader. I have a function to print a string onto the screen.
Here is some code that I have:
ORG 0x7C00
BITS 16
mov si, msg
call Print
cli
hlt
Print:
lodsb
cmp al, 0
je Done
mov ah, 0Eh
mov bh, 0
int 10h
jmp Print
Done:
ret
msg db 'Hello World!', 0
times 510-($-$$) db 0
dw 0xAA55
This is then compiled with the following command:
nasm -f bin bootloader.asm -o bootloader.bin
The question is, how would I be able to access the print function within C? I know I have to use the extern keyword, but how would I compile this into a binary format file?
Basically you have to run gcc with -ffreestanding (don't link) and then link using ld with the flags -static, -nostdlib.
Creating bootloader in C is not exactly good idea. I'd recommend you to get copy of GRUB and work on top of it. OSDEV wiki has explained this incredibly well.
To sum things up, whenever you'll try to create bootloader in C, use these to compile it:
$ gcc -m16 -c -g -Os -march=i686 -ffreestanding -Wall -Werror -I. -o bootloader.o bootloader.c
$ ld -static -T linker.ld -nostdlib --nmagic -o bootloader.elf bootloader.o
$ objcopy -O binary bootloader.elf bootloader.bin
Second thing, you can't use extern! You didn't set up stack, so C code will probably bail out pretty quickly. C compiler doesn't know in which format do you pass parameters to it, because your function doesn't follow any of usual conventions. Possible linker script:
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
_text = .;
*(.text);
_text_end = .;
}
.data :
{
_data = .;
*(.bss);
*(.bss*);
*(.data);
*(.rodata*);
*(COMMON)
_data_end = .;
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
/DISCARD/ :
{
*(.note*);
*(.iplt*);
*(.igot*);
*(.rel*);
*(.comment);
}
}
Also, GCC is by default emitting 32-bit code - you need to force it to generate 16-bit code using __asm__(".code16gcc\n") or, as suggested in comments, pass -m16 parameter to compilers' commandline.
You can rewrite your function to C (to make it complain any of calling conventions) like so:
void print(const unsigned char * s){
while(*s){
__asm__ __volatile__ ("int $0x10" : : "a"(0x0E00 | *s), "b"(7));
s++;
}
}
And of course, right after .code16gcc, you'd have to jump directly to your bootloader start: __asm__ ("jmpl $0, $main\n");

How can I call assembly function from C code?

I use avr-as assembler. I want to use functions defined in assembly from a C file. How can I use assembly code in C code?
I am looking for solutions where the assembly source is in a separate source, i.e. not inlined into the C source.
Here's a simple example to get you started. Suppose you want to write a main loop in C and you want to call a function written in assembly to blink PB5.
The C source declares and uses (but doesn't define) blinkPB5:
/* main.c */
#include <avr/io.h>
#include <util/delay.h>
extern void blinkPB5();
int main ()
{
DDRB |= _BV(DDB0);
for (;;)
{
blinkPB5();
_delay_ms(500);
}
}
The assembly source defines blinkPB5. Note that .global is used to export blinkPB5:
;; blinkPB5.s
.global blinkPB5
.section .text
blinkPB5:
ldi r25, 0x01
in r24, 0x05
eor r24, r25
out 0x05, r24
ret
.end
The two can be compiled separately:
avr-gcc -c -O3 -w -mmcu=atmega328p -DF_CPU=1000000L main.c -o _build/main.c.o
avr-gcc -c -O3 -w -mmcu=atmega328p -DF_CPU=1000000L blinkPB5.s -o _build/blinkPB5.s.o
then linked together, and formatted into a .hex image:
avr-gcc -Os -Wl,--gc-sections -mmcu=atmega328p _build/main.c.o _build/blinkPB5.s.o -o _build/image.elf
avr-objcopy -Oihex -R.eeprom _build/image.elf _build/image.hex

Is function declaration essential to C programming?

I used to believe that we should declare a function which is defined in another file before use it, but recently I changed my way of thinking due to an experience of programming. For three files, C and ASM:
main.c
extern test_str;
/*extern myprint*/ --> If I add the line, gcc will report an error: called object ‘myprint’ is not a function
void Print_String() {
myprint("a simple test", test_str);
}
kernel.asm
extern Print_String
[section .text]
global _start
global test_str
test_str dd 14
_start:
call Print_String
jmp $
another.asm
[section .text]
global myprint
myprint:
mov edx, [esp + 8]
mov ecx, [esp + 4]
mov ebx, 1
mov eax, 4
int 0x80
ret
compile
nasm -f elf another.asm -o another.o
gcc -c -g main.c -o main.o
nasm -f elf kernel.asm -o kernel.o
ld -o final main.o kernel.o another.o
result
./final
a simple test
In my view, if I want to use the function myprint in main.c, I should declare it using extern beforehand, because myprint is defined in another file, but the result is exactly opposite. Just as main.c shows above. If I add the line extern myprint, I will get an error. However, without that declaration, I will get the right result. What's more, I didn't define function myprint in main.c, why can I use that function? Shouldn't I declare it beforehand?
When you call a function without a prototype the compiler makes some assumptions and guesses about the parameters of that function. So you should declare it, but declare it as a function:
void myprint(const char *, const char *); /* Or whatever. */
Well, you can use the function myprint, though its not defined function in main.c, with no error. This is because the compiler, while creating the object file fills in a NULL value against the symbol myprint in the object file created.
This NULL value is replaced at all places in the binary with the actual address of the function only during the linking phase. The linker refers to the symbol table across all the object files and resolves the symbol (wherever referred) with the actual address.
Certainly you shall see warnings/errors with the -Werror -Wall options to gcc. Although, you can get more insight using objdump as follows:
objdump -D main.o | less
Hope that helps to clear your doubt.

Resources