pass variables into inline assembly ( I am confused)

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
ITchimp
Member
Member
Posts: 134
Joined: Sat Aug 18, 2018 8:44 pm

pass variables into inline assembly ( I am confused)

Post by ITchimp »

I read the tutorial the only way to pass variables into inline assembly is using input and output list

but I came across with early linux context switch code (below) it seems that the variable _current is being passed
into assembly as if it is a variable in C.. notice this is the only place this happens... the rest of the variable is being passed
using the standard extended assembly convention with input list, output list and then followed by a clobbered list...

Code: Select all

#define switch_to(n) {
struct {long a,b;} __tmp;
__asm__("cmpl %%ecx,[color=#0000FF]_current[/color]\n\t"
  "je 1f\n\t"
  "xchgl %%ecx,_current\n\t"
  "movw %%dx,%1\n\t"
  "ljmp %0\n\t"
  "cmpl %%ecx,%2\n\t"
  "jne 1f\n\t"
  "clts\n"
  "1:"
  ::"m" (*&__tmp.a),
  "m" (*&__tmp.b),
  "m" (last_task_used_math),
  "d" _TSS(n),
  "c" ((long) task[n]));
}
Last edited by ITchimp on Fri Apr 10, 2020 6:16 pm, edited 1 time in total.
User avatar
Schol-R-LEA
Member
Member
Posts: 1925
Joined: Fri Oct 27, 2006 9:42 am
Location: Athens, GA, USA

Re: pass variables into inline assembly ( I am confused)

Post by Schol-R-LEA »

Before we discuss the code itself, I would like to ask that, in the future (and in this case too, if you go back and edit the original post), you use put any formatted code samples between a

Code: Select all

[/b] and  [b]
[/b] tag pair, like so:

Code: Select all

#define switch_to(n) {
struct {long a,b;} __tmp;
__asm__("cmpl %%ecx,_current\n\t"
  "je 1f\n\t"
  "xchgl %%ecx,_current\n\t"
  "movw %%dx,%1\n\t"
  "ljmp %0\n\t"
  "cmpl %%ecx,%2\n\t"
  "jne 1f\n\t"
  "clts\n"
  "1:"
  ::"m" (*&__tmp.a),
  "m" (*&__tmp.b),
  "m" (last_task_used_math),
  "d" _TSS(n),
  "c" ((long) task[n]));
}
You can use the 'CODE' button at the top of the editing window to do this automatically.

To address the question, I will point out that switch_to() is a macro rather than a function. This means that when you invoke switch_to(), it is not passing n as a variable on the stack (or in registers, as the case may be), but rather the exact text of the argument is substituted for n in the compiler text stream. If you wrote

Code: Select all

switch_to(2+4);
what the pre-processor would generate would be the code snippet

Code: Select all

struct {long a,b;} __tmp;
__asm__("cmpl %%ecx,_current\n\t"
  "je 1f\n\t"
  "xchgl %%ecx,_current\n\t"
  "movw %%dx,%1\n\t"
  "ljmp %0\n\t"
  "cmpl %%ecx,%2\n\t"
  "jne 1f\n\t"
  "clts\n"
  "1:"
  ::"m" (*&__tmp.a),
  "m" (*&__tmp.b),
  "m" (last_task_used_math),
  "d" _TSS(n),
  "c" ((long) task[2+4]));
I should add that the code as given probably won't work in a modern version of GCC anyway, as it doesn't use line continuations (the backslash, \) at the end of each line to show that it is all part of a single directive. This may simply be an issue with how the code was pasted in, though.

As for the pointers to __tmp.a and __tmp.b, those are passed to the assembler using the inline assembly placeholder m, which is used to indicate that it is a memory operand holding an address. These become the inline assembly variables %0 and %1, respectively.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
ITchimp
Member
Member
Posts: 134
Joined: Sat Aug 18, 2018 8:44 pm

Re: pass variables into inline assembly ( I am confused)

Post by ITchimp »

Thanks a lot for the reply!
I added the block to my old post.

With regard to the /, it seems that there are a few ways to delimit each assembly instruction
such as ; or \n\t

I still have the same question on how "_current" gets passed into the assembly code? is it an
immediate, or register or memory operand?
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: pass variables into inline assembly ( I am confused)

Post by kzinti »

ITchimp wrote:I still have the same question on how "_current" gets passed into the assembly code? is it an
immediate, or register or memory operand?
Looks like a memory operand to me. It is not "passed in", it is simply accessed like any other memory location. The linker will find the symbol ("_current") and patch the instruction at link time with its address.
ITchimp
Member
Member
Posts: 134
Joined: Sat Aug 18, 2018 8:44 pm

Re: pass variables into inline assembly ( I am confused)

Post by ITchimp »

If linker is so smart as to be able to patch in the symbol... then why does gcc's inline assembly have input output list as all?

the related question is if there are any compiler/linker flags that need to be instantiated in order to invoke symbol patching behavior?

in kernel symbols, does underscore _ have any special meanings?
often I see the following behavior, if there is a macro called switch_to
then there will be an declaration of procedure called _switch_to...
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: pass variables into inline assembly ( I am confused)

Post by kzinti »

ITchimp wrote:If linker is so smart as to be able to patch in the symbol... then why does gcc's inline assembly have input output list as all?
Because not everything is a memory operand. Also using a global variable is not always what you want.

You might want to have a function parameter (or some other local variable) as an input operand to your assembly, but you don't have a way of knowing where that variable is stored (is it in a register? on the stack? where on the stack?). So using input operand specifications informs GCC as to what you want. GCC will generate the required code to get the operand you want in the register you want. But in the case of a memory operand (i.e. a global variable), you don't need that since x86 instructions can directly use memory addressing.
ITchimp wrote:the related question is if there are any compiler/linker flags that need to be instantiated in order to invoke symbol patching behavior?
That's just part of the normal work done by a linker. That's in fact one of the main reason why you use a linker. You don't want to manually be calculating memory addresses for operands, so you use symbols that the linker will resolve for you.
ITchimp wrote:in kernel symbols, does underscore _ have any special meanings?
often I see the following behavior, if there is a macro called switch_to
then there will be an declaration of procedure called _switch_to...
In user space, symbols that start with an underscore are usually reserved by the OS / implementation. The reason to use them is to ensure that user program symbols don't conflict with system ones.

In your example, I am guessing it's just a way to differentiate the macro from the actual function. Every code base will have different conventions. it's up to you what you use in your own code. In kernel space, you are king and you decide how things are named.
Octocontrabass
Member
Member
Posts: 5575
Joined: Mon Mar 25, 2013 7:01 pm

Re: pass variables into inline assembly ( I am confused)

Post by Octocontrabass »

The GCC documentation offers this advice:
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. [...] 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.
The Linux code you're looking at is very old. When it was written, it may have been okay to reference global symbols without putting them in the operand list, but current versions of GCC don't support it.
ITchimp
Member
Member
Posts: 134
Joined: Sat Aug 18, 2018 8:44 pm

Re: pass variables into inline assembly ( I am confused)

Post by ITchimp »

A related question on hardware context switching using ljump m32:m16 (task register as segment selector)
is the ljmp instruction atomic? or will it be interrupted because a lot of things are done with it...
nullplan
Member
Member
Posts: 1792
Joined: Wed Aug 30, 2017 8:24 am

Re: pass variables into inline assembly ( I am confused)

Post by nullplan »

ITchimp wrote:A related question on hardware context switching using ljump m32:m16 (task register as segment selector)
is the ljmp instruction atomic? or will it be interrupted because a lot of things are done with it...
All instructions are "interrupt atomic". That is, when an interrupt occurs, the architectural state you can see will be set such that it is consistent. Either rIP is set to the instruction, then it has not completed already, or it is set after the instruction, then it has completed. Special case: If a string instruction with REP prefix is interrupted, rCX, rSI, and rDI may be updated to show a partial execution. The hardware context switch can go wrong, there are many reasons to interrupt it. However, external interrupts are only recognized before or after it. And exceptions tend to be handled sequentially. Only in special cases will a double-fault be issued, even if multiple things are wrong with a TSS.
Carpe diem!
Post Reply