I have a few lines of assembler arm code in an .s file. Just a few routines i need to call. It works fine when building for the device, however when i switch to iPhone Simulator i get "no such instruction" errors. I tried to compile parts of the .s file conditionally with what i know:
#if !TARGET_IPHONE_SIMULATOR
But the assembler doesn't recognize these preprocessor directives (of course) and none of the conditional compilation techniques for assembler that i could remember or find worked, so i'm scratching my head now on how to avoid compilation of that assembler code when building for the Simulator. I also don't see a project option in Xcode that would allow me to compile the file or not depending on the target platform.
SOLVED:
All i was missing was the proper #import in the assembler file. I did not think of adding it because Xcode syntax highlighted any preprocessor directive in green (comment) which made me assume that these commands are not recognized when in fact they work just fine.
This works:
#import "TargetConditionals.h"
#if !TARGET_IPHONE_SIMULATOR
... asm code here ...
#endif
You do do it with a pre-processor macro. They are defined in TargetConditionals.h TARGET_IPHONE_SIMULATOR should be there! (You do need to #include it however.)
Here is code I use to detect ARM vs Thumb vs Simulator:
#include "TargetConditionals.h"
#if defined(__arm__)
# if defined(__thumb__)
# define COMPILE_ARM_THUMB_ASM 1
# else
# define COMPILE_ARM_ASM 1
# endif
#endif
#if TARGET_IPHONE_SIMULATOR
// Simulator defines
#else
// ARM or Thumb mode defines
#endif
// And here is how you might use it
uint32_t
test_compare_shifted_operand(uint32_t w1) {
uint32_t local;
#if defined(COMPILE_ARM_ASM)
const uint32_t shifted = (1 << 8);
__asm__ __volatile__ (
"mov %[w2], #1\n\t"
"cmp %[w2], %[w1], lsr #8\n\t"
"moveq %[w2], #10\n\t"
"movne %[w2], #11\n\t"
: \
[w1] "+l" (w1),
[w2] "+l" (local)
: \
[shifted] "l" (shifted)
);
#else // COMPILE_ARM_ASM
if ((w1 >> 8) == 1) {
local = 10;
} else {
local = 11;
}
#endif // COMPILE_ARM_ASM
return local;
}
Related
I'm working on implementation of a swift package with an adaptive logger that can determine environment for output purposes. The logger supports all platforms (macOS, iOS, tvOS etc.) and can be used from Tests or Swift command line apps as well.
My question: how to determine that the Swift code is being run under Terminal app for instance when run swift test?
I've found that ProcessInfo's environment property has a "_" field with "/usr/bin/swift" or "/Users/.../TestApp" values when you use the Terminal app but I'm not sure that is a correct approach.
var isTerminal : Bool {
return ProcessInfo.processInfo.environment["_"] != nil
}
Are there any other approaches to check this?
Instead of trying to detect whether you're using Terminal.app (which could be error-prone if you're e.g. using a different terminal program or running on Linux), you should instead query the terminal to find out whether it supports specific features. This can be done easily with the ncurses library.
1. Add a CCurses target to your Swift package
We need to create a system library target so that the Swift Package Manager knows how to find and link against the ncurses library Add the target to your Package.swift:
let package = Package(
// ...
targets: [
// ...
.systemLibrary(name: "CCurses"),
],
// ...
)
And create a new directory at Sources/CCurses with the following files:
curses.h
#include <curses.h>
#include <term.h>
module.modulemap
module CCurses [system] {
header "curses.h"
link "curses"
export *
}
2. Check if the terminal supports color output
When you're setting up, query the terminfo database to check for color support:
import CCurses
// ...
var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
useColor = has_colors()
} else {
useColor = false
}
Based on https://stackoverflow.com/a/7426284.
If you need to check for the existence of another feature, there's almost certainly an ncurses function for it -- just check the man page, search online, or ask here on SO.
The ncurses dylib is available on all platforms, but for some reason the headers only exist on macOS. You shouldn't need it on iOS, watchOS, or tvOS anyway, because those platforms don't have terminals. There are two ways to exclude ncurses from being built on iOS, etc: 1) you can #ifdef out the headers, or 2) with Swift 5.3 you can declare a conditional target dependency, which is slightly simpler and cleaner.
Approach 1: #ifdef the headers
Use the following Sources/CCurses/curses.h file instead:
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#if !(defined TARGET_OS_IPHONE || defined TARGET_OS_WATCH || defined TARGET_OS_TV)
#include <curses.h>
#include <term.h>
#endif
Then, whenever you use a function from curses in your Swift code, surround it with a build conditional:
#if os(iOS) || os(watchOS) || os(tvOS)
useColor = false
#else
var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
useColor = has_colors()
} else {
useColor = false
}
#endif
Approach 2: conditional target dependency (requires Swift 5.3)
No changes are necessary to the CCurses target; you only have to modify your dependency on CCurses in your Package.swift:
.target(
name: "MyLib",
dependencies: [
.target(name: "CCurses", condition: .when(platforms: [.macOS, .linux]))
]),
And use a build condition whenever you import CCurses or use curses functions:
#if canImport(CCurses)
import CCurses
#endif
// ...
#if canImport(CCurses)
var erret: Int32 = 0
if setupterm(nil, 1, &erret) != ERR {
useColor = has_colors()
} else {
useColor = false
}
#else
useColor = false
#endif
I'm trying to move one of my apps over to using Swift. It contains an OpenGL draw loop (that also contains some Cocoa statements - yes, I realise it's probably a horrible mess of a class) so I've copied the original .m & .h files into my new project and added a *-Bridging-Header.h file. I've also added a build phase to link with the OpenGL.framework (although I'm not sure I needed to and it made no difference to the issue).
Originally I borrowed heavily from Apple's example OpneGL project and within one of the files I'm trying to compile there is:
#include "sourceUtil.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#import <OpenGL/OpenGL.h>
demoSource* srcLoadSource(const char* filepathname)
{
demoSource* source = (demoSource*) calloc(sizeof(demoSource), 1);
// Check the file name suffix to determine what type of shader this is
const char* suffixBegin = filepathname + strlen(filepathname) - 4;
if(0 == strncmp(suffixBegin, ".fsh", 4))
{
source->shaderType = GL_FRAGMENT_SHADER;
}
else if(0 == strncmp(suffixBegin, ".vsh", 4))
{
source->shaderType = GL_VERTEX_SHADER;
}
else
{
// Unknown suffix
source->shaderType = 0;
}
// more code follows
.
}
However, GL_FRAGMENT_SHADER is causing Xcode to stop any build with the error "Use of undeclared identifier 'GL_FRAGMENT_SHADER'" - similarly with the GL_VERTEX_SHADER. I presume there'll be more errors but currently this is what Xcode stops at.
You need: #import <OpenGL/gl.h>
I read a very interesting blog about implementing some anti-piracy protection into your apps. Some of them dont work anymore, some of them do. The 2 ones that still are effective to an extent are the 2 last ones listed.
http://shmoopi.wordpress.com/2011/06/19/27/
The one I'm interested in is the very last one. Code below. I've implemented this in my AppDelegate.m
Anti piracy via the encryption check.
Required Headers
#import <dlfcn.h>
#import <mach-o/dyld.h>
#import <TargetConditionals.h>
Encryption Struct
#if TARGET_IPHONE_SIMULATOR && !defined(LC_ENCRYPTION_INFO)
#define LC_ENCRYPTION_INFO 0x21
struct encryption_info_command
{
uint32_t cmd;
uint32_t cmdsize;
uint32_t cryptoff;
uint32_t cryptsize;
uint32_t cryptid;
};
#endif
Needed Methods
int main (int argc, char *argv[]);
static BOOL is_encrypted ()
{
const struct mach_header *header;
Dl_info dlinfo;
/* Fetch the dlinfo for main() */
if (dladdr(main, &dlinfo) == 0 || dlinfo.dli_fbase == NULL)
{
NSLog(#"Could not find main() symbol (very odd)");
return NO;
}
header = dlinfo.dli_fbase;
/* Compute the image size and search for a UUID */
struct load_command *cmd = (struct load_command *) (header+1);
for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++)
{
/* Encryption info segment */
if (cmd->cmd == LC_ENCRYPTION_INFO)
{
struct encryption_info_command *crypt_cmd = (struct encryption_info_command *) cmd;
/* Check if binary encryption is enabled */
if (crypt_cmd->cryptid < 1)
{
return NO;
}
return YES;
}
cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize);
}
return NO;
}
This method checks to see if the binary is still encrypted.
When I run this on the device attached to x-code it gives me a false positive on this line
if (crypt_cmd->cryptid < 1)
{
NSLog(#"Pirated from (crypt_cmd->cryptid < 1) ");
return NO;
}
I was wondering is it possible that the builds xcode puts onto the device for debugging purposes not encrypted? And its only encrypted when the build is submitted to Apple for use on iTunes. Hence why I am getting this false positive when check the code.
Many Thanks,
-Code
This code won't work successfully on a 64-bit device like the iPhone 5s. The header has been changed from mach_header to mach_header_64 and the command ID is now LC_ENCRYPTION_INFO_64.
What I did was to read the header and then see what the magic number was. If it's MH_MAGIC_64 then you're on a 64-bit device and you need to use the mach_header_64 struct and look for LC_ENCRYPTION_INFO_64 (defined as 0x2C) instead of LC_ENCRYPTION_INFO.
A better otool command to see whether a file is encrypted or not is:
otool -arch armv7 -l YourAppName | grep crypt
I have been looking into this recently as well and tested with the same results. It turns out this code is telling you YES or NO based on whether the binary is encrypted with Apple's FairPlay DRM. Any debug or ad-hoc builds you do will say NO.
You can see the same information on your binary or any iPhone apps you have purchased using the otool command-line tool.
For your own binaries, find the binary in your project under e.g. build/Debug-iphoneos/MyApp.app and run (from Terminal)
otool -l MyApp | more
Scan through for cryptid in the LC_ENCRYPTION_INFO section. Since this is a debug build it will be 0. If you have synched your phone to your computer, check under ~/Music/iTunes/Mobile Applications and pick an .ipa file. Unzip it and try otool against the binary from the .ipa and it should have 1 for the cryptid.
It looks like this is looking for the signature block in the dyload header. This means that you're only going to see this on code which is signed. Chances are that your code isn't being automatically signed for debugging (unnecessary), although it will be signed when it goes to the device.
You might want to make this entire check conditional on the project running on an iOS device instead of in the simulator. Any binary sent to an iOS device must be signed.
#if !(TARGET_IPHONE_SIMULATOR)
your check
#endif //
Made a new window application. Right-clicked Frameworks > Add Existing Frameworks. Selected Frameworks folder, then AudioToolbox.framework.
Build, and 11 crashes.
#elif defined __ppc__ || __ppc64__
#define _IntSaturate(x) ((int) (x))
#else
#error "Unknown architecture."
// To use unoptimized standard C code, remove above line.
#define _IntSaturate(x) ((x) <= -0x1p31f ? (int) -0x80000000 : \
0x1p31f <= (x) ? (int) 0x7fffffff : (int) (x))
#endif
Tried commenting that line, then MachineExceptions still crashes:
typedef CALLBACK_API_C( OSStatus , ExceptionHandlerProcPtr )(ExceptionInformation * theException);
error: expected ")" before '*' token
..what the hell happened? I'm 99.9% sure I've never modified the AudioToolbox or any other framework.
Just scrapped the project, copied main classes and everything working smoothly again.
How is it done? What steps do I need to take and what pitfalls and gotchas are there to consider?
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.
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;
}
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
}
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...
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
GCC-Inline-Assembly-HOWTO (ibiblio.org)
Extended Asm (Using the GNU Compiler Collection (GCC))
Procedure Call Standard for the Arm® 64-bit Architecture
ARM GCC Inline Assembler Cookbook
ConvertBasicAsmToExtended - GCC Wiki
ios - fork() implementation by using svc call - Stack Overflow
linux - ARM inline asm: exit system call with value read from memory - Stack Overflow