30

How is it done? What steps do I need to take and what pitfalls and gotchas are there to consider?

Hans Sjunnesson
  • 21,745
  • 17
  • 54
  • 63

5 Answers5

20

I've gotten this to work, thanks to some inside help over at the Apple Devforums, you should sign up if you're a dedicated IPhone developer.

First thing's first, it's __asm__(), not plain asm().

Secondly, by default, XCode generates a compilation target that compiles inline assembly against the ARM Thumb instruction set, so usat wasn't recognized as a proper instruction. To fix this, do "Get Info" on the Target. Scroll down to the section "GCC 4.0 - Code Generation" and uncheck "Compile for Thumb". Then this following snippet will compile just fine if you set the Active SDK to "Device"

inline int asm_saturate_to_255 (int a) {
  int y;
  __asm__("usat %0, #8, %1\n\t" : "=r"(y) : "r"(a));
  return y;
}

Naturally, now it won't work with the IPhone Simulator. But TargetConditionals.h has defines you can #ifdef against. Namely TARGET_OS_IPHONE and TARGET_IPHONE_SIMULATOR.

plinth
  • 48,267
  • 11
  • 78
  • 120
Hans Sjunnesson
  • 21,745
  • 17
  • 54
  • 63
  • 2
    I think you mean __asm__() instead of plain asm(). – Quinn Taylor Jun 24 '09 at 17:31
  • Why doesn't it work with the iPhone Simulator? Is it that no assembly works on the simulator, or is it that you have to have a different set of assembly instructions because it is running on i386? – Roberto Jul 07 '12 at 07:50
  • 1
    @Roberto: You got it. The simulator is running on x86, so you can't run ARM assembly. It just won't compile. – Dietrich Epp Jul 11 '12 at 04:45
12

I write quite a bit of ARM Cortex-A8 assembly-code. The CPU on the iPhone is an ARM11 (afaik) so the core instruction set is the same.

What exactly are you looking for? I could give you some examples if you want.


EDIT:

I just found out that on the iPhone you have to use the llvm-gcc compiler. As far as I know it should understand the inline assembler syntax from GCC. If so all the ARM inline assembler tutorials will work on the iPhone as well.

Here is a very minimal inline assembler function (in C). Could you please tell me if it compiles and works on the iphone? If it works I can rant a bit how to do usefull stuff in ARM inline assembler, especially for the ARMv6 architecture and the DSP extensions.

inline int saturate_to_255 (int a)
{
  int y;
  asm ("usat %0, #8, %1\n\t" : "=r"(y) : "r"(a));
  return y;
}

should be equivalent to:

inline int saturate_to_255 (int a)
{
  if (a < 0) a =0;
  if (a > 255) a = 255;
  return a;
}
Nils Pipenbrinck
  • 83,631
  • 31
  • 151
  • 221
  • 1
    I havn't been able to get this compiled. I've been trying through Apple's XCode. The compiler is actually set to GCC 4 by default. If I compile it using that it complains of an invalid instruction 'usat' and if I set it to using llvm-gcc it complains of an invalid architecture, 'armv6'. – Hans Sjunnesson Oct 26 '08 at 15:38
  • try gcc -O3 -march=armv6 test.c – Nils Pipenbrinck Oct 26 '08 at 16:43
  • isn't llvm-gcc actually a compiler that targets llvm virtual machine? – artificialidiot Oct 26 '08 at 17:16
  • yes, but llvm has a ARM code generator backend, so you can use it to compile real code as well as llvm bytecode. – Nils Pipenbrinck Oct 26 '08 at 17:22
  • This works with no trouble compiling with the toolchain. I'd be keen on hearing a bit of a rant on useful inline assembler! – Max Stewart Oct 27 '08 at 20:42
  • @Max Stewart It would be awesome if you could post an answer detailing how you got the above example to compile through XCode. I didn't spend too long fiddling with it, but I didn't get it to work properly. – Hans Sjunnesson Oct 28 '08 at 14:34
  • be pationed guys, I'll write something up (that said: if you can tell me where you want asnm and where your focus of research us I can tailor my post to your demands): – Nils Pipenbrinck Oct 28 '08 at 20:45
  • Hey Hans, I'm actually developing using the open toolchain on Linux - I have no experience with XCode yet so don't really know where you'd start. – Max Stewart Oct 29 '08 at 10:04
2

The registers can also be used explicitly in inline asm

void foo(void) {
#if TARGET_CPU_ARM64
    __asm ("sub        sp, sp, #0x60");
    __asm ("str        x29, [sp, #0x50]");
#endif
}
Kamil.S
  • 5,205
  • 2
  • 22
  • 51
0

Thumb is recommended for application which do not require heavy float operation. Thumb makes the code size smaller and results also in a faster code execution.

So you should only turn Thumb off for application like 3D games...

catlan
  • 25,100
  • 8
  • 67
  • 78
  • 3
    Actually, as a rule, using the Thumb instruction set is significantly slower than using the ARM instruction set. As a rule of thumb (no pun intended), you gain 1/3 in code size and loose 1/3 in speed. See eg. http://www.arm.com/pdfs/Thumb-2CoreTechnologyWhitepaper-Final4.pdf – gc. Apr 26 '09 at 14:36
-1

Background

  • Now is 2021 year -> other answer seems is too old?
  • the most iOS device(iPhone etc.) is ARM 64bit: arm64

Inline assembly on the iPhone

asm keyword

  • GNU/GCC compiler
    • standard C (compile flag: -ansi / -std): use __asm__
    • GNU extensio: use asm
  • ARM compiler: use __asm

asm syntax

AFAIK, there many asm syntax

  • asm syntax
    • AT&T syntax ~= GNU syntax ~= UNIX syntax
    • Intel syntax
    • ARM syntax

here only focus on most common used GNU/GCC syntax

GNU/UNIX syntax

Basic Asm

asm("assembly code");
__asm__("assembly code");

Extended Asm

asm asm-qualifiers ( AssemblerTemplate 
                 : OutputOperands 
                 [ : InputOperands
                 [ : Clobbers ] ])

My Example code

  • environment
    • dev
      • macOS
        • IDE: XCode
          • compiler: clang
    • running
      • iOS - iPhone
        • hardware arch: ARM64

inline asm to call svc 0x80 for ARM64 using Extended Asm

  • inline asm inside ObjC code
// inline asm code inside iOS ObjC code
__attribute__((always_inline)) long svc_0x80_syscall(int syscall_number, const char * pathname, struct stat * stat_info) {
    register const char * x0_pathname asm ("x0") = pathname; // first arg
    register struct stat * x1_stat_info asm ("x1") = stat_info;  // second arg
    register int x16_syscall_number asm ("x16") = syscall_number; // special syscall number store to x16

    register int x4_ret asm("x4") = -1; // store result

    __asm__ volatile(
         "svc #0x80\n"
         "mov x4, x0\n"
        : "=r"(x4_ret)
        : "r"(x0_pathname), "r"(x1_stat_info), "r"(x16_syscall_number)
//         : "x0", "x1", "x4", "x16"
    );
    return x4_ret;
}
  • call inline asm
// normal ObjC code
#import <sys/syscall.h>

...
    int openResult = -1;
    struct stat stat_info;
    const char * filePathStr = [filePath UTF8String];
...
  // call inline asm function
    openResult = svc_0x80_syscall(SYS_stat64, filePathStr, &stat_info);

Doc

crifan
  • 12,947
  • 1
  • 71
  • 56
  • Your examples of GNU C Basic asm are all unsafe except in an `__attribute__((naked))` function or at global scope if you're defining a whole function instead of using a separate .S file. It should never be used outside of that context; always use Extended asm so you can decide whether you want a `"memory"` clobber with your `"dsb ish"` or not, instead of having it be implicit on some compilers and not on others. See https://gcc.gnu.org/wiki/ConvertBasicAsmToExtended for reasons why it's terrible. – Peter Cordes Nov 23 '21 at 08:40
  • x86 AT&T syntax seems like an odd choice on an iphone Q&A. (Also, `$label(%edx,%ebx,$4)` references the symbol `$label`, not `label`; perhaps you were mixing it up with AT&T syntax for immediate operands? But that's in the Basic Asm section so it should just get removed.) In the Extended Asm section, you're still using AT&T syntax for x86 / x86-64, although at least it appears bug-free. But with AT&T having the opposite source/destination order from AArch64 assembly, it's going to be potentially confusing. – Peter Cordes Nov 23 '21 at 08:44
  • You later say "My Example code"; are you saying you quoted the x86 examples from somewhere else? The BNF for the syntax is copied from the GCC manual, which is fine, but if you're going to copy examples you should probably quote them. (But again, since they're x86 examples, you should remove them.) As is, they're all your examples, by dint of choosing to include them that way in your answer. – Peter Cordes Nov 23 '21 at 08:45
  • Unfortunately your AArch64 example of using `svc 0x80` forgets to declare clobbers on the hard registers you write. Notice that your function is even `always_inline` so this can and will cause breakage even at `-O0`, not just with optimization enabled. (Not that that would make it ok; even `noinline` would not justify it, because the compiler could pick x0 for an input so you overwrite it before you read it.). – Peter Cordes Nov 23 '21 at 08:49
  • Also, if your asm template starts and/or ends with `mov`, you're usually doing it wrong (or inefficiently): instead, tell the compiler which registers you want your values in. e.g. using `register const char *path __asm__("x0") = pathname` to make an `"r"` constraint pick `x0`. See links in https://stackoverflow.com/tags/inline-assembly/info. This has the other advantage that the compiler knows those values are still in regs after. And you should probably using an `"r"(SYS_stat64)` constraint for the call number, with constants from `` if iPhone has that (GNU and BSD do). – Peter Cordes Nov 23 '21 at 08:53
  • great thanks to @PeterCordes comments, to let me learned more detailed inline asm knowledge. So I will update my answer according to your advice. – crifan Nov 24 '21 at 02:10
  • @PeterCordes about use `SYS_stat64` in ``: which I have done it in my later update code of my project. also done it for `SYS_stat` == 188 – crifan Nov 24 '21 at 02:11
  • Another thing I forgot to mention: the asm in your system-call wrapper needs a `"memory"` clobber, or a dummy `"=m"` memory output operand as in [How can I indicate that the memory \*pointed\* to by an inline ASM argument may be used?](https://stackoverflow.com/q/56432259) to tell the compiler the pointed-to struct is written. – Peter Cordes Nov 24 '21 at 03:03
  • Does the iOS kernel really error / no-error status in `x0`? On x86-64 MacOS, error / no-error is indicated in the carry flag (and the return-value register is `errno` if it's set). I guess just checking for non-zero works for this system call where `0` is the only non-error return. But I don't see why you'd want to destroy the error code and always return -1. That makes it impossible for code using this to know why it failed and print a useful error message (perror / strerror()). – Peter Cordes Nov 24 '21 at 03:07
  • @PeterCordes always return `-1` is special here for `svc 0x80 syscall` for `check open file`. and I will tested later, then update code to not use `-1` for general case. – crifan Nov 24 '21 at 03:27
  • You could and should write callers to just check `!= 0` instead of `== -1`, especially if they're written to use this inline-asm wrapper that doesn't set `errno`. – Peter Cordes Nov 24 '21 at 03:37
  • Why is a function called `openFile` using `stat`? If it's actually going to open the file, it should use `open` and then if successful, `fstat` on the result file descriptor. That removes a potential TOCTOU problem where the path could be replaced with a different file between `stat` and `open`. (Whichever order you do it in, although it would be silly to use the path again to stat *after* opening.) – Peter Cordes Nov 24 '21 at 03:41
  • @PeterCordes here is VERY special case: just use inline asm to emulate ( `stat()`/ `syscall( stat )` / `open()` ) open file, aim to can NOT be bypass by normal iOS jailbreak anti-detection technology, such as hooking `open`/`stat()`/ `syscall(stat)` – crifan Nov 24 '21 at 05:40
  • Unclear why a `stat` system call is part of this at all, instead of just doing an `open` system call via inline asm. I don't know iOS specifically, but under a POSIX OS, you don't need to stat first. And if you want to know the file size, you should `fstat` after the file descriptor is already open. – Peter Cordes Nov 24 '21 at 05:44
  • @PeterCordes yes, here `stat` `openFile` is not related to inline asm. and I have done the C code to call `stat()` and `syscall()`. so here just ignore it. – crifan Nov 24 '21 at 06:36