I'm developing my own operating system. I have completed the boot sector and loaded my kernel successfully. My development environment is Ubuntu 14.04 and GCC 4.8. My kernel will run under BOCHS. I wanted to print a specific character in a specific position on the screen, so I created a function like this:
void print_char(char target, int col, int row, unsigned char attribute) {
//attribute controls the colour of the character
char * vidmem = (char *) VIDEO_ADDRESS; //#define VIDEO_ADDRESS 0xb8000
*vidmem = target;
//TODO: add other control statements
}
I call this function in my main function, which is the entry point of my kernel:
print_char('T', 0, 0, (unsigned char) 0x0f);
The code above is supposed to print the character 'T' at the top-left of the screen. It does't show up! After I changed the declaration of the print_char:
void print_char(char target, int col, int row, int attribute)
Or something like this:
void print_char(char target, int col, int row) then call it like print_char('T', 0, 0)
After I change it, everything works! I am really confused about this problem. Can any one explain it to me?
I have fix the problem by modify the flags of gcc and ld to generate a 32-bit .o file and '.bin' file (My ubuntu is a 64-bit version) by the following statemens.
%.o: %.c ${HEADERS}
gcc -ffreestanding -m32 -c $< ${CFLAG} -o $#
kernel.bin: kernel/kernel_entry.o ${OBJ}
ld -o $# -melf_i386 -Ttext 0x1000 $^ --oformat binary
where kernel_entry.o will make sure our routine finds the entry of main function in kernel code. then I get my os image by:
os-image: boot/boot_sect.bin kernel.bin
cat $^ > os-image
where boot_sect.bin plays the role of a boot sector.
But I still don't know why the 64-bit objective file will cause the phenomena I described above...
Related
I'd like to compile a set of functions into a flat library for lack of a better term. There are a bunch of functions like
// add.c
int add (int a, int b) {
return a + b;
}
// multiply.c
int multiply (int a, int b) {
int result = 0;
if (a >= 0)
for (; a > 0; --a) result = add(result, b);
else
for (; a < 0; ++a) result = add(result, b);
return result;
}
// double.c
int two = 2;
int double_ (int x) {
return multiply(x, two);
}
and the compiled binary shall have
no main or __start entry points (it's a library, not an executable),
only instructions and data, no headers,
position-independent code,
no external dependencies (I'm not using any external libraries, but GCC appears to always include standard library stuff, which I don't need), and
little to no padding (i.e. no excessive amounts of null bytes for page/sector alignment)
And to be able to call the functions from outside the binary I either need to know their offsets from the beginning of the binary, or have a jump table at the beginning of the binary.
Using GCC points 3 and 4 can probably be achieved with -fPIC and -nostdlib. And if the functions were independent of each other I could achieve 5. by simply compiling the files separately and concatenating them manually which would also give me the function offsets, but here the functions are not independent of each other, so I rely on GCC to stich together the functions with minimal padding. For point 2 there is probably some objcopy --oformat binary trick or something similar. But I have no clue how to get point 1 to work. So far every single guide I've found online is for compiling custom/"hello world" kernels all of which are executables and have entry points. And if I don't provide an entry point ld complains that the symbol __start cannot be found. Furthermore, I don't know how to get the function offsets of the compiled binary or how to tell GCC to include a jump table (whichever of the two is possible).
Any ideas on how to compile the example above so that the compiled binary satisfies points 1 through 5 and is callable from outside the binary (either by offsets or via a jump table at the beginning)?
After realizing that my requirements kinda look like compiling stuff for embedded devices with little storage, I looked up how firmware is compiled for embedded devices and found out that the linker can be finely tuned with linker scripts. I ended up writing my own linker script that looks like this:
// File: link.ld
OUTPUT(test.bin);
OUTPUT_FORMAT(binary);
SECTIONS {
.text 0 : {
add.o(.text);
multiply.o(.text);
double.o(.text);
}
/DISCARD/ : {
*(*)
}
}
Now, I can compile my source code with
gcc -c -fPIC -nostartfiles -nostdlib add.c multiply.c double.c
ld -M -T link.ld
where the first line compiles (-c) the source code into position-independent (-fPIC) object files (*.c -> *.o) without standard library (-nostartfiles -nostdlib), and the second line basically takes the .text sections of the object files and concatenates them, and prints out (-M) the section layout of the output file including the offsets of all symbols.
I tried to use below sample bootloader from Internet.
__asm__(".code16gcc\n");
__asm__ ("jmpl $0, $main\n");
#define __NOINLINE __attribute__((noinline))
#define __REGPARM __attribute__ ((regparm(3)))
#define __NORETURN __attribute__((noreturn))
/* BIOS interrupts must be done with inline assembly */
void __NOINLINE __REGPARM print(const char *s){
__asm__ __volatile__("movb %0 , %%al\n" : :"c"(*s):"memory");
//__asm__ __volatile__("movb $'W' , %al\n");
__asm__ __volatile__("movb $0x0e, %ah\n");
__asm__ __volatile__("int $0x10\n");
}
/* and for everything else you can use C! Be it traversing the filesystem, or verifying the kernel image etc.*/
void __NORETURN main(){
__asm__ __volatile__ ("movb $0x0, %al\n");
print("woo hoo!\r\n:)");
while(1);
}
For linking below basic linker script used.
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
*(.text);
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
}
I prepared a binary bootable image and copied to floppy using below commands.
gcc -c -Os -g -march=i686 -ffreestanding -Wall -Werror -I. test.c -o test.o
ld -m elf_i386 -static -Ttest.ld -nostdlib --nmagic -o test.elf test.o
objcopy -O binary test.elf test.bin
sudo dd if=test5.bin of=/dev/fd0
As we can easily notice I am trying to print character 'w' only, from the string "woo hoo!" passed to print() function.
But when I tried to boot it on Virtual Machine, it did not print anything.
However if I comment 1st statement of print() and uncomment second statement, character 'W' is printed as below, which proves compilation and boot stamp are all fine. This is a successful test case using movb $'W' , %al:
I tried my best searching questions for Linker script, gcc options, inline assembly and real vs virtual machine, but could not fix this problem.
Also it could be problem of how static variables are saved in case of boot-loader which I am yet unaware of.
I met a problem when inspecting the local variables of user space application in systemtap.
I write a test.c like this:
#include <stdio.h>
int func(int *p, int val)
{
printf("p=%p val=%d\n", p, val);
return 1;
}
int main()
{
int a = 7;
func(&a, a);
return 0;
}
and compile it with -g
# gcc -g -o test test.c
Systemtap can see the variable of func(): p and val
# stap -L 'process("./test").function("func")'
process("/home/ryan/Public/test").function("func#/home/ryan/Public/test.c:3") $p:int* $val:int
So I use this stp to watch the variables:
# stap -e 'probe process("./test").function("func") {printf("%s(%p, %d)\n", probefunc(), $p, $val)}'
But the local variables are not right in the result when test program executed, it shows:
func(0x0, 0)
I am using fedora19 with:
kernel-3.11.9-200.fc19.x86_64
systemtap-sdt-devel-2.3-1.fc19.x86_64
systemtap-2.3-1.fc19.x86_64
systemtap-client-2.3-1.fc19.x86_64
systemtap-devel-2.3-1.fc19.x86_64
systemtap-runtime-2.3-1.fc19.x86_64
gcc-4.8.2-7.fc19.x86_64
Could someone meet this problem or give me a solution?
.function probes are defined to fire at entry to the function. If you're looking for values of local variables, you need to use .statement probes, identifying the source-file:line-number. In this case though, you're looking for parameters to a function (which happened to be based on another function's locals). In this case, the .function probe is appropriate.
You appear to be hitting a GCC bug. In plain -g mode (ironically), dwarf debuginfo is sometimes inaccurate for incoming function parameters. Try "gcc -g -O" or "gcc -g -O2" instead. Systemtap prologue-searching (stap -P) might help. See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51358, https://sourceware.org/bugzilla/show_bug.cgi?id=13420
In case the "stap -P" doesn't help, you may need to resort to statement-level probing after all:
probe process("./test").statement("func#test.c:5") { println($$parms) }
(line :5 refers to the printf)
I'm using GCC and the NVIDIA implementation of OpenCL, and online compilation instead of offline compilation.
I use this list to check which is the error I have. But nevertheless if I have an error inside my kernel the only information I have is an error value -48.
My question is: Is there a way to display the exact kernel compilation error?
If a semicolon is missing, or I have a wild pointer I would like to read so, instead of just a -48 error. Otherwise the development time is getting too slow.
I add also my Makefile:
CC=gcc
FILE=main
all:
$(CC) -c -Wall -I /usr/local/cuda/include/ $(FILE).c -o $(FILE).o
$(CC) $(FILE).o -o $(FILE) -L /usr/local/cuda/lib64/ -l OpenCL
clean:
$(RM) $(FILE) $(FILE).o
In C++, do something like:
int ErrorCode = 0;
cl_program P;
cl_device_id D;
size_t LogSize;
cl_build_status BuildStatus;
//Configure OpenCL
//Load Program Here
//Compile Program here
//Check the status of compilation
ErrorCode = clGetProgramBuildInfo(P, D, CL_PROGRAM_BUILD_STATUS, NULL, NULL, &BuildStatus);
if(BuildStatus == CL_BUILD_ERROR){
//Fetch Error
ErrorCode = clGetProgramBuildInfo(P, D, CL_PROGRAM_BUILD_LOG, NULL, NULL, &LogSize);
char Log = new Log[LogSize]; //Or use Malloc if in C
ErrorCode = clGetProgramBuildInfo(P, D, CL_PROGRAM_BUILD_LOG, LogSize, Log, NULL);
//Display Error Code here, close out OpenCL, try again, etc
}
I'm trying to compile a binary file into a MACH_O object file so that it can be linked it into a dylib. The dylib is written in c/c++.
On linux the following command is used:
ld -r -b binary -o foo.o foo.bin
I have tried various option on OSX but to no avail:
ld -r foo.bin -o foo.o
gives:
ld: warning: -arch not specified
ld: warning: ignoring file foo.bin, file was built for unsupported file format which is not the architecture being linked (x86_64)
An empty .o file is created
ld -arch x86_64 -r foo.bin -o foo.o
ld: warning: ignoring file foo.bin, file was built for unsupported file format which is not the architecture being linked (x86_64)
Again and empty .o file is created. Checking the files with nm gives:
nm foo.o
nm: no name list
The binary file is actually, firmware that will be downloaded to an external device.
Thanks for looking
Here's the closest translation to the Linux linker command to perform binary embedding with the OSX linker:
touch stub.c
gcc -o stub.o -c stub.c
ld -r -o foo.o -sectcreate binary foo_bin foo.bin stub.o
foo.bin will be stored in segment binary, section foo_bin (both names are arbitrary but chosen to mimic GNU ld for ELF on Linux) of the foo.o object.
stub is necessary because ld refuses to create just a custom segment/section. You don't need it if you link directly with a real code object.
To get data back from the section, use getsectbyname (struct is defined in mach-o/loader.h):
#include <mach-o/getsect.h>
const struct section_64 *sect = getsectbyname("binary", "foo_bin");
char *buffer = calloc(1, sect->size+1);
memcpy(buffer, sect->addr, sect->size); // whatever
or getsectdata:
#include <mach-o/getsect.h>
size_t size;
char *data = getsectdata("binary", "foo_bin", &size);
char *buffer = calloc(1, size+1);
memcpy(buffer, data, size); // whatever
(I used it to store text data, hence the stringification via calloc zeroing of size+1 plus blob copying)
Warning: Since 10.7, ASLR got stronger and messes badly with getsect* functions, resulting in segfaults. set disable-aslr off in GDB before running to reproduce EXC_BAD_ACCESS (SIGSEGV) in debug conditions. People had to jump through inordinate hoops to find the real address and get this working again.
A simple workaround is to get the offset and size, open the binary and read the data straight from disk. Here is a working example:
// main.c, build with gcc -o main main.c foo.o
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <mach-o/getsect.h>
int main() {
// finding the filename of the running binary is left as an exercise to the reader
char *filename = "main";
const struct section_64 *sect = getsectbyname("binary", "foo_bin");
if (sect == NULL) {
exit(1);
}
char *buffer = calloc(1, sect->size+1);
int fd = open(filename, O_RDONLY);
if (fd < 0) {
exit(1);
}
lseek(fd, sect->offset, SEEK_SET);
if (read(fd, buffer, sect->size) != sect->size) {
close(fd);
exit(1);
}
printf("%s", buffer);
}