'Porting AT&T inline-asm inb / outb wrappers to work with gcc -masm=intel

I am currently working on my x86 OS. I tried implementing the inb function from here and it gives me Error: Operand type mismatch for `in'.

This may also be the same with outb or io_wait.

I am using Intel syntax (-masm=intel) and I don't know what to do.

Code:

#include <stdint.h>
#include "ioaccess.h"

uint8_t inb(uint16_t port)
{
    uint8_t ret;
    asm volatile ( "inb %1, %0"
                   : "=a"(ret)
                   : "Nd"(port) );
    return ret;
}

With AT&T syntax this does work.


For outb I'm having a different problem after reversing the operands:

void io_wait(void)
{
    asm volatile ( "outb $0x80, %0" : : "a"(0) );
}

Error: operand size mismatch for `out'



Solution 1:[1]

It's possible to writing code that works with or without -masm=intel, using dialect alternatives for GNU C inline asm https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html (This is a good idea for headers that other people might include.)

It works like "{at&t stuff | intel stuff}": the compiler picks which side of the | to keep based on the current mode.

The major difference between AT&T vs. Intel syntax is that the operand-list is reversed, so usually you have something like "inb {%1,%0 | %0,%1}".

This is a version of @MichaelPetch's nice functions using dialect alternatives:

// make this a header: these single instructions can inline more cheaply
// than setting up args for a function call
#include <stdint.h>

static inline
uint8_t inb(uint16_t port)
{
    uint8_t ret;
    asm volatile ( "inb {%1, %0 | %0, %1}"
                   : "=a"(ret)
                   : "Nd"(port) );
    return ret;
}

static inline
void outb(uint16_t port, uint8_t byte)
{
    asm volatile ( "outb {%1, %0 | %0, %1}"
                   :
                   : "a"(byte),
                     "Nd"(port) );
}

static inline
void io_wait(void) {
    outb (0x80, 0);
}

The Linux/Glibc sys/io.h macros sometimes use %w1 to expand a constraint to the 16-bit register name, but using types of the right size also works.

If you want a memory-barrier version of these to take advantage of the fact that in / out are (more or less) serializing like a locked instruction or mfence, add a "memory" clobber to stop compile-time reordering of memory access across it.

If a port I/O can trigger a DMA read of some other memory that you wrote recently, you might also need a "memory" clobber for that. (x86 has cache-coherent DMA so you wouldn't have had to explicitly flush it, but you can't let the compiler reorder it after an outb, or even optimize away an apparently dead store.)


There's no support in GAS for saving the old mode, so using .intel_syntax noprefix inside your inline asm leaves you no way know whether to switch back to .att_syntax or not.

But that wouldn't usually be sufficient anyway: you need to get the compiler to format operands in ways that match the syntax mode when filling in a template. e.g. the port number needs to expand to $imm or %dx (AT&T1) vs. dx or imm without the $ prefix.

Or for a memory operand, [rdi + rax*4 + 8] or 8(%rdi, %rax, 4).

But you still need to take care of reversing the operand list with { | } yourself; the compiler doesn't try to do that for you. It's purely a text-substitution into the template according to simple rules.

Footnote 1: AT&T disassembly by objdump -d bizarrely uses (%dx) for the port number in the non-immediate form, but GAS accepts %dx or (%dx) on input, so an "Nd" constraint is usable, simply expanding to the bare register name.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1