I want to learn more about firmware developement. I already know how to write assembly programs for the old BIOS and now I wanted to start with UEFI. I managed to compile and emulate a Hello World program, and now I was trying to write a program which displays on the screen the current time using the Runtime Service GetTime(). However when I use this function the program hangs, as if it wasn't installed during PI.
Here is the code:
#include <efi.h>
#include <efilib.h>
#include <efiapi.h>
//gBS: SystemTable->BootServices;
//gRS: SystemTable->RuntimeServices;
EFI_STATUS
efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE* systab)
{
EFI_TIME* time;
InitializeLib(image, systab);
RT->GetTime(time, NULL);
Print(L"Time %u\n", time->Hour);
return EFI_SUCCESS;
}
Do you have any clue about what I've done wrong?
Here is the code I use to compile and emulate in case you need:
gcc -I/usr/include/efi -I/usr/include/efi/x86_64/ -fpic -ffreestanding -fno-stack-protector -fno-stack-check -fshort-wchar -mno-red-zone -maccumulate-outgoing-args -c main.c -o main.o
ld -shared -Bsymbolic -L/usr/lib -T/usr/lib/elf_x86_64_efi.lds /usr/lib/crt0-efi-x86_64.o main.o -o main.so -lgnuefi -lefi
objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela -j .rel.* -j .rela.* -j .reloc --target efi-app-x86_64 --subsystem=10 main.so main.efi
uefi-run -b /usr/share/edk2-ovmf/x64/OVMF.fd -q /usr/bin/qemu-system-x86_64 main.efi
If you are using gnu-efi, use uefi_call_wrapper() to call UEFI functions.
RT->GetTime(time, NULL); // Program hangs
uefi_call_wrapper(RT->GetTime, 2, time, NULL); // Okay
The reason is the different calling convention between UEFI (which uses Microsoft x64 calling convention) and Linux (which uses System V amd64 ABI). By default, gcc will generate the code in Linux format, so we need to explicitly tell it to generate it in UEFI format.
You can see the difference by peforming an objdump.
I think you missed to initialize RT.
RT = SystemTable->RuntimeServices;
Your code is very similar to one of the examples (the one at section 4.7.1) of the Unified Extensible Firmware Interface Specification 2.6. I doubth you haven't read it, but just in case.
https://www.uefi.org/sites/default/files/resources/UEFI%20Spec%202_6.pdf
Related
I have a simple OS and Bootloader code downloaded from https://github.com/FRosner/FrOS/tree/minimal-c-kernel
tutorial is at https://dev.to/frosnerd/writing-my-own-boot-loader-3mld
I have some questions about the make file
in below makefile what is x86_64-elf-ld, ld I belive is to link the object files and libraries into output .a and bin files which are libraries. So I believe to output a library called kernel.bin the author used ld's -o flag since its linker then the output is .bin file. I like to know can I output .o file from ld program (by using only ld program can I output .o binary executable, if no, then I assumed its a linker not compiler and compiler can output only executable/or self executable with main() and linker can output only library which needed by some other executable code to call its functions )
the first line is noticed is
kernel.bin: kernel_entry.o kernel.o
x86_64-elf-ld -m elf_i386 -o $# -Ttext 0x1000 $^ --oformat binary
since the author built the program in Mac computer and wanted to output binary from code that runs on x86-64 architecture so he needed x86-64 linker which is x86_64-elf-ld and can be easily installed on Mac. But I do not have Mac, I have x86-64 computer then can I use only ld instead of cross platform ld which is x86_64-elf-ld on authors computer also what is -m flag? does -m is an option and elf_i386 is a value of the option -m, can any one please clarify this. or are they (-m and elf_i386 are two separate things if yes then what they mean?) and is with flag -Ttext can I specify the address of output file? so what exactly above two lines means along with there flags
this is next is
kernel_entry.o: kernel_entry.asm
nasm $< -f elf -o $#
what is -f flag and what is elf in above two lines? in above its output-ing .o $# target file which is executable. with NASM assembly compiler
so in rest of the makefile what are -m32,-b,-f,-ffreestanding,-fda flags and what is xxd program?
These are many questions or someone just direct me to some page where I will not like to be lost in dozens of flags. May concise page where these flags can be covered. Or if some one take time to explain them then very thanks
# $# = target file
# $< = first dependency
# $^ = all dependencies
# First rule is the one executed when no parameters are fed to the Makefile
all: run
# Notice how dependencies are built as needed
kernel.bin: kernel_entry.o kernel.o
x86_64-elf-ld -m elf_i386 -o $# -Ttext 0x1000 $^ --oformat binary
kernel_entry.o: kernel_entry.asm
nasm $< -f elf -o $#
kernel.o: kernel.c
x86_64-elf-gcc -m32 -ffreestanding -c $< -o $#
# Disassemble
kernel.dis: kernel.bin
ndisasm -b 32 $< > $#
mbr.bin: mbr.asm
nasm $< -f bin -o $#
os-image.bin: mbr.bin kernel.bin
cat $^ > $#
run: os-image.bin
qemu-system-i386 -fda $<
echo: os-image.bin
xxd $<
clean:
$(RM) *.bin *.o *.dis
now the only question I have is why I have to resort to using
x86_64-elf-ld linker and x86_64-elf-gcc compiler since the author used
them because he was not developinh makefile and program on x86-64. But
why I have to use them. So question actually is now: can I use only
gcc instead of x86_64-elf-gcc cross compiler and can I use ld as
linker instead of x86_64-elf-ld cross platform linker.
in short, it is recommended to use a cross compiler when developing an os because it:
allows you to leave the current operating system behind, meaning that
no headers or libraries of your host operating system will be used.
You need a cross-compiler for operating system development, otherwise
a lot of unexpected things can happen because the compiler assumes
that your code is running on your host operating system.
for more information see:
Why do I need a Cross Compiler?
GCC Cross-Compiler
hence, even if you are on x86-64 it is still recommended to use a cross compiler, because it will save you a lot of trouble.
as for the linker, on osdev.org they recommend to:
Linking with your compiler rather than ld
You shouldn't be invoking ld directly. Your cross-compiler is able to
work as a linker and using it as the linker allows it control at the
linking stage. This control includes expanding the -lgcc to the full
path of libgcc that only the compiler knows about. If you get weird
errors during compilation, use your cross-compiler for linking and it
may go away. If you do need ld, be sure to use the cross-linker
(i686-elf-ld) rather than the system linker.
see also: https://wiki.osdev.org/Category:FAQ
I encountered some problems when running newlib-linked programs in xv6. (This is the newlib port I have used)
I used this toolchain to compile the newlib. There aren't any problems in compiling and I do get libc.a, libm.a and other library files.
Then I wrote a hello world program and linked it statically against newlib. The program is simply like this:
#include <stdio.h>
int main()
{ printf("hello world\n");
return 0;
}
But the executable generated is too big for the xv6 filesystem (That's a restriction of design), so I stripped it. After stripping the file size is 53k so it's now ok to put it in fs.
When I run "make qemu" I was able to go into the system, and other programs works fine. But when I run my test program, it stuck for a few second and then it says "panic: loaduvm: addr must be page aligned". Is it because I stripped my program, or there are patches or modifications I have to apply to xv6 source code, or some other reasons?
P.S. I'm using up-to-date version of xv6 from it's offical Github repo, and below is the flags I used to compile my test programs:
cc -fno-pic -static -fno-strict-aliasing -fvar-tracking -fvar-tracking-assignments -static-libgcc -nostartfiles -nostdlib -ffreestanding -nodefaultlibs -fno-builtin -m32 -Wall -MD -gdwarf-2 -fno-omit-frame-pointer -fno-stack-protector -I../include/newlib -o build/_test test.c -L../lib/newlib/ -L../lib/libnosys -e main -Ttext 0 -lc -lm -lnosys
The problem has been solved. I forgot to add a "-N" parameter when linking with GCC.
From my understanding, "-N" and "-Ttext 0" flags both keep the compiled programs aligned in 4k when it is loaded into memory, which is required by xv6.
I would like to produce assemblies like the one in the answer of this question Using GCC to produce readable assembly?
for simple test code: test.c
void main(){
int i;
for(i=0;i<10;i++){
printf("%d\n",i);
}
}
gcc command : gcc -g test.c -o test.o
objdump command: objdump -d -M intel -S test.o
But what i got is assemblies starts with .init section
080482bc<_init>: and end with .fini section 080484cc<_fini>
which i do not want them to be shown.
why is this happening ? and how can i avoid showing sections that are not in the source file?
Right now you're creating an executable file and not an object file. The executable file of course contains lot of extra sections.
If you want to create an object file, use the -c flag to GCC.
You can specify sections using -j option.
So objdump -d executable -j .text -j .plt will only show disassembly from .text and .plt sections.
I'm going through a book focusing on x86 programming (Professional Assembly Language, WROX 2005). I had some problems last night and I was hoping to sort this out before returning home today so I can get a running-start and continue the text. My machine runs x64 Ubuntu (11.04 if I'm not mistaken) so the text focusing on 32bit x86 is slightly 'outdated' (I have to add --32 when assembling etc).
I am trying to dynamically link C-library functions with my assembly program but I am unsuccesfull (below commands are from memory).
ld -dynamic-linking /lib/ld-linux.so.2 -o complex -lc complex.o -m elf_i386
Running the above command in Linux gives me the message that it can't understand -lc. Okay, so I removed it.
ld -dynamic-linking /lib/ld-linux.so.2 -o complex complex.o -m elf_i386
I then get the notification that 'printf' is not recognised. The hopes was for the dynamic linker to link to the library but it does not seem to do so. Going to \lib\ I could not locate ld-linux.so.2 (strangely it didn't give me an error on this) but I did locate ld-linux-86-64.so.2. My code is 32bit but I thought what the heck, let's try this:
ld -dynamic-linking /lib/ld-linux-86-64.so.2 -o complex complex.o -m elf_i386
Still it gave the same error that 'call printf' was not recognized.
Need help dynamically linking C library functions with my 32bit Assembly program using 64bit Linux and standard GNU tools.
Sounds like you need to install the 32-bit C-runtime. Under Fedora this is:
yum install glibc-devel.i686
But I don't know the name of the equivalent Ubunutu package; perhaps:
apt-get install libc6-dev-i386
It is almost always a bad idea to try to construct a ld command line yourself. Let GCC do it for you; it automatically handles all sorts of subtleties that you don't want to have to worry about. For a 32-bit program, you do need one special command line switch, -m32:
gcc -m32 -o complex complex.o
If you have more .o files, just stack them up at the end. If you need to link against any system libraries other than libc, put appropriate -lwhatever options after all the object files.
trojanfoe is also correct; the 32-bit toolchain is an optional component. But you need more than just the 32-bit C library. Try this first:
apt-get install gcc-multilib
it should pull in most of what you need.
Try the following order please(suppose your code fil is try.s):
as --32 -g -o try.o try.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -lc -o try try.o
For x86-64 format executable fileļ¼
as -g -o try.o try.s
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -o try try.o
I'm trying to compile a C program under Linux. However, out of curiosity, I'm trying to execute some steps by hand: I use:
the gcc frontend to produce assembler code
then run the GNU assembler to get an object file
and then link it with the C runtime to get a working executable.
Now I'm stuck with the linking part.
The program is a very basic "Hello world":
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
I use the following command to produce the assembly code:
gcc hello.c -S -masm=intel
I'm telling gcc to quit after compiling and dump the assembly code with Intel syntax.
Then I use th GNU assembler to produce the object file:
as -o hello.o hello.s
Then I try using ld to produce the final executable:
ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello
But I keep getting the following error message:
/usr/lib/crt1.o: In function `_start':
(.text+0xc): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x11): undefined reference to `__libc_csu_init'
The symbols __libc_csu_fini/init seem to be a part of glibc, but I can't find them anywhere! I tried linking against libc statically (against /usr/lib/libc.a) with the same result.
What could the problem be?
/usr/lib/libc.so is a linker script which tells the linker to pull in the shared library /lib/libc.so.6, and a non-shared portion, /usr/lib/libc_nonshared.a.
__libc_csu_init and __libc_csu_fini come from /usr/lib/libc_nonshared.a. They're not being found because references to symbols in non-shared libraries need to appear before the archive that defines them on the linker line. In your case, /usr/lib/crt1.o (which references them) appears after /usr/lib/libc.so (which pulls them in), so it doesn't work.
Fixing the order on the link line will get you a bit further, but then you'll probably get a new problem, where __libc_csu_init and __libc_csu_fini (which are now found) can't find _init and _fini. In order to call C library functions, you should also link /usr/lib/crti.o (after crt1.o but before the C library) and /usr/lib/crtn.o (after the C library), which contain initialisation and finalisation code.
Adding those should give you a successfully linked executable. It still won't work, because it uses the dynamically linked C library without specifying what the dynamic linker is. You'll need to tell the linker that as well, with something like -dynamic-linker /lib/ld-linux.so.2 (for 32-bit x86 at least; the name of the standard dynamic linker varies across platforms).
If you do all that (essentially as per Rob's answer), you'll get something that works in simple cases. But you may come across further problems with more complex code, as GCC provides some of its own library routines which may be needed if your code uses certain features. These will be buried somewhere deep inside the GCC installation directories...
You can see what gcc is doing by running it with either the -v option (which will show you the commands it invokes as it runs), or the -### option (which just prints the commands it would run, with all of the arguments quotes, but doesn't actually run anything). The output will be confusing unless you know that it usually invokes ld indirectly via one of its own components, collect2 (which is used to glue in C++ constructor calls at the right point).
I found another post which contained a clue: -dynamic-linker /lib/ld-linux.so.2.
Try this:
$ gcc hello.c -S -masm=intel
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o
$ ./hello
hello, world
$
Assuming that a normal invocation of gcc -o hello hello.c produces a working build, run this command:
gcc --verbose -o hello hello.c
and gcc will tell you how it's linking things. That should give you a good idea of everything that you might need to account for in your link step.
In Ubuntu 14.04 (GCC 4.8), the minimal linking command is:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
/usr/lib/x86_64-linux-gnu/crt1.o \
/usr/lib/x86_64-linux-gnu/crti.o \
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
-lc -lgcc -lgcc_s \
hello.o \
/usr/lib/x86_64-linux-gnu/crtn.o
Although they may not be necessary, you should also link to -lgcc and -lgcc_s, since GCC may emit calls to functions present in those libraries for operations which your hardware does not implement natively, e.g. long long int operations on 32-bit. See also: Do I really need libgcc?
I had to add:
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
because the default linker script does not include that directory, and that is where libgcc.a was located.
As mentioned by Michael Burr, you can find the paths with gcc -v. More precisely, you need:
gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
This is how I fixed it on ubuntu 11.10:
apt-get remove libc-dev
Say yes to remove all the packages but copy the list to reinstall after.
apt-get install libc-dev
If you're running a 64-bit OS, your glibc(-devel) may be broken. By looking at this and this you can find these 3 possible solutions:
add lib64 to LD_LIBRARY_PATH
use lc_noshared
reinstall glibc-devel
Since you are doing the link process by hand, you are forgetting to link the C run time initializer, or whatever it is called.
To not get into the specifics of where and what you should link for you platform, after getting your intel asm file, use gcc to generate (compile and link) your executable.
simply doing gcc hello.c -o hello should work.
Take it:
$ echo 'main(){puts("ok");}' > hello.c
$ gcc -c hello.c -o hello.o
$ ld hello.o -o hello.exe /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o \
-dynamic-linker /lib/ld-linux.so.2 -lc
$ ./hello.exe
ok
Path to /usr/lib/crt*.o will when glibc configured with --prefix=/usr