-1

I have format ELF64 executable 3 at top of my source file.

I compiled my program using fasm main.asm

Output:

flat assembler  version 1.73.13  (16384 kilobytes memory, x64)
3 passes, 319 bytes.

Then I tried to run it using strace ./main, because it didn't work as expected and in output there is strace: [ Process PID=3012310 runs in 32 bit mode. ].

file main: main: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, no section header

uname -m: x86_64

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
DeBos
  • 176
  • 1
  • 13
  • That should not happen unless maybe your program itself is executing some other program and that is what's running in 32 bit mode. Of course that should be clear from strace output. You did not show any code, but what I tested works. – Jester Nov 01 '19 at 23:45
  • My program is not executing any other program. There are only basic syscalls i.e. `read`, `write`, `open`, `close` and `exit`. – DeBos Nov 01 '19 at 23:47
  • Is the 32 bit message at the start of the strace output or do you have some syscalls logged before that? – Jester Nov 01 '19 at 23:48
  • Under this message there is only `restart_syscall` and `exit(1)`. – DeBos Nov 01 '19 at 23:51
  • 1
    Show your code, does it include `BITS` anywhere? – David C. Rankin Nov 01 '19 at 23:52
  • By any chance are you using `int 0x80` (this is part of the compatibility layer on 64-bit versions of Linux built with IA32_COMPAT) to make system calls rather than `syscall` (The preferred mechanism to make 64-bit system calls? – Michael Petch Nov 01 '19 at 23:55
  • @DavidC.Rankin It doesn't include `BITS` anywhere. Like I said in question, program is compiled as 64-bit, I'm **not** using any 32-bit registers, but still when I `mov rax, 0` it runs `restart_syscall` (32-bit behavior) instead of `read` (64-bit behavior). – DeBos Nov 01 '19 at 23:55
  • @MichaelPetch I'm using `int 0x80`. Is it only for 32-bit asm or both? – DeBos Nov 01 '19 at 23:57
  • [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code) TL;DR: don't. – Jester Nov 01 '19 at 23:58
  • Int 0x80 goes through the IA32_COMPAT layer. It is allowed in 64-bit code (if the Linux kernel is built with IA32_COMPAT support) but it only looks at the lower 32-bits of a 64-bit register. To make 64-bit system calls use the `syscall` instruction. Ryan Chapman has the Linux 64-bit system calls documented here in a table: https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ – Michael Petch Nov 01 '19 at 23:59
  • `int 0x80` is the x86 kernel interrupt call... `syscall` is the x86_64 call. – David C. Rankin Nov 01 '19 at 23:59
  • I checked now and it works with `syscall`. Thanks a lot everyone. – DeBos Nov 02 '19 at 00:01
  • Be aware that the system call numbers are different between int 0x80 and syscall and the parameters are passed in different registers. – Michael Petch Nov 02 '19 at 00:02
  • I know about different numbers and registers. I only didn't know that I have to use something different than `int 0x80` for system call. – DeBos Nov 02 '19 at 00:04
  • I can't tell that because you didn't present any code. When I was first to suggest `int 0x80` vs `syscall` I had to make an educated guess in the absence of seeing what calls you are making and what registers you used to pass things. – Michael Petch Nov 02 '19 at 00:12
  • So, the program isn't actually running in 32-bit mode; presumably `strace` just assumes (wrongly) that any program using `int 0x80` is a 32-bit program. – Nate Eldredge Nov 02 '19 at 00:36
  • @DeBos99: you don't need to avoid 32-bit registers in 64-bit mode. In fact you should *prefer* them except for things that need to be 64-bit (sometimes pointers). [The advantages of using 32bit registers/instructions in x86-64](//stackoverflow.com/q/38303333) – Peter Cordes Nov 02 '19 at 02:40

2 Answers2

2

Use syscall instead of int 0x80

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
0

strace is wrong, your process isn't actually running in 32-bit mode, just using the 32-bit int 0x80 system call ABI.

You can check with gdb ./main and use starti. info regs will show that the register state is 64-bit, including 16x 64-bit registers, not 8x 32-bit registers. Or more simply, layout reg.


I see the same strace bug(?) when building a program with NASM that uses the 32-bit int 0x80 ABI in 64-bit mode to make an exit system call.

I added a delay loop before the first system call and I see strace doesn't print out the bitness of the target process until it makes a system call. So apparently strace infers that from whether it uses the 64-bit syscall ABI or the 32-bit int 0x80 / sysenter ABI!

What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?

Perhaps this is related to strace trying to figure out how to decode system calls: The Linux ptrace API that strace uses doesn't have a simple reliable mechanism to tell which system call ABI a process invoked. https://superuser.com/questions/834122/how-to-distinguish-syscall-from-int-80h-when-using-ptrace

A 64-bit process that uses 32-bit system calls used to just get decoded according to 64-bit call numbers. But now it seems modern strace checks:

I used eax=1 / syscall to invoke write, and eax=1 / int 0x80 to invoke exit, and strace decoded them both correctly

execve("./nasm-test", ["./nasm-test"], 0x7ffdb8da5890 /* 52 vars */) = 0
write(0, NULL, 0)                       = 0
strace: [ Process PID=5219 runs in 32 bit mode. ]
exit(0)                                 = ?
+++ exited with 0 +++

This is with strace 5.3 on Linux 5.3.1-arch1-1-ARCH.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847