macOS 10.13 High Sierra 에서는 기본적인 dyld 는 start 함수가 함수의 시작점이 된다


어셈블러로 start 함수를 작성하여 다른 라이브러리 import 없이 프로그램을 실행할 수 있다


코드 작성

hello2.s

.macosx_version_min 10, 7
.globl start
.text

start:
    mov $0x2000004, %rax
    mov $1, %rdi
    movabs $msg, %rsi
    mov $len, %rdx
    syscall

    mov $0x2000001, %rax
    mov $0x0, %rdi
    syscall

.data

msg:
    .ascii "Hello, World!\n"
    len = . - msg
 


macOS 10.8 부터는 ld 에서 강제적으로 main 함수를 호출하고 있어 macosx_version_min 을 10.7 로 강제로 세팅한다


ld 코드 확인 ( https://opensource.apple.com/source/ld64/ld64-274.2/src/ld/Options.cpp.auto.html )


void Options::reconfigureDefaults()
{
.................

switch
( fOutputKind ) { case Options::kDynamicExecutable: if ( fEntryPointLoadCommandForceOn ) { fEntryPointLoadCommand = true; if ( fEntryName == NULL ) fEntryName = "_main"; } else if ( fEntryPointLoadCommandForceOff ) { fNeedsThreadLoadCommand = true; if ( fEntryName == NULL ) fEntryName = "start"; } else { // <rdar://problem/16310363> Linker should look for "_main" not "start" when building for sim regardless of min OS if ( minOS(ld::mac10_8, ld::iOS_6_0) || fTargetIOSSimulator ) { fEntryPointLoadCommand = true; if ( fEntryName == NULL ) fEntryName = "_main"; if ( strcmp(fEntryName, "start") == 0 ) { warning("Ignoring '-e start' because entry point 'start' is not used for the targeted OS version"); fEntryName = "_main"; } } else { fNeedsThreadLoadCommand = true; if ( fEntryName == NULL ) fEntryName = "start"; } } break;

.....................

 



macos 10.8 부터 강제로 _main 으로 점프하게 되어 있으며 ld 옵션 -e 옵션으로 지정해 주는 것도 막고 있다.


빌드 방법

as -c hello2.s -o hello2.o

ld -macosx_version_min 10.7 -o hello2 hello2.o


10.8 이상에서는

Undefined symbols for architecture x86_64:
  "_main", referenced from:
     implicit entry/start for main executable
ld: symbol(s) not found for inferred architecture x86_64 



_main 함수가 없다는 에러가 발생한다


혹은 no_new_main 옵션을 추가하면 최신 버전에서도 빌드가 잘 된다


ld -macosx_version_min 10.13 -no_new_main -o hello2 hello2.o


해당 옵션은 man page 에서는 보이지 않는다.


아래에 구현되어 있다

            else if ( strcmp(arg, "-new_main") == 0 ) {
                fEntryPointLoadCommandForceOn = true;
                cannotBeUsedWithBitcode(arg);
            }   
            else if ( strcmp(arg, "-no_new_main") == 0 ) {
                fEntryPointLoadCommandForceOff = true;
                cannotBeUsedWithBitcode(arg);
            }    


참조: https://stackoverflow.com/questions/24841283/how-can-i-set-a-custom-c-entry-point-with-clang-on-os-x-mavericks

GAS (GNU Assembler)


macOS 에서는 as 컴파일러 사용.

macOS 에서는 clang에 포팜된 컴파일러가 사용된다


Hello, World! 출력 코드 작성


Reference : http://www.idryman.org/blog/2014/12/02/writing-64-bit-assembly-on-mac-os-x/


hello1.s 작성


.globl _main
.text

_main:
    mov $0x2000004, %rax
    mov $1, %rdi
    movabs $msg, %rsi
    mov $len, %rdx
    syscall
    ret

.data

msg:
    .ascii "Hello, World!\n"
    len = . - msg
 


64bit assembly 에서는 system call 방식이 int 0x80 에서 syscall 로 변경되었다.

또한 syscall 번호도 base 0x2000000 이 추가된다


syscall 번호 확인 : https://opensource.apple.com/source/xnu/xnu-4570.1.46/bsd/kern/syscalls.master.auto.html


4	AUE_NULL	ALL	{ user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); } 


rax 레지스터에 write syscall 인 4 + base 추가

rdi 레지스터에 stdout 인 1 추가 (fd)

rsi 레지스터에 "Hello, World!\n" 가 저장된 주소 추가 (cbuf)

rdx 레지스터에 길이 값 추가 (nbyte)


syscall 실행


ret 호출로 마무리

+ Recent posts