I'm trying to open my Xbox Gamepad at /dev/input/js0 using syscall 56. I have verified the devices' existence at the given path. Including through a test program written in C with open(). However, the program continues to branch FailExit.
.global main
.section .text
.align 2
main:
ldr x0, =GamepadPath
mov x1, #0
mov x8, #56
svc #0
cmp x0, #0
blt FailExit
mov x0, #1
ldr x1, =SuccessMsg
mov x2, #25
mov x8, #64
svc #0
mov x0, #0
mov x8, #93
svc #0
FailExit:
mov x0, #1
ldr x1, =ErrorMsg
mov x2, #25
mov x8, #64
svc #0
mov x0, #0
mov x8, #93
svc #0
.section .data
.section .rodata
GamepadPath: .asciz "/dev/input/js0"
SuccessMsg: .asciz "Gamepad Connected\n\n"
ErrorMsg: .asciz "Gamepad Not Available\n\n"
.section .bss
I've attempt to understand several references including:
What leaves me confused is that Syscall 56, OpenAt's first argument at x0 is suppose to be a return value set by syscall Open, which I cannot find on any reference for ARMv8 Linux.
linux.die.net tends to have outdated versions of the man pages. man7.org is better. Here you can read:
The dirfd argument is used in conjunction with the pathname argument as follows:
• If the pathname given in pathname is absolute, then dirfd is ignored.
This is your situation; /dev/input/js0
is an absolute path (begins with /
).
So just pass whatever you like (e.g. 0 or -1) as the dirfd
first argument to the openat
system call. This means that the pathname becomes the second argument, and the flags the third argument.
Try
mov x0, #-1
ldr x1, =GamepadPath
mov x2, #0
mov x8, #56
svc #0
The value added by openat
is for relative paths like foo/bar/baz
. With normal open
, this always gets resolved relative to the current working directory; so if your current working directory is /blah
, then open("foo/bar/baz")
will always open /blah/foo/bar/baz
. You can get this same behavior from openat
by passing the special value AT_FDCWD
(defined in <fcntl.h>
as -100
) as the first argument. So in your code, if you want to replicate open
when using a relative path, just do mov x0, #-100
and populate x1
, x2
, x3
with the remaining arguments.
But you can also open a directory first and pass its fd instead.
Thus if your current working directory is /blah
but you really want to open relative to /gurgle
, you can do
dirfd = openat(-1, "/gurgle", O_RDONLY | O_DIRECTORY);
fd = openat(dirfd, "foo/bar/baz", O_RDONLY);
This opens /gurgle/foo/bar/baz
.
Using a file descriptor can help avoid race conditions. Suppose you want to create a file /foo/new
but only if /foo/old
already exists in that directory. If you do the obvious
fdold = open("/foo/old", O_RDONLY);
if (fdold >= 0) {
fdnew = open("/foo/new", O_CREAT | O_WRONLY, 0644)
}
then the problem is that in between your two calls to open
, someone could rename and recreate the directory /foo
, or recreate it as a symlink pointing somewhere else. You might then end up creating new
in a directory that doesn't contain old
.
openat
lets you avoid this:
dirfd = openat(-1, "/foo", O_RDONLY | O_DIRECTORY);
fdold = openat(dirfd, "old", O_RDONLY);
if (fdold) {
openat(dirfd, "new", O_CREAT | O_WRONLY, 0644)
}
Now the two files will definitely exist in the same directory, regardless of whether that directory's name has changed from foo
to something else in the meantime.