In an attempt to get the Interrupt Descriptor Table's data using the following code:
/* SIDT returns IDT in following format */
unsigned short IDTLimit;
unsigned short LowIDTBase;
unsigned short HighIDTBase
s_idt_info idt_info; // returned by sidt
s_idt_entry *idt_entries; // obtained from idt_info
unsigned long count;
// load idt_info
__asm ("sidt idt_info");
||=== Build: Release in driver2 (compiler: gnu_64) ===|
obj\Release\driver.o:driver.c|| undefined reference to `idt_info'|
||error: ld returned 1 exit status|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
GCC doesn't support local variables (inside a function) within inline assembly unlike MSVC. If the variable
idt_info were made global then it should be visible to the inline assembly, however the GCC documentation discourages this:
Accessing data from C programs without using input/output operands (such as by using global symbols directly from the assembler template) may not work as expected. Similarly, calling functions directly from an assembler template requires a detailed understanding of the target assembler and ABI.
Since GCC does not parse the assembler template, it has no visibility of any symbols it references. This may result in GCC discarding those symbols as unreferenced unless they are also listed as input, output, or goto operands.
What you want to do is use a GCC extended assembler template and use an output constraint to allow GCC to pass in the address of
idt_info that will be updated by SIDT. Something like this should work:
__asm__ __volatile__ ("sidt %[idtptr]" : [idtptr]"=m"(idt_info));
= denotes the constraint will be used for output,
m forces GCC to pass a memory operand. SIDT requires a memory operand and not a register.
%[idtptr] is the name given to the parameter, and GCC will replace it with the actual memory reference.