메모리 주소를 사용하는 데이터 타입은 unsigned long 을 사용해야 한다

 

32bit 에서는 메모리 주소가 4바이트

64bit 에서는 메모리 주소가 8바이트

를 차지하므로 CPU 아키텍처에 따라 데이터 타입의 크기도 달라져야 한다

 

unsigned long 은 32bit 에서는 4바이트 64bit 에서는 8바이트가 되므로

 

메모리 주소를 담을때 사용가능하다.

 

ex)

char c;

unsigned long addr = (unsigned long)&c;

shared library 이해 및 만들기


1) shared library

  - Windows 에서는 .dll , Linux, macos 에서는 .so


2) soname

  - compile time 에서는 .so 를 참조하며 runtime 시 .so.{version} 의 파일을 참조

  - 실제 library 파일은 .so.{major}.{minor}... 등의 형태로 사용

[참고: https://kldp.org/node/85366]


3) 빌드방법

 - Linux :

    . gcc -c -fPIC -o output source

    . gcc -shared -Wl,-soname,"libraryname".so.{major version} -o "libraryname".so.{major}.{minor} object


 - macos :

    . gcc -shared -Wl,-install_name,"libraryname".so.{major version} -o "libraryname".so.{major}.{minor} object


4) soname 과 ABI

  - library major 변경이 아닌 경우 재빌드 없이 사용할수 있게 해줌

  - libwaintman.so.1.1 라이브러리가 soname 이 libwaintman.so.1 일때,

    TEST 모듈이 libwaintman.so.1.1 을 참조하게 되면 compile 타임에 libwaintman.so 를 참조함

    . libwaintman.so --> libwaintman.so.1

    . libwaintman.so.1 --> libwaintman.so.1.1

    일 때, TEST 모듈은 빌드될때 libwaintman.so.1.1를 참조하게 됨

 

    TEST 모듈이 실행 될 때 soname 인 libwaintman.so.1 을 찾게 됨

    이 때 libwiantman.so.1 이 libwaintman.so.1.3 버전을 가리키고 있어도 정상적으로 실행이 됨


    정상적으로 실행이 안될 경우 libwaintman.so.1.1 과 libwaintman.so.1.3 은 major 변경이 일어난 경우 이므로

    libwaintman.so.1.3 soname 은 libwaintman.so.2 와 같이 변경이 되어야 함.

    이렇게 되면 TEST 모듈이 실행 될 때 libwaintman.so.1 이 시스템에 없으므로 실행이 되지 않으며

    TEST 를 libwaintman.so.2 를 포함해서 재빌드 해야 됨

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 호출로 마무리

macOS 에서 링킹 시 crt1.o 를 같이 import 해야 한다


crt1.o 에는 macOS loader 에서 실행하는 start() 함수가 구현되어 있다


https://opensource.apple.com/source/Csu/Csu-85/


start.s 를 보면 start 함수가 arhictecture 에 따라 구현되어 있는 것을 확인 할 수 있다.


#if __x86_64__
start:	pushq	$0		    # push a zero for debugger end of frames marker
	movq	%rsp,%rbp	    # pointer to base of kernel frame
	andq    $-16,%rsp	    # force SSE alignment
	movq	8(%rbp),%rdi	    # put argc in %rdi
	leaq	16(%rbp),%rsi	    # addr of arg[0], argv, into %rsi
	movl	%edi,%edx	    # copy argc into %rdx
	addl	$1,%edx		    # argc + 1 for zero word
	sall	$3,%edx		    # * sizeof(char *)
	addq	%rsi,%rdx	    # addr of env[0], envp, into %rdx
#if OLD_LIBSYSTEM_SUPPORT	
	call	__start		    # call _start(argc, argv, envp)
	hlt			    # should never return
#else
	movq	%rdx,%rcx
	jmp	Lapple2
Lapple:	add	$8,%rcx
Lapple2:cmpq	$0,(%rcx)	    # look for NULL ending env[] array
	jne	Lapple		    
	add	$8,%rcx		    # once found, next pointer is "apple" parameter now in %rcx
	call	_main
	movl	%eax,%edi	    # pass result from main() to exit() 
	call	_exit		    # need to use call to keep stack aligned
	hlt
#endif
#endif // __x86_64__ 


call _main 부분에서 우리가 흔히 만드는 main 함수를 call 하는 부분이 되며

call _exit 를 통해 프로세스를 종료한다


main 함수에서 exit 를 호출하면 call _exit 전에 프로세스가 종료된다


<stdlib.h> 에 있는 exit 함수와 start.s 에서 call _exit 로 호출되는 함수는 동일하다


컴파일 : 소스코드에서 오브젝트 코드를 만드는 것

링킹 : 목적코드에서 실행가능한 형태 혹은 라이브러리로 만드는 것


빌드 : 컴파일 + 링킹



[Linux (ubuntu) 에서 GCC 를 이용한 컴파일 및 링킹]


1) 컴파일 : gcc -c test.c

2) 링킹 : ld -o test /usr/lib/x86_64-linux-gnu/crt1.o test.o -lc /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/x86_64-linux-gnu/crtn.o -dynamic-linker /lib64/ld-linux-x86-64.so.2


설명:

-lc : libc.a 를 포함 ( printf 함수와 같은 C Library 를 사용할수 있게 한다)


libc : C Library

glibc : GNU C Library


crt1.o , crti.o, crtn.o : _start, _init 함수와 main 함수로의 jump 를 해주는 라이브러리를 가지고 있다


-dynamic-linker /lib64/ld-linux-x86-64.so.2 : ELF 포맷을 실행해주는 dynamic linker 를 지정한다


[macOS (10.13) 에서 GCC 를 이용한 방법]


1) 컴파일 : gcc -c test.c

2) 링킹 : ld -o test -lSystem test.o /usr/lib/crt1.o


-lSystem : C Library import ( libSystem.dylib )

/usr/lib/crt1.o : main 으로 jump 해주는 라이브러리 포함 ( com.apple.pkg.DevSDK_macOS1013_Public )

(source code : https://opensource.apple.com/source/Csu/Csu-85/)

출처 : https://github.com/skaht/Csu-85


libSystem.dylib : C Library ( com.apple.pkg.Core 에 포함 )


확인 방법

pkgutil --file-info /usr/lib/libSystem.dylib

pkgutil --file-info /usr/lib/crt1.o



* 보통은 gcc -o test test.c 명령으로 한번에 실행파일 생성이 가능하다



Makefile 에 변수 할당 방법


1) variable = define

2) variable := define


1) 의 방법은 변수를 읽는 시점에 값이 정해지며, 2)의 방법은 변수를 assign 하는 시점에 값이 정해진다


할당된 변수 사용법은

$(variable)


출처: https://www.gnu.org/software/make/manual/html_node/Flavors.html#Flavors

macOS 10.13 high sierra 에서 gst-plugins-good-1.12.3 버전 osxvideosink 사용시

illegal instruction : 4 에러 발생


출처: https://bugzilla.gnome.org/show_bug.cgi?id=786047


해결방법 : gst-plugins-good sys/osxvideo/osxvideosink.m 파일

_CFRunLoopSetCurrent(CFRunLoopGetMain()) 라인 위에

CFRetain(CFRunLoopGetCurrent()) 라인 추가


OS : macOS 10.13 (high sierra)

gst-plugins-good 1.12.3


osxvideosink error


fix : https://bugzilla.gnome.org/show_bug.cgi?id=786047


sys/osxvideo/osxvideosink.m


nsAppThread


insert CFRetain(CFRunLoopGetCurrent());

below _CFRunLoopSetCurrent(CFRunLoopGetMain());


Pintos 란

 - 스탠포드에서 만든 교육용 OS

 - cs140 수업

    http://www.scs.stanford.edu/17wi-cs140/reference/index.html


PintOS 설치 - macOS 10.13 High Sierra


 1.  Prerequires

    . homebrew (https://brew.sh/)

        - gcc 4.9

        - qemu

          (bochs 대신 qemu 를 사용한다)


2. Download pintos

git clone http://cs140.stanford.edu/pintos.git pintos

 


3. Build pintos

    . PintOS 빌드하려면 32bit gcc 4.1 버전이 필요함


    - 32bit gcc 4.1 빌드

       a) http://wiki.osdev.org/Cross-Compiler_Successful_Builds

         gcc 4.1 버전과 맞는 binutils 설치 (binutils-2.17)

        -

git clone git://sourceware.org/git/binutils-gdb.git

git checkout tags/binutils-2_17

        - build

./configure --prefix=$PREFIX --target=i386-elf --disable-multilib --disable-nls --disable-werror

make

make install


        - i386-elf-ld 실행 확인

      b) gcc 4.1 설치

컴파일러는 brew로 설치한 gcc-4.9를 이용 (최신 버전의 gcc 혹은 clang 으로는 gcc-4.1 을 빌드할수 없음)

git clone git://gcc.gnu.org/git/gcc.git

git checkout tags/gcc-4_1_0-release

CC=gcc-4.9 ./configure --prefix=$PREFIX --target=i386-elf --disable-multilib --disable-nls --disable-werror --without-headers --enable-languages=c,c++

make all-gcc install-gcc


      - i386-elf-gcc 실행 확인


   - PintOS Make.config 수정

ifneq (0, $(shell expr `uname -m` : '$(X86_64)'))
    CC = $(CCPROG) -m32
    LD = ld -melf_i386
    OBJCOPY = objcopy
else
 

이 부분을

fneq (0, $(shell expr `uname -m` : '$(X86_64)'))

     CC = i386-elf-gcc
     LD = i386-elf-ld
     OBJCOPY = i386-elf-objcopy

else


로 수정

i386-elf-gcc (4.1) 컴파일러로 빌드하기위한 옵션


. 동작확인

pintos --qemu -- run alarm-multiple 




API란
Application Programming Interface 의 줄임말.
우리말로 그대로 풀어쓰면 응용프로그래밍 프로그래밍 인터페이스
******************************************************************

여기서 부터는 개인적인 생각.
번역하자면, 응용프로그래밍과  프로그래밍과의 의사소통.

다시말해, 프로그래밍을 할때 뭔가와 의사소통을 하게 해주는 것이란 것이다.

윈도우즈 프로그래밍을 할때, 메세지 박스호출과 같은 윈도우 함수들을 win32 API 라고 한다.
(메세지 박스 함수는 내가 만든 프로그램에서 호출하는 것으로, 기능은 MS Windows 에서 제공하는 것으로, 함수자체의 구조는 몰라도 된다.)

C 언어에서 보면, printf 함수를 API 라고 볼수 있다.
보통 printf 나 strlen, strcpy 함수들은 라이브러리라고 한다.
(strlen 과 같은 함수는 그 안의 내용은 유추할 수 있으며 그것을 대신할 함수를 스스로 만들수 있다.)

그럼 라이브러리와 API 의 차이는?
본인이 생각하기에는, 라이브러리와 API 들은 함수들이다.
API 는 라이브러리에 포함되는, 라이브러리중에서도 특별한 함수를 API 라고 생각하면 될 것 같다.
(프로그래머 스스로가 구현하기 힘든 함수들 다시말해, OS 의 내부 구조를 알아야 가능한 함수. 예를들어 MessageBox 함수)

그래서 일반적으로 API Library 라고들 표현한다. API 와 Library 가 비교대상은 아닌것 같다.

그렇다면 OpenAPI 나 BlogAPI는?

OpenAPI 는 통상적으로 웹관련된 API 를 말한다고 할수 있겠다. Naver에서 제공하는 열린검색같은 일반 홈페이지에도 네이버 검색기라던지 지도검색을 달수 있도록 해주니 말이다.

BlogAPI 는 뭐 블로그와 연결해서 좀더 좋은 환경을 제공해주는 것을 제공해주는 것이라고 할수 있겠다.

(지칭할 만한 표현이 딱히 떠오르지 않아 그냥 '것' 이라고만 했다. '도구(tool)'란 말을 써도 될듯 하다.)

API 란 Interface 의 한 종류라고 생각하면 될듯 하다.

+ Recent posts