6

When I'm reading the xv6 source code, I'm confused about the syntax of the declaration below. Can anyone explain it to me?

extern struct cpu *cpu asm("%gs:0");
Peter Cordes
  • 245,674
  • 35
  • 423
  • 606
xiaoma
  • 83
  • 4
  • 2
    `asm` after a variable declaration usually allows you to specify the variable's name for linkage purposes, but in this case, you're not specifying a name at all, but rather a register reference of sorts. – Kerrek SB Dec 21 '17 at 02:30
  • Next time - please Google first! – Antti Haapala Dec 21 '17 at 07:20

2 Answers2

9

I assume you understand what extern struct cpu *cpu means. The question you have is: What does the asm("%gs:0") part mean?

This code is using a gcc extension called asm labels to say that the variable cpu is defined by the assembler string %gs:0.

This is NOT how this extension is intended to be used and is considered a hack.

There's an excellent discussion of gs (and fs) here, but in short gs points to the current thread's local storage. The format of the data at gs depends on your OS (Windows is very different than Linux). This particular code is saying that at offset 0 from gs, there is a pointer to a struct cpu.

Community
  • 1
  • 1
David Wohlferd
  • 6,158
  • 2
  • 24
  • 45
  • See [discussion on a recent duplicate](https://stackoverflow.com/questions/47917027/what-does-asm-suffix-mean-in-given-code/47917087?noredirect=1#comment82802441_47917087) for cases where the hack either doesn't compile+assemble (32-bit PIC code, or any time the compiler tries to `mov` the address to a register as an immediate), or worse where it compiles but uses the wrong address (64-bit code using RIP-relative addressing) – Peter Cordes Dec 21 '17 at 22:21
9

It is a special case of an asm label. It instructs the compiler to emit %gs:0 instead of the usual symbol name if you reference the cpu variable. Presumably %gs has been previously set up as a per-cpu storage area, with a struct cpu pointer at offset zero. The purpose is to allow each cpu to access its own data.

Jester
  • 52,795
  • 4
  • 67
  • 108
  • 1
    so this just calls memory that is at 0 bytes offset in segment referenced by gs 'cpu' ? – Zhani Baramidze Dec 21 '17 at 02:34
  • I had glanced at the question a bit too quickly to notice how it was being used. – Michael Petch Dec 21 '17 at 02:36
  • 4
    I would say this is still somewhat of an abuse of the feature. I have a feeling it could make the compiler generate invalid code under circumstances. – Jester Dec 21 '17 at 02:38
  • @Jester, I tend to agree. I think there are better ways to go about this, I was laying around to see if I could find a case where this fails (other than what is documented). – Michael Petch Dec 21 '17 at 02:44
  • 1
    It fails for taking the address, but you probably wouldn't do that knowing that it's not in the same zero based segment. Now, let's see if we can make the compiler take the address as part of an optimization. – Jester Dec 21 '17 at 02:45
  • 3
    Can't use it with position independent code.in 32-bit code. It will attempt to generate instructions that look like this `%gs:0@GOT(%ebx)` which of course isn't valid. Not an issue with 64-bit code since it will just use RIP relative addressing since the outputted instruction would be ` %gs:0(%rip)` – Michael Petch Dec 21 '17 at 02:51
  • 1
    @MichaelPetch: It compiles in 64-bit mode, but uses a RIP-relative addressing mode like `movq %gs:0(%rip), %rax`. https://godbolt.org/g/jSvB2g (Godbolt doesn't enable `-pie` by default, so this is plain position-dependent code. disassembly output shows it really is using a rel32 = 0, not using an offset of `-RIP` to encode an absolute address of 0. i.e. **the linear address is GS_base + RIP**) – Peter Cordes Dec 21 '17 at 03:03
  • 1
    It could fail when optimizing for space, since if multiple loads of `cpu` are necessary, it saves space to load the address of `cpu` into register. Neither GCC nor clang appear to be smart enough to do this but ICC will: https://godbolt.org/g/JkVTW5 – Ross Ridge Dec 21 '17 at 03:45
  • It also breaks `-masm=intel`, unlike normal symbol names. And it doesn't seem to build with clang at all, because clang really does use it as a symbol name. (You'd probably have to tell clang not to use its built-in assembler, so the asm has to go through a plain text format. But clang on godbolt includes literal `""`, so that probably just totally breaks (or get GAS to look for `%gs:0` as a symbol name?) – Peter Cordes Dec 21 '17 at 04:15
  • @PeterCordes: `GS_base+RIP` is the wrong address. It should be just `GS_base`. – R.. GitHub STOP HELPING ICE Dec 21 '17 at 04:40
  • @R..: That's exactly my point. Using `... var asm("%gs:0")` in 64-bit code leads to code that compiles, but loads it from the wrong address even with `-fPIC`. – Peter Cordes Dec 21 '17 at 04:51
  • Slow mods, I've flagged this A for migration to the other 4 days ago, pfft christmas. – Antti Haapala Dec 25 '17 at 18:03
  • 1
    @AnttiHaapala Merger flags usually are delayed because merging posts is irreversible. It does need someone who has some knowledge in the tag to perform the merger. (I did see your flag a few hours after you created it, but decided to move on). That said, as there's only one answer here, if Jester also agrees with the merger, then I can merge them rightaway. (or else, you'll need to wait till another mod sees the flag.) – Bhargav Rao Dec 29 '17 at 07:32
  • 1
    Have become a bit more busy @Antti, have enough time to moderate, but not enough to [chat](https://chat.stackoverflow.com). Hopefully, I'll get back to the normal 18hrs a day on Stack Overflow, in a few months. :p – Bhargav Rao Dec 29 '17 at 07:43