0

I run the following ccall's:

status = ccall((:ioperm, "libc"), Int32, (Uint, Uint, Int32), 0x378, 5, 1)
ccall((:outb, "libc"), Void, (Uint8, Uint16), 0x00, 0x378)

After the second ccall I receive the following Error message:

ERROR: ccall: could not find function outb in library libc
 in anonymous at no file
 in include at ./boot.jl:245
 in include_from_node1 at loading.jl:128
 in process_options at ./client.jl:285

After some research and messing around I found the following information:

  1. ioperm is in libc, but outb is not
  2. However, both ioperm and outb are defined in the same header file <sys/io.h>
  3. An equivalent version of C code compiles and runs smoothly.
  4. outb in glibc, however on the system glibc is defined as libc
  5. Same problem with full path names /lib/x86_64-linux-gnu/libc.so.6

EDIT:

Thanks for the insight @Employed Russian! I did not look closely enough to realize the extern declaration. Now, all of my above notes make total sense!

Great, we found that ioperm is a libc function that is declared in <sys/io.h>, and that outb is not in libc, but is defined in <sys/io.h> as a volatile assembly instruction.

Which library, or file path should I use?

Implementation of ccall.

The Brofessor
  • 1,008
  • 6
  • 15

1 Answers1

2

However, both ioperm and outb are defined in the same header file <sys/io.h>

By "defined" you actually mean "declared". They are different. On my system:

extern int ioperm (unsigned long int __from, unsigned long int __num,
                   int __turn_on) __attribute__ ((__nothrow__ , __leaf__));

static __inline void
outb (unsigned char __value, unsigned short int __port)
{
  __asm__ __volatile__ ("outb %b0,%w1": :"a" (__value), "Nd" (__port));
}

It should now be obvious why you can call ioperm but not outb.

Update 1

I am still lost as to how to correct the error.

You can't import outb from libc. You would have to provide your own native library, e.g.

void my_outb(unsigned char value, unsigned short port) {
  outb(value, port);
}

and import my_outb from it. For symmetry, you should probably implement my_ioperm the same way, so you are importing both functions from the same native library.

Update 2

Making a library worked, but in terms of performance it is horrible.

I guess that's why the original is implemented as an inline function: you are only executing a single outb instruction, so the overhead of a function call is significant.

Unoptimized python does x5 better.

Probably by having that same outb instruction inlined into it.

Do you know if outb exist in some other library, not in libc

That is not going to help: you will still have a function call overhead. I am guessing that when you call the imported function from Julia, you probably execute a dlopen and dlsym call, which would impose an overhead of additional several 100s of instructions.

There is probably a way to "bind" the function dynamically once, and then use it repeatedly to make the call (thus avoiding repeated dlopen and dlsym). That should help.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • missed the `extern`! While this solves all my confusion, I am still lost as to how to correct the error. – The Brofessor Aug 13 '15 at 08:28
  • Making a library worked, but in terms of performance it is horrible. Unoptimized python does x5 better. Do you know if `outb` exist in some other library, not in `libc`? – The Brofessor Aug 14 '15 at 19:10
  • Presumably you are not actually writing just a single byte, and what you want is a vectorized function that outputs a whole array of bytes at once. Write this in C (using outb) and call it from Julia and then the function-call overhead will be negligible for a large enough vector. – SGJ Aug 26 '15 at 16:08
  • To do this in pure Julia, you should probably use Base.llvmcall to emit the x86 outb instruction via an LLVM intrinsic. This way the instruction can be inlined. (But it requires you to know some low-level asm and llvm stuff.) – SGJ Aug 26 '15 at 16:10