Compiling GAS code doesn't detect -fPIC option - gcc

I am trying to compile some GAS code for a project using the GCC gnu compiler. Here is how I am compiling it:
gcc -c boot.s -o boot.o -fPIC
After I compile my kernel.c file with the -fPIC argument, I try to link it with this command:
gcc -N -T linker.ld -o Slack\ Berry.bin -ffreestanding -nostdlib kernel.o boot.o -lgcc
It comes up with:
/usr/bin/ld: boot.o: relocation R_X86_64_32 against '.multiboot' can not be used when making a PIE object; recompile with -fPIC
This leads me to think that it is not compiling my GAS code with -fPIC. How can I fix this?

First of all you probly need -fPIE rather than -fPIC. -fPIE allows compiler to generate more efficient code but can only be used for code that's part of main executable (not shared library).
Now both -fPIC and -fPIE are compiler-only flags and are not passed to assembler. You'll need to explicitly use PIC-specific mnemonics in your assembly code instead of position-dependent calls and branches e.g instead of
movq $bar, %rdx
use
movq bar#GOTPCREL(%rip), %rdx
(normally to get the syntax I need I just run gcc -fPIE -S -o- on matching C snippet).

Recompile with -fPIC only applies if the asm was generated by a compiler, not written by hand. It has no effect on how asm is assembled into machine code.
The problem is that your PIE executable can't be linked with 32-bit absolute addresses. (Did you mean to make a PIE instead of a static position-dependent executable)?
You don't need the full shared-library stuff for referencing symbols in another library or the main executable (like #yugr's answer shows how to do). Your freestanding kernel may not even have a GOT or PLT, and definitely shouldn't use them for internal symbols.
The only change needed is lea bar(%rip), %rdx, a RIP-relative LEA instead of a mov $imm32, %r/m64. (movabs would work to, but be larger and usually slower.)
Or, if you actually meant to build with -static and create an executable that will be loaded at a fixed address in the low 32 bits of address space, you should use mov $bar, %edx to get a 5-byte mov $imm32, %r32 encoding instead of 7-byte mov $sign_extended_imm32, %r/m64 or a 7-byte LEA. See also Difference between movq and movabsq in x86-64

Related

What causes "x.asm:(.text+0xd): undefined reference to `y'"?

For a long time I had not programmed with C and Assembler (about 2 years). Now I have decided to start again but I would like to do something much more complicated. I thought about creating a simple kernel. Now I found this source code on the internet:
boot.asm:
global loader
extern kernel_main
MAGIC equ 0xbad
FLAGS equ 0x3
CHECKSUM equ -(MAGIC+FLAGS)
section .text
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
loader:
call kernel_main
cli
quit:
hlt
jmp quit
kernel.c:
void print(char *text) {
char *memory = (char*)0xb8000;
while(*text) {
*memory++ = *text++;
*memory++ = 0x3;
}
}
void kernel_main() {
print("My cat sometimes smells like cafe. I love it.");
}
linker.ld:
ENTRY(loader)
SECTIONS {
. = 0x100000;
.text : { *(.text) }
}
Note: I compiled the C file with "GCC" and the Assembler file with "NASM".
If I try this command:
ld -T linker.ld -elf_i386 -o final.bin boot.o kernel.o
It says: "boot.asm:(.text+0xd): undefined reference to `kernel_main'".
How can I fix this?
Im working on windows and do not want to run a VM with Linux or anything. Thanks in advance!
Edit:
This is my GCC command:
gcc -m32 -o kernel.o srckernel.c -nostdlib -nostartfiles -nodefaultlibs
This is my NASM command:
nasm -f elf32 -o boot.o boot.asm
There are a number of things wrong. I will assume given the error:
boot.asm:(.text+0xd): undefined reference to kernel_main
that you are not using an ELF cross compiler and that you are using a GCC compiler that generates native Windows executables (ie. Cygwin and MinGW). I highly recommend the use of an i686 (or x86_64) ELF cross compiler for OS Development especially on Windows.
Your primary problems are:
The option -elf_i386 was probably meant to be -melf_i386 however that is even incorrect. With a GCC that targets windows you will want to use -mi386pe to output as Win32 PE/COFF format. The Windows GCC linker usually doesn't know how to generate ELF executables. I also recommend using the -N option when using LD to output i386pe format. Change your linker command to be:
ld -N -T linker.ld -mi386pe -o final.bin boot.o kernel.o
With Win32 PE/COFF objects1: functions that use the CDECL calling convention have to have an underscore (_) prepended to them. kernel_main needs to be _kernel_main. You need to change these lines in boot.asm from:
extern kernel_main
call kernel_main
to:
extern _kernel_main
call _kernel_main
You don't show how you compile kernel.c and how you assemble boot.asm but they should look similar to:
nasm -f win32 boot.asm -o boot.o
gcc -g -c -m32 -ffreestanding kernel.c -o kernel.o
When you do manage to generate final.bin it is a Windows PE executable. The Multiboot specification requires ELF executables. After linking to final.bin with LD, you can convert final.bin to ELF format with:
objcopy -O elf32-i386 final.bin final.elf
final.elf should now be usable as a Multiboot ELF executable.
There is an issue with your Multiboot header in boot.asm. The Multiboot magic value is 0x1badb002 not 0xbad. Since you haven't specified a video configuration in your Multiboot header FLAGS should not have Bit 1 set, FLAGS should be 0x1 instead of 0x3. Change your Multiboot header from:
MAGIC equ 0xbad
FLAGS equ 0x3
to:
MAGIC equ 0x1badb002
FLAGS equ 0x1
With the changes noted above I was able to generate an ELF executable called final.elf. When run with QEMU using the command:
qemu-system-i386 -kernel final.elf
The output I get is:
Footnotes:
1The extra underscore on function names doesn't apply when generating Win64 PE32+ objects.

how can I link with gcc without moving things in a .text section

I'm trying to make an executable out of two files:
vm.o, which contains a bunch of non-relocatable functions,
launcher.c, which has a main function that starts my vm.
vm.o is an elf32 file, with a .text section of code that expects to be loaded at 0x401000, as shown by objdump -x vm.o.
...
SYMBOL TABLE:
00401000 l d .text 00000000 .text
00401000 g .text 00000000 _binary_vm_o_start
....
I'm running gcc like this:
gcc -no-pie -m32 -o vm vm.o loader.c -ldl
But the generated file ignores the positions specified in vm.o. If I do
gcc -no-pie -m32 -o vm vm.o loader.c -ldl -Ttext 0x401000
I get close, but _start is put at 0x401000 and my vm.o is put at 0x4010fb.
Is there a simple way to tell gcc to put the .text section of vm.o in the desired position? I know a way of doing this with ld, but I'd rather use gcc to link.
I finally found a workaround that, while not exactly the same, works very well. I can just rename my .text section in vm.o (so that it does not get merged with the other .text ones), and then make gcc put that particular section at the desired position:
# change the name of .text section to .vm
objcopy --rename-section .text=.vm,contents,alloc,load,code vm.o vm-temp.o
# compile setting .vm to 0x401000 (no-pie means no position-independent code)
gcc -no-pie -m32 -o vm vm-temp.o loader.c -Wl,--section-start=.vm=0x401000 -ldl
note that the -T in gcc only works for changing the address of .text but doesn't for other sections.

are there debugging options for ld

I have written an assembly program that, for testing purposes, just exits. The code is as follows:
section .text
_global start
_start:
mov eax, 1
mov ebx, 0
int 0x80
The program is obviously in 32-bit; however, I am using 1 64-bit processor and operating system, so I compiled it (using nasm) and linked it as follows:
nasm -f elf exit.asm
ld -m elf_i386 -s -o exit exit.o
debugging the program with gdb, I can't list the code since there are no debugging symbols.
(gdb) list
No symbol table is loaded. Use the "file" command.
In using gcc, you can use the options -ggdb to load the symbols while compiling a c file. but since I don't how to use gcc to compile 32-bit assembly for 64-bit machines (I have searched this but can't find a solution,) I am forced to use ld. can I load the debugging symbols using ld? sorry for the long question and the excess information. Thanks in advance.
Debugging information is generated by nasm when you pass -g. Additionally, you also need to specify what type of debugging information you want (typically dwarf), which is done with the -F switch. So to assemble your file, write
nasm -f elf -F dwarf -g file.asm
then link without -s to preserve the symbol table and debugging information:
ld -m elf_i386 -o file file.o
The -s switch tells ld to "strip" the debugging info. Lose that!

Gcc complains about vstmia - why?

I'm trying to compile a program for Raspberry Pi 2B (ARMv7 / Neon), but I get an error from an inline assembly code:
Error: VFP single precision register expected -- `vstmia.64
r9,{d16-d31}'
The code is:
asm volatile (
"vstmia.64 %[reg]!, {d0 - d15} # read all regs\n\t"
"vstmia.64 %[reg], {d16 - d31} # read all regs\n\t"
::[reg] "r" (&vregs):
);
Funny thing is that it doesn't complain about the first vstmia.
I tried with single {d0 - d32} first and I thought maybe there were too many 64-bit registers, but that's obviously not the problem.
vregs is a 8-byte aligned storage.
I'm using arm-linux-gnueabihf-gcc 4.8.3, with this command line:
arm-linux-gnueabihf-gcc -mcpu=cortex-a7 -marm -O2 -g -std=gnu11 -MMD -MP -MF"ARM_decode_table.d" -MT"ARM_decode_table.o" -c -o "ARM_decode_table.o" "../ARM_decode_table.c"
By not specifying an appropriate -mfpu option, you get whatever FPU support the compiler's default configuration provides. From your configuration in this case, that is --with-fpu=vfp, which means crusty old VFPv2 with only 16 D registers overlaying the 32 S registers. Thus the first instruction targeting d0-d15 is fine, but the assembler refuses to assemble the second instruction which it knows won't work on the chosen target.
For Cortex-A7 with NEON, -mfpu=neon-vfpv4 will let the toolchain know that it can let rip and use everything you have available.

ld MinGW link to standard C library

I have a problem with following code.
extern printf
global _main
main:
push msg
call printf
ret
msg db "Hello world",0
I assemble this with NASM using nasm -fwin32 test.asm Then i link it using ld test.obj.
And it tells me "test.obj:test.asm:(text+0x6): undefined reference to 'printf'"
How to link my file to standard C libraries? I have ld from latest MinGW.
To assemble code :
nasm -fwin32 test.asm
Microsoft will prefix functions using the cdecl calling convention with a underscore.
To be match to the C calling convention printf should be _printf.
The same applies for _main instead of main.
And link with:
ld test.obj -lmsvcrt -entry=_main -subsystem=console -o test.exe
Here -entry command line option is used to invoking ld to specify the entry point for program .
Then use -l options to pass msvcrt library to the ld linker, otherwise you will get an error message, (undefined reference to `printf') which means that the linker did not found the symbol printf in the specified object file produced by NASM.
Here is completed source:
global _main
extern _printf
section .text
_main:
push msg
call _printf
add esp, 4 ;adjust the stack
ret
msg db "Hello world",0
I can see several issues with your code. First, you've got an underscore on global _main but not on main:. These should match. You can either use underscores throughout, or - what I would do - not at all... and for Windows, assemble as nasm -f win32 --prefix _ test.asm. This would make it "portable" in that, for Linux, it would assemble, without the --prefix _ without the underscores. Linux doesn't use underscores on global or extern symbols. If, by some chance, you were using OpenWatcom C, you could use --postfix _. Yeah, OpenWatcom uses trailing underscores. Yeah, I know they told us C was standardized. but once you get under the hood, this isn't really true.
The other big issue is that after calling _printf, you need to add esp, 4 (or pop a dummy register) to "clean up the stack". If you're using Windows APIs, they use the STDCALL calling convention in which "callee cleans up", so you don't want to do this. Mixing C calls (CDECL calling convention) and Windows APIs might get confusing, but should work.
I think Carl has the right idea with using gcc to link it. There's nothing to "compile", but gcc knows the proper command line to ld. gcc -o test.exe test.obj will probably be enough (maybe add -m32 if the latest MinGW expects to be doing 64-bit code). This will link in some "startup code" which calls _main. This will increase the size of your executable slightly, and you "might" be able to get along without it, but it's easier to just do it.
In Linux, we can use ld directly (the command line is horrid), but ld is looking for _start, not main, as the entrypoint. We can tell ld -e main, but this entrypoint is not called(!) and there's no possible way to ret from it! The situation is probably different in Windows. You would need - as a bare minimum - -lc to tell ld that we want those C libraries. Easiest to "let gcc do it" - it won't touch your .asm code (but does link in that "startup code"). Happy Hello World! :)
Use a compiler front-end to link:
cc test.obj
If you really want to use ld directly (and you shouldn't), use the -v flag to cc to figure out what complete command line you need. For example, on my machine, it's:
ld -demangle -dynamic -arch x86_64 -macosx_version_min 10.8.0 \
-o a.out test.obj -lSystem \
/usr/bin/../lib/clang/4.2/lib/darwin/libclang_rt.osx.a
If I use GCC instead of Clang, it's even crazier:
ld -dynamic -arch x86_64 -macosx_version_min 10.8.4 -weak_reference_mismatches \
non-weak -o a.out -lcrt1.10.6.o \
-L/usr/llvm-gcc-4.2/bin/../lib/gcc/i686-apple-darwin11/4.2.1/x86_64 \
-L/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/x86_64 \
-L/usr/llvm-gcc-4.2/bin/../lib/gcc/i686-apple-darwin11/4.2.1 \
-L/usr/llvm-gcc-4.2/bin/../lib/gcc \
-L/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1 \
-L/usr/llvm-gcc-4.2/bin/../lib/gcc/i686-apple-darwin11/4.2.1/../../.. \
-L/Applications/Xcode.app/Contents/Developer/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/../../.. \
test.obj -lgcc -lSystem

Resources