My puts function is not working in 16-bit code - gcc

I'm writing a bootloader using GCC and a small assembler bootstrap routine. I've written a puts routine that prints a string to the display using BIOS interrupts that doesn't appear to write strings properly.
My bootstrap assembler file boot.s contains:
.code16 .section .text
.extern main
.globl start
start:
mov $0x7c0, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
jmp main
here:
hlt
jmp here
My C code in main.c is:
/*
* A 16 bit bootloader.
*/
void putchar_bios(unsigned char ch);
void set_videomode(unsigned short mode);
void puts(char str[]);
#define set_stack(ss, size) \
{ \
__asm__ __volatile__ ( \
"mov %%ax, %%ss\n" \
"mov $512, %%sp\n" : : "a" (ss), "r" (size)\
); \
}
#define set_videomode(mode) \
{ \
__asm__ __volatile__ ( \
"int $0x10\n" : : "a" (mode) \
); \
}
void putchar_bios(unsigned char ch)
{
__asm__ __volatile__ (
"int $0x10\n" : : "a" (0x0E | ch)
);
}
void puts(char *str)
{
while(*str)
putchar_bios(*str++);
}
void main()
{
set_stack(0x07C0, 512);
set_videomode(0x03);
char name[] = "0001234567890";
puts(name);
//This works fine.
// for(i=0; i<15; i++)
// putchar_bios(name[i]);
while(1);
}
I have successfully done this entirely in assembly, but now I'm trying to migrate it to GCC . I am using a cross-compiler (i386-gcc) and used -m16 flag also. I have used a custom linker script.
OUTPUT_FORMAT("binary");
ENTRY(start);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00) {
*(.text);
}
.data : SUBALIGN(0) {
*(.data);
*(.rodata);
}
.bss : SUBALIGN(4) {
__bss_start = .;
*(.COMMON);
*(.bss)
. = ALIGN(4);
__bss_end = .;
}
__bss_sizel = SIZEOF(.bss)>>2;
__bss_sizeb = SIZEOF(.bss);
/* Boot signature */
.sig : AT(0x7DFE) {
SHORT(0xaa55);
}
}
The script I used to compile, link and run in QEMU are:
i386-elf-gcc -m16 -static -ffreestanding -nostdlib -c boot/boot.s
i386-elf-gcc -m16 -static -ffreestanding -nostdlib -c boot/main.c
i386-elf-ld -T link.ld -o b.bin -nostdlib --nmagic boot.o main.o
dd if=b.bin of=HD.img conv=notrunc
#add some noticable garbage to second sector since I also try to read it next
echo "This is the second sector..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................." | dd seek=512 bs=1 of=HD.img
qemu-system-i386 -hda HD.img # -boot a -s -S
Why does my program not display strings via my puts function properly?

I realized I set the values of segment registers to some garbage (0x7c0) which caused this to happen. I modified my assembly file to zero out the segment registers. The code now looks like:
.code16
.section .text
.extern main
.globl start
start:
xor %ax, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
jmp main
here:
hlt
jmp here
I had expected that the compiler would automatically initialize the segment registers, but it was not.

Related

C assembly pushl not working for WriteFile function

i used C-assembly to call the (_GetStdHandle#4) function to get (output) handle then used (_WriteFile#20) function to write my string on console using handle that i got from (_GetStdHandle#4).
i used (pushl) in my source code for each function to pass the parameters but something's is wrong because (WriteFile)) function return error (6) which is invalid handle but the handle is valid ... so something's wrong with passing argument ... yes ... my problem is passing argument to (_WriteFile) function using (pushl) ... in this code, i used (g) for each argument because there is no reason to move the parameters to register then push the registers ... so i didn't used (r) but if i use (r), the program work without any problem (which mov the parameters to registers first then push the registers (which i want to push the parameters without moving them into the registers)
this code is show nothing and the problem is from (WriteFile) function and if i use (r) for (WriteFile) parameters, the print will be done but why i can't use "g" to not mov the parameters to registers ?
typedef void * HANDLE;
#define GetStdHandle(result, handle) \
__asm ( \
"pushl %1\n\t" \
"call _GetStdHandle#4" \
: "=a" (result) \
: "g" (handle))
#define WriteFile(result, handle, buf, buf_size, written_bytes) \
__asm ( \
"pushl $0\n\t" \
"pushl %1\n\t" \
"pushl %2\n\t" \
"pushl %3\n\t" \
"pushl %4\n\t" \
"call _WriteFile#20" \
: "=a" (result) \
: "g" (written_bytes), "g" (buf_size), "g" (buf), "g" (handle))
int main()
{
HANDLE handle;
int write_result;
unsigned long written_bytes;
GetStdHandle(handle, -11);
if(handle != INVALID_HANDLE_VALUE)
{
WriteFile(write_result, handle, "Hello", 5, & written_bytes);
}
return 0;
}
the Assembly code for this program is :
.file "main.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "Hello\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB25:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
call ___main
/APP
pushl $-11
call _GetStdHandle#4
# 0 "" 2
/NO_APP
movl %eax, 12(%esp)
cmpl $-1, 12(%esp)
je L2
leal 4(%esp), %eax
/APP
pushl $0
pushl %eax
pushl $5
pushl $LC0
pushl 12(%esp)
call _WriteFile#20
# 0 "" 2
/NO_APP
movl %eax, 8(%esp)
L2:
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE25:
.ident "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
what is the problem ?
I would question the need for calling the WINAPI through wrappers like this rather than calling them directly. You can declare prototypes for the stdcall calling convention with
__attribute__((stdcall))
If you don't need to use inline assembly you shouldn't. GCC's inline assembly is hard to get right. Getting it wrong can make the code appear to work until one day it doesn't, especially if optimizations are enabled. David Wohlferd has a good article on why you shouldn't use inline assembly if you don't need to.
The primary problem can be seen in this section of generated code:
pushl $0
pushl %eax
pushl $5
pushl $LC0
pushl 12(%esp)
call _WriteFile#20
GCC has computed the memory operand (handle) for the first parameter as 12(%esp) . The problem is that you have altered ESP with the previous pushes and now offset 12(%esp) is no longer where handle is.
To get around this problem you can pass memory addresses through registers or as immediates (if possible). Rather than use g constraint which includes m (memory constraints), simply use ri for registers and immediates. This prevents memory operands from being generated. If you pass pointers through registers you will also need to add the "memory" clobber.
The STDCALL(WINAPI) calling convention allows a function to destroy EAX, ECX, and EDX (AKA the volatile registers). It is possible that GetStdHandle and WriteFile will clobber ECX and EDX as well as return a value in EAX. You need to ensure that ECX and EDX are listed as clobbers as well (or have a constraint that marks it as output), otherwise the compiler may assume the values in those registers are the same before and after the inline assembly blocks are completed. If they are different it could cause subtle bugs.
With these changes your code could look something like:
#define INVALID_HANDLE_VALUE (void *)-1
typedef void *HANDLE;
#define GetStdHandle(result, handle) \
__asm ( \
"pushl %1\n\t" \
"call _GetStdHandle#4" \
: "=a" (result) \
: "g" (handle) \
: "ecx", "edx")
#define WriteFile(result, handle, buf, buf_size, written_bytes) \
__asm __volatile ( \
"pushl $0\n\t" \
"pushl %1\n\t" \
"pushl %2\n\t" \
"pushl %3\n\t" \
"pushl %4\n\t" \
"call _WriteFile#20" \
: "=a" (result) \
: "ri" (written_bytes), "ri" (buf_size), "ri" (buf), "ri" (handle) \
: "memory", "ecx", "edx")
int main()
{
HANDLE handle;
int write_result;
unsigned long written_bytes;
GetStdHandle(handle, -11);
if(handle != INVALID_HANDLE_VALUE)
{
WriteFile(write_result, handle, "Hello", 5, &written_bytes);
}
return 0;
}
Notes:
I marked the WriteFile inline assembly as __volatile so that the optimizer can't remove the entire inline assembly if it thinks result isn't being used. The compiler doesn't know that a side effect of the function is that the display is updated. Mark the function volatile to prevent the inline assembly from being removed entirely.
GetStdHandle doesn't have a problem with potential memory operands because there are no further uses of constraints after the initial push %1. The problem you are encountering is only an issue when ESP has been modified (via a PUSH/POP or change to ESP directly) and there is a possible use of a memory constraint in that inline assembly afterwards.

Link object files from header files in real mode when using GCC -m16 option?

I would like to implement header files in my c-code which consists partly of GCC inline assembly code for 16 bit real mode but i seem to have linking problems. This is what my header file console.h looks like:
#ifndef CONSOLE_H
#define CONSOLE_H
extern void kprintf(char*);
#endif
and this is console.c:
#include "console.h"
void kprintf(char *string)
{
for(int i=0;string[i]!='\0';i++)
{
asm("mov $0x0e,%%ah;"
"mov $0x00,%%bh;"
"mov %0,%%al;"
"int $0x10"::"g"(string[i]):"eax", "ebx");
}
}
the last one hellworld.c:
asm("jmp main");
#include "console.h"
void main()
{
asm("mov $0x1000,%ax;"
"mov %ax,%es;"
"mov %ax,%ds");
char string[]="hello world";
kprintf(string);
asm(".rept 512;"
"hlt;"
".endr");
}
My bootloader is in bootloader.asm:
org 0x7c00
bits 16
section .text
mov ax,0x1000
mov ss,ax
mov sp,0x000
mov esp,0xfffe
xor ax,ax
mov es,ax
mov ds,ax
mov [bootdrive],dl
mov bh,0
mov bp,zeichen
mov ah,13h
mov bl,06h
mov al,1
mov cx,6
mov dh,010h
mov dl,01h
int 10h
load:
mov dl,[bootdrive]
xor ah,ah
int 13h
jc load
load2:
mov ax,0x1000
mov es,ax
xor bx,bx
mov ah,2
mov al,1
mov cx,2
xor dh,dh
mov dl,[bootdrive]
int 13h
jc load2
mov ax,0
mov es,ax
mov bh,0
mov bp,zeichen3
mov ah,13h
mov bl,06h
mov al,1
mov cx,13
mov dh,010h
mov dl,01h
int 10h
mov ax,0x1000
mov es,ax
mov ds,ax
jmp 0x1000:0x000
zeichen db 'hello2'
zeichen3 db 'soweit so gut'
bootdrive db 0
times 510 - ($-$$) hlt
dw 0xaa55
Now I use the following buildscript build.sh:
#!bin/sh
nasm -f bin bootloader.asm -o bootloader.bin
gcc hellworld.c -m16 -c -o hellworld.o -nostdlib -ffreestanding
gcc console.c -m16 -c -o console.o -nostdlib link.ld -ffreestanding
ld -melf_i386 -Ttext=0x0000 console.o hellworld.o -o hellworld.elf
objcopy -O binary hellworld.elf hellworld.bin
cat bootloader.bin hellworld.bin >disk.img
qemu-system-i386 disk.img
and the linkscript link.ld:
/*
* link.ld
*/
OUTPUT_FORMAT(elf32-i386)
SECTIONS
{
. = 0x0000;
.text : { *(.startup); *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
Unfortunately it isn't working because it doesn't print the expected hello world. I think there must be something wrong with the linking command:
ld -melf_i386 -Ttext=0x0000 console.o hellword.o link.ld -o hellworld.elf`
How do I link header-files in 16-bit mode correctly?
When I write the kprintf function directly in the hellworld.c it is working correctly. I am using Linux Mint Cinnamon Version 18 64 bit for development.
The header files are not really the issue at all. When you restructured the code and split it into multiple objects it has identified issues with how you build and how jmp main is placed into the final kernel file.
I have created a set of files that make all the adjustments discussed below if you wish to test the complete set of changes to see if they rectify your problems.
Although you show the linker script, you aren't actually using it. In your build file you have:
ld -melf_i386 -Ttext=0x0000 console.o hellworld.o -o hellworld.elf
It should be:
ld -melf_i386 -Tlink.ld console.o hellworld.o -o hellworld.elf
When using -c (compiles but doesn't link) with GCC don't specify link.ld as a linker script. The linker script can be specified at link time when you invoke LD. This line:
gcc console.c -m16 -c -o console.o -nostdlib link.ld -ffreestanding
Should be:
gcc console.c -m16 -c -o console.o -nostdlib -ffreestanding
In order for this linker script to locate the jmp main in a place that is first in the output kernel file you need to change:
asm("jmp main");
To:
asm(".pushsection .startup\r\n"
"jmp main\r\n"
".popsection\r\n");
The .pushsection temporarily changes the section to .startup, outputs the instruction jmp main and then restores the section with .popsection to whatever it was before. The linker script deliberately places anything in the .startup section before anything else. This ensures the jmp main (or any other instructions you place there) appear as the very first instructions of the output kernel file. The \r\n can be replaced by ; (semicolon). \r\n makes for prettier output if you ever have GCC generate an assembly file.
As mentioned in the comments of a now deleted question your kernel file exceeds the size of a single sector. When you don't have a linker script, the default one will place the data section after the code. Your code has repeated the hlt instruction so that your kernel is greater than 1 sector (512 bytes) and your bootloader only reads a single sector with Int 13h/AH=2h .
To rectify this remove:
asm(".rept 512;"
"hlt;"
".endr");
And replace it with:
asm("cli;"
"hlt;");
You should be mindful that as your kernel grows you'll need to adjust the number of sectors read in bootloader.asm to ensure all of the kernel is loaded into memory.
I also suggest that to keep QEMU, and other virtual machines happy that you simply generate a well known disk image size and place the bootloader and kernel inside it. Rather than:
cat bootloader.bin hellworld.bin >disk.img
Use this:
dd if=/dev/zero of=disk.img bs=1024 count=1440
dd if=bootloader.bin of=disk.img seek=0 conv=notrunc
dd if=hellworld.bin of=disk.img seek=1 conv=notrunc
The first command makes a zero filled file of 1440kb. This is the exact size of a 1.44MB floppy. The second command inserts bootloader.bin in the first sector without truncating the disk file. The third command places the kernel file into the disk images starting at the second sector on the disk without truncating the disk image.
I had made available a slightly improved linker script. It was amended to remove some of the potential cruft that the linker may insert into the kernel that won't be of much use and specifically identifies some of the sections like .rodata (read only data) etc.
/*
* link.ld
*/
OUTPUT_FORMAT(elf32-i386)
SECTIONS
{
. = 0x0000;
.text : { *(.startup); *(.text) }
.data : { *(.data); *(.rodata) }
.bss : { *(COMMON); *(.bss) }
/DISCARD/ : {
*(.eh_frame);
*(.comment);
*(.note.gnu.build-id);
}
}
Other Comments
Not related to your question but this code can be removed:
asm("mov $0x1000,%ax;"
"mov %ax,%es;"
"mov %ax,%ds");
You do this in bootloader.asm, so setting these segment registers again with the same value won't do anything useful.
You can improve the extended assembly template by using input constraints to pass the values you need via register EAX(AX) and EBX(BX) rather than coding the moves inside the template. Your code could have looked like:
void kprintf(const char *string)
{
while (*string)
{
asm("int $0x10"
:
:"a"((0x0e<<8) | *string++), /* AH = 0x0e, AL = char to print */
"b"(0)); /* BH = 0x00 page #
BL = 0x00 unused in text mode */
}
}
<< is the C bit shift left operator. 0x0e<<8 would shift 0x0e left 8 bits which would be 0x0e00. | is bitwise OR which effectively places the character to print in the lower 8 bits. That value is then passed into the EAX register by the assembly template via input constraint "a".
It is hard to say without knowing what your bootloader.asm does, but:
The link order must be wrong;
ld -melf_i386 -Ttext=0x0000 console.o hellworld.o -o hellworld.elf
should be:
ld -melf_i386 -Ttext=0x0000 hellworld.o console.o -o hellworld.elf
(Edit: I see that you have a linker script which would remove the need for this re-arrangement, but you're not using it for the link).
I suspect that your bootloader loads a single sector, and your padding:
asm(".rept 512;"
"hlt;"
".endr");
... prevents the code from the other object file from ever being loaded, since it pads hellword.o to (more than) the size of a sector.
The problem is nothing to do with the use of header files, it is because you have two compilation units which become separate objects, and the combined size of both when linked is larger than a sector (512 bytes).

Executing bare metal qemu execution with -pflash command

I have a website Hello world for bare metal ARM using QEMU that teaches how to run qemu for versatilePB.
The website example uses -kernel option to load the binary image into 0x10000; I just assume that the binary is loaded into 0x10000 internally with the -kernel.
This is the command qemu-system-arm -M versatilepb -m 128M -kernel test.bin -serial stdio, and the source can be found at - https://dl.dropboxusercontent.com/u/10773282/2014/b1.zip
The ld setup is as follows:
ENTRY(_Reset)
SECTIONS
{
. = 0x10000;
.startup . : { startup.o(.text) }
...
}
Start up assembly is simple as follows:
.global _Reset
_Reset:
LDR sp, =stack_top
BL c_entry
B .
The main c code (c_entry) is as follows:
volatile unsigned int * const UART0DR = (unsigned int *)0x101f1000;
void print_uart0(const char *s) {
while(*s != '\0') { /* Loop until end of string */
*UART0DR = (unsigned int)(*s); /* Transmit char */
s++; /* Next char */
}
}
void c_entry() {
print_uart0("Hello world!\n");
}
I need to modify the code to boot without -kernel, but with -pflash to emulate as if the binary is read from the flash drive. This is my approach in trying to make it work:
Change the startup assembly and test.ld
I just used the other example from the same author of my example: http://balau82.wordpress.com/2010/02/14/simplest-bare-metal-program-for-arm/
This is the startup code:
.section INTERRUPT_VECTOR, "x"
.global _Reset
_Reset:
B Reset_Handler /* Reset */
B . /* Undefined */
B . /* SWI */
B . /* Prefetch Abort */
B . /* Data Abort */
B . /* reserved */
B . /* IRQ */
B . /* FIQ */
Reset_Handler:
LDR sp, =stack_top
BL c_entry
B .
This is the test.ld
ENTRY(_Reset)
SECTIONS
{
. = 0x0;
.text : {
startup.o (INTERRUPT_VECTOR)
*(.text)
}
.data : { *(.data) }
.bss : { *(.bss COMMON) }
. = ALIGN(8);
. = . + 0x1000; /* 4kB of stack memory */
stack_top = .;
}
Update the build code
After the build to get the test.bin, I used the dd command to create a flash binary.
arm-none-eabi-as -mcpu=arm926ej-s -g startup.s -o startup.o
arm-none-eabi-gcc -c -mcpu=arm926ej-s -g test.c -o test.o
arm-none-eabi-ld -T test.ld test.o startup.o -o test.elf
arm-none-eabi-objcopy -O binary test.elf test.bin
dd if=/dev/zero of=flash.bin bs=4096 count=4096
dd if=test.bin of=flash.bin bs=4096 conv=notrunc
qemu execution
Executed qemu to get this error message.
qemu-system-arm -M versatilepb -m 128M -pflash flash.bin -nographic
>> failed to read the initial flash content
>> Initialization of device cfi.pflash01 failed
What might be wrong? I uploaded the examples and sample code.
not working with -pflash: https://dl.dropboxusercontent.com/u/10773282/2014/b2.zip
It seems like that the -M option affects the other option.
I tried with -M connex to use gumstix board, and it works fine.
The other thing that I notice was that with -M versatilepb, I had to use -kernel for loading and running the program.

gcc inline asm embedding pointer to .rodata in .text, x86

I'm trying to embed a pointer to a string in the code section using inline assembler. But gcc is adding a $ to the start of the symbol name, causing a link error.Here is a minimal example,
static const char str[] = "bar";
int main()
{
__asm__ __volatile__
(
"jmp 0f\n\t"
".long %0\n\t"
"0:"
:
: "i" ( str )
);
return 0;
}
building with
gcc -Wall -save-temps test.c -o test
gives the error
test.o: In function `main':
test.c:(.text+0x6): undefined reference to `$str'
looking at the .s temp file, can see the additional $ prepended to str
.file "test.c"
.section .rodata
.type str, #object
.size str, 4
str:
.string "bar"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
#APP
# 4 "test.c" 1
jmp 0f
.long $str
0:
# 0 "" 2
#NO_APP
movl $0, %eax
leave
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
.section .note.GNU-stack,"",#progbits
Think i am doing this the correct way, as the same approach works on ppc gcc,
<clip>
b 0f
.long str
0:
</clip>
Then again, maybe it is just "luck" it works for ppc. Is the issue because $ is used as a prefix for immediates when using the AT&T synax ?
In this simple example, i can work around the issue by hardcoding the symbol name, "str", in the inline assembler, but really need it to be an input constraint to the inline assembler.
Does anyone have any ideas on how to get this working on x86 targets ?
Thanks,
- Luke
The same thing happens using clang, probably because the code generator doesn't know the operand is bing used in a .long rather than as an immediate instruction operand. You code try something like:
const char str[] = "bar";
#define string(str) __asm__ __volatile__ \
( \
"jmp 0f\n\t" \
".long " #str "\n\t" \
"0:" \
)
int main()
{
string(str);
return 0;
}
(I had to remove the "static" on str because the compiler optimized it out as not being referenced.)

Error in my first assembly program (GCC Inline Assembly)

After a lot of internet research I implemented a small assembler routine in my C++ program to get the CPU's L1 cache size using cpuid.
int CPUID_getL1CacheSize() {
int l1CacheSize = -1;
asm ( "mov $5, %%eax\n\t" // EAX=80000005h: L1 Cache and TLB Identifiers
"cpuid\n\t"
"mov %%eax, %0" // eax into l1CacheSize
: "=r"(l1CacheSize) // output
: // no input
: "%eax" // clobbered register
);
return l1CacheSize;
}
It works perfectly on Windows 7 64 bit with MinGW (GCC, G++). Next I tried this on my Mac computer using GCC 4.0 and there must be an error somewhere because my program shows strange strings in the ComboBoxes and some signals cannot be connected (Qt GUI).
This is my first assembler program, I hope someone can give me a hint, Thanks!
I think that CPUID actually clobbers EAX, EBX, ECX, EDX, so it's probably just a register trashing problem. The following code appears to work OK with gcc 4.0.1 and 4.2.1 on Mac OS X:
#include <stdio.h>
int CPUID_getL1CacheSize()
{
int l1CacheSize = -1;
asm ( "mov $5, %%eax\n\t" // EAX=80000005h: L1 Cache and TLB Identifiers
"cpuid\n\t"
"mov %%eax, %0" // eax into l1CacheSize
: "=r"(l1CacheSize) // output
: // no input
: "%eax", "%ebx", "%ecx", "%edx" // clobbered registers
);
return l1CacheSize;
}
int main(void)
{
printf("CPUID_getL1CacheSize = %d\n", CPUID_getL1CacheSize());
return 0;
}
Note that you need to compile with -fno-pic as EBX is reserved when PIC is enabled. (Either that or you need to take steps to save and restore EBX).
$ gcc-4.0 -Wall -fno-pic cpuid2.c -o cpuid2
$ ./cpuid2
CPUID_getL1CacheSize = 64
$ gcc-4.2 -Wall -fno-pic cpuid2.c -o cpuid2
$ ./cpuid2
CPUID_getL1CacheSize = 64
$
I finally resolved the problem. I got a compiler error while playing around: "error: PIC register '%ebx' clobbered in 'asm'" and after some internet research I modified my code to:
int CPUID_getL1CacheSize() {
int l1CacheSize = -1;
asm volatile ( "mov $5, %%eax\n\t"
"pushl %%ebx; cpuid; popl %%ebx\n\t"
"mov %%eax, %0"
: "=r"(l1CacheSize)
:
: "%eax"
);
return l1CacheSize;
}
Thanks Paul, The compiler option -fno-pic is a nice solution too.
Greetings

Resources