들어가는말

cc main.c

./a.out

컴파일하면 자동으로 실행파일을 만들어주는건 우리는 이미 잘 알고 있다. 어셈블리어로 작성해서 기계어로 변환된다고 어디선가 들어본거 같은데, c 로만 작성했지 어셈블리어를 한번도 보지 못했다. 어셈블리어는 조상님이 변환해준것일까?

어셈블리어를 보지 못한것은 똑똑한 컴파일러님이 알아서 C → 어셈블리어 → 기계어 로 중간 에셈블리어 단계를 알아서 해준것이다.

컴파일러를 믿지 못하고 C → 어셈블리어로 만들고 고쳐진 어셈블리어 → 기계어로 컴파일하고 싶은 경우가 있을 수 있다. (예를 들어, 극한으로 성능을 끌어올리기위해 어셈블리 최적화까지 진행해야할 경우….)


예시코드

// main.c
#include <unistd.h>

int main(void)
{
	write(1, "Hello World", 11);
	return (0);
}

이런 코드를 cc -S main.c 로 컴파일해보면, 아래와 같이 main.s파일이 얻어진다.

이 mian.s를 수정한뒤에 cc main.s하면 어셈블리수준까지 최적화된 실행파일을 얻어낼 수 있다!(내가 컴파일러보다 최적화를 잘한다면..)

// main.s

.section	__TEXT,__text,regular,pure_instructions
	.build_version macos, 12, 0	sdk_version 13, 1
	.globl	_main                           ; -- Begin function main
	.p2align	2
_main:                                  ; @main
	.cfi_startproc
; %bb.0:
	sub	sp, sp, #32
	stp	x29, x30, [sp, #16]             ; 16-byte Folded Spill
	add	x29, sp, #16
	.cfi_def_cfa w29, 16
	.cfi_offset w30, -8
	.cfi_offset w29, -16
	mov	w8, #0
	str	w8, [sp, #8]                    ; 4-byte Folded Spill
	stur	wzr, [x29, #-4]
	mov	w0, #1
	adrp	x1, l_.str@PAGE
	add	x1, x1, l_.str@PAGEOFF
	mov	x2, #11
	bl	_write
	ldr	w0, [sp, #8]                    ; 4-byte Folded Reload
	ldp	x29, x30, [sp, #16]             ; 16-byte Folded Reload
	add	sp, sp, #32
	ret
	.cfi_endproc
                                        ; -- End function
	.section	__TEXT,__cstring,cstring_literals
l_.str:                                 ; @.str
	.asciz	"Hello World"

.subsections_via_symbols

<aside> 💡 -S는 어셈블러호출전까지만 넓은 의미의 컴파일을 진행해준다. 자세한 내용는 man cc 를 참고!

</aside>