GNU Assembler (as) 완전 가이드
GAS 문법을 단순 명령어 목록이 아닌 커널 코드 작성 관점으로 설명합니다. `.section`·`.type`·`.size`·재배치 지시자 의미, 매크로/조건 조립으로 반복 코드 관리, CFI 기반 언와인드 정보 생성, x86·AArch64·RISC-V별 calling convention 차이, C 코드 inline asm과의 제약 조건 연결, 부트 코드·트랩 핸들러·저수준 컨텍스트 전환 루틴 작성 시 흔한 오류와 점검법까지 상세히 정리합니다.
기준 버전: GNU Binutils 2.46 (2025년). 이 페이지는
Using as 공식 매뉴얼(470페이지)의 핵심 내용을 커널 개발 관점으로 정리합니다.
커맨드라인 도구 전반은
GNU Binutils 를, GCC 파이프라인과 인라인 어셈블리는
어셈블리 종합 을 참고하세요.
전제 조건: GNU Binutils 와
어셈블리 종합 문서를 먼저 읽으세요.
ELF 파일 형식(섹션, 심볼 테이블, 재배치)의 기본 개념을 알고 있으면 이 문서를 훨씬 빠르게 이해할 수 있습니다.
일상 비유: GNU Assembler는 번역가 와 같습니다.
사람이 읽을 수 있는 어셈블리 텍스트(영어 원문)를 CPU가 직접 이해하는 기계어 바이트(외국어 번역본)로 변환합니다.
번역 규칙(문법)을 알아야 번역가(어셈블러)와 올바르게 소통할 수 있습니다.
핵심 요약
역할 — as는 .s/.S 파일을 ELF .o로 변환하는 GNU 어셈블러입니다. GCC가 내부적으로 호출합니다.
문법 — 기본은 AT&T 문법(소스→목적 순서, 접미사). .intel_syntax noprefix로 Intel 문법 전환 가능.
지시자 — .section, .global, .type, .align 등 100개+ 지시자로 어셈블 동작을 제어합니다.
CFI — .cfi_startproc/.cfi_endproc으로 DWARF 언와인드 정보를 생성, 스택 트레이스와 예외 처리에 필수입니다.
아키텍처 — x86/x86_64, AArch64, RISC-V 각각 고유 지시자와 확장 문법이 있습니다.
단계별 이해
기본 변환 이해 as -o foo.o foo.s로 어셈블리 파일을 오브젝트로 변환하고 objdump -d foo.o로 결과를 확인합니다.
문법 규칙 익히기 AT&T 문법의 레지스터 접두사(%), 상수 접두사($), 명령어 접미사(b/w/l/q)를 익힙니다.
지시자 활용 .section으로 섹션을 지정하고, .global/.type/.size로 심볼 속성을 설정합니다.
CFI 추가 함수에 .cfi_startproc/.cfi_endproc을 추가하여 DWARF 언와인드 정보를 생성합니다.
아키텍처 확장 필요한 아키텍처(-march=)와 확장(+sve 등)을 지정하여 최신 명령어를 활용합니다.
GNU Assembler 개요
GNU Assembler(as)는 GNU Binutils 패키지의 핵심 구성 요소로, 어셈블리 언어 소스 파일(.s, .S)을 ELF(Executable and Linkable Format) 오브젝트 파일(.o)로 변환합니다. GCC 컴파일 파이프라인에서 cc1(C 컴파일러 프론트엔드)이 생성한 임시 .s 파일을 as가 어셈블합니다.
소스 파일
foo.c / foo.s
전처리/컴파일
cpp / cc1
GNU as
어셈블러
오브젝트
foo.o (ELF)
.s
.s
GNU Assembler 처리 흐름
gcc -S → .s 파일 → as → .o (ELF) → ld → 실행 파일
as는 AT&T 문법을 기본으로 하지만, .intel_syntax 지시자로 Intel 문법으로 전환할 수 있습니다. 멀티패스 어셈블러이며, 전방 참조(forward reference)를 허용합니다. 전처리(#include, #define 등)는 as 자체가 하지 않고, C 전처리기(cpp)가 먼저 처리한 후 as에 전달합니다.
커맨드라인 옵션
GNU Assembler 2.46의 주요 커맨드라인 옵션입니다. 아키텍처 공통 옵션을 다루며, 아키텍처별 옵션은 각 섹션을 참고하세요.
기본 사용법
# 어셈블리 파일을 오브젝트로 변환
as -o output.o input.s
# 64비트 모드 (x86-64)
as --64 -o foo.o foo.s
# 32비트 모드 (x86)
as --32 -o foo.o foo.s
# 디버그 정보 포함 (DWARF)
as -g -o foo.o foo.s
# 크로스 어셈블러 (AArch64 타겟)
aarch64-linux-gnu-as -o foo.o foo.s
옵션 전체 참조
옵션 설명
-a[cdghilns][=file]어셈블 리스팅 생성. c=조건부 생략, d=디버그 생략, g=일반 정보, h=헤더, i=입력, l=리스팅, m=매크로, n=폼 피드 생략, s=심볼. =file로 파일 지정
--alternate대체 매크로 모드 활성화 (MRI 호환 매크로 확장)
-D심볼 정의 (현재는 무시됨, 호환성 유지용)
-f화이트스페이스/주석 전처리 생략. 입력이 이미 처리된 경우에 사용
-gDWARF 디버그 정보 생성 (파일명, 라인 번호)
-I path.include 지시자의 검색 경로 추가
-K차이 테이블(difference table)이 넘치면 경고 출력
-L로컬 심볼(이름이 L로 시작)을 심볼 테이블에 포함
--listing-lhs-width=n리스팅 왼쪽 폭(바이트 수) 설정
--listing-rhs-width=n리스팅 오른쪽 폭(문자 수) 설정
-M / --mriMRI 호환 어셈블러 모드
--MD fileMakefile 의존성 파일 생성 (gcc -MD와 유사)
-o file출력 오브젝트 파일명 지정 (기본: a.out)
-Rdata 섹션을 text 섹션에 병합 (읽기 전용 데이터 최적화)
--statistics어셈블 완료 후 CPU 시간과 메모리 사용량 출력
--traditional-format일부 출력을 전통적(구식) 형식으로 출력 (호환성)
-v / --version버전 정보 출력 후 종료
-W / --no-warn경고 메시지 억제
--warn경고 출력 활성화 (기본값)
--fatal-warnings경고를 오류로 처리하여 어셈블 실패
-Z오류가 있어도 오브젝트 파일 생성 (불완전할 수 있음)
리스팅 파일 활용
# 전체 리스팅 (주소, 인코딩, 소스 나란히)
as -aln=foo.lst -o foo.o foo.s
# 매크로 전개 포함
as -almn=foo.lst -o foo.o foo.s
# 의존성 파일 생성 (Makefile 통합)
as --MD foo.d -o foo.o foo.s
GAS 문법 규칙
GNU Assembler는 AT&T 문법을 기본으로 합니다. 소스 파일의 각 줄은 레이블, 명령어, 지시자, 또는 주석으로 구성됩니다.
기본 문법 구조
my_function :
movq $1 , %rax
movl %eax , -4(%rbp )
ret
1 :
decl %ecx
jnz 1b
jmp 2f
2 :
주석 스타일
문법 설명 플랫폼
/* ... */C 스타일 블록 주석 모든 플랫폼
# 주석줄 끝 주석 대부분 플랫폼
@ 주석줄 끝 주석 ARM
// 주석C++ 스타일 줄 끝 주석 일부 아키텍처
; 주석줄 끝 주석 x86 일부
AT&T vs Intel 문법 비교
항목 AT&T 문법 (기본) Intel 문법
피연산자 순서 소스, 목적 (mov src, dst) 목적, 소스 (mov dst, src)
레지스터 %rax, %rbxrax, rbx
상수(즉시값) $42, $0x1f42, 0x1f
명령어 접미사 movb, movw, movl, movq접미사 없음 (크기 추론)
메모리 참조 offset(%base, %idx, scale)[base + idx*scale + offset]
전환 지시자 (기본) .intel_syntax noprefix
.intel_syntax noprefix
mov rax , 1
mov [rbp-4 ], eax
.att_syntax prefix
상수 표현
타입 표기 예제
10진수 숫자 그대로 42, -100
16진수 0x 또는 0X 접두사0xFF, 0x1000
8진수 0 접두사0644, 077
2진수 0b 또는 0B 접두사0b1010, 0B1111
문자 작은따옴표 'A, '\n
현재 주소 점(.) . - start_label
심볼 명명 규칙
영문자, 숫자, _, ., $로 구성 (숫자 시작 불가)
L로 시작하는 심볼은 로컬(기본적으로 심볼 테이블에서 제외)
숫자로만 이루어진 레이블은 지역(numeric local) 레이블로 재사용 가능 (0:~9:)
커널에서는 주로 ENTRY(name), END(name), SYM_FUNC_START(name) 매크로 사용
섹션과 재배치
어셈블러는 소스를 여러 섹션으로 나눠 처리합니다. 각 섹션은 ELF 파일의 섹션 헤더로 기록되며, 링커가 최종 실행 파일을 생성할 때 배치를 결정합니다.
표준 섹션
섹션 플래그 설명
.textax (실행·할당) 실행 코드 섹션
.dataaw (쓰기·할당) 초기화된 읽기/쓰기 데이터
.rodataa (할당) 읽기 전용 데이터 (상수)
.bssaw (쓰기·할당) 미초기화 데이터 (파일에 공간 없음)
.note(없음) GNU 노트 섹션 (빌드 ID 등)
.debug_*(없음) DWARF 디버그 정보
섹션 제어 지시자
.section .text
.section .data
.section .mydata, "aw" , @progbits
.section .mycode, "ax" , @progbits
.pushsection .altinstructions, "a"
.long 0x1234
.popsection
.subsection 1
.previous
재배치(Relocation)
어셈블러가 심볼 주소를 확정할 수 없을 때 재배치 엔트리를 생성하며, 링커가 최종 주소를 채웁니다.
movq external_sym(%rip ), %rax
call another_func
leaq my_data(%rip ), %rsi
심볼과 속성
심볼은 주소·값에 이름을 붙이는 것입니다. .global/.local로 가시성, .type으로 타입, .size로 크기를 지정합니다.
심볼 가시성 지시자
지시자 ELF 바인딩 설명
.global symSTB_GLOBAL 전역 심볼 — 다른 오브젝트 파일에서 참조 가능
.globl symSTB_GLOBAL .global의 동의어
.local symSTB_LOCAL 로컬 심볼 — 현재 오브젝트에서만 유효
.weak symSTB_WEAK 약한 심볼 — 강한 정의가 있으면 덮어써짐
.weakref alias, targetSTB_WEAK 약한 참조 별칭 정의
심볼 가시성(Visibility) 지시자
지시자 ELF 가시성 설명
.protected symSTV_PROTECTED 외부에서 보이지만 오버라이드 불가
.hidden symSTV_HIDDEN 공유 라이브러리 외부에서 보이지 않음
.internal symSTV_INTERNAL 프로세서별 숨김 (더 강한 제한)
심볼 타입과 크기
.section .text
.global my_func
.type my_func, @function
my_func :
ret
.size my_func, .-my_func
.section .data
.global my_var
.type my_var, @object
my_var :
.long 42
.size my_var, 4
.symver my_func_v2, my_func@@GLIBC_2.17
상수 심볼
.equ PAGE_SIZE, 4096
.set STACK_SIZE, 0x4000
.equiv MAX_CPUS, 256
.equ BUFFER_END, BUFFER_START + BUFFER_SIZE
어셈블러 지시자 완전 참조
GAS는 100개 이상의 지시자를 제공합니다. 아래는 커널 개발에서 자주 사용하는 지시자를 카테고리별로 정리합니다.
섹션 관리
지시자 설명
.section name [, "flags" [, @type]]섹션 전환 또는 생성. 플래그: a(할당) w(쓰기) x(실행) M(병합) S(문자열) G(그룹) T(TLS)
.text [n]코드 하위 섹션 n으로 전환 (기본 0)
.data [n]데이터 하위 섹션 n으로 전환
.bssBSS 섹션으로 전환
.pushsection name [, "flags"]현재 섹션을 스택에 저장하고 섹션 전환
.popsection스택에서 이전 섹션 복원
.previous최근 이전 섹션으로 복귀
.subsection n현재 섹션의 하위 섹션 n으로 전환
데이터 지시자
지시자 크기 설명
.byte expr [, ...]1바이트 1바이트 정수 상수 삽입
.word / .short expr2바이트 2바이트 정수 상수
.long / .int expr4바이트 4바이트 정수 상수
.quad expr8바이트 8바이트 정수 상수
.octa expr16바이트 16바이트 정수 상수
.float / .single expr4바이트 IEEE 754 단정밀도 부동소수
.double expr8바이트 IEEE 754 배정밀도 부동소수
.ascii "str"가변 NULL 없는 문자열
.asciz "str" / .string "str"가변+1 NULL 종료 문자열
.fill count [, size [, val]]가변 count×size 바이트를 val로 채움. 기본: size=1, val=0
.space n [, val] / .skip n [, val]n바이트 n바이트 공간 확보, val로 채움 (기본 0)
.zero nn바이트 n바이트 0으로 채움 (.space n, 0과 동일)
.comm sym, size [, align]가변 BSS 공통 심볼 선언 (링커가 병합)
.lcomm sym, size [, align]가변 로컬 BSS 심볼 선언
.incbin "file" [, skip [, count]]가변 이진 파일을 현재 위치에 직접 삽입
정렬 지시자
지시자 설명
.align n [, fill [, max]]n 바이트(또는 일부 아키텍처에서 2^n 바이트) 경계 정렬. fill: 패딩 값, max: 최대 패딩 바이트
.balign n [, fill [, max]]n 바이트 경계 정렬 (아키텍처 독립적, 이식성 우수)
.p2align n [, fill [, max]]2^n 바이트 경계 정렬 (아키텍처 독립적)
주의: .align의 인자 해석이 아키텍처마다 다릅니다. x86에서는 바이트 수(.align 16 = 16바이트 정렬), ARM/RISC-V에서는 2의 지수(.align 4 = 16바이트 정렬). 이식성을 위해서는 .balign(바이트)이나 .p2align(지수) 사용을 권장합니다.
조건부 어셈블리
지시자 설명
.if expr표현식이 0이 아니면 포함
.ifz expr표현식이 0이면 포함
.ifeq expr표현식이 0이면 포함 (.ifz와 동일)
.ifne expr표현식이 0이 아니면 포함
.ifgt / .ifge표현식이 0보다 크면 / 크거나 같으면 포함
.iflt / .ifle표현식이 0보다 작으면 / 작거나 같으면 포함
.ifdef sym심볼이 정의되어 있으면 포함
.ifndef sym / .ifnotdef sym심볼이 정의되지 않았으면 포함
.ifc str1, str2두 문자열이 같으면 포함
.ifnc str1, str2두 문자열이 다르면 포함
.else조건 반전 블록
.elseif expr추가 조건
.endif조건부 블록 종료
매크로 지시자
.macro SAVE_REGS reg1, reg2=rax, reg3:req
push \reg1
push \reg2
push \reg3
.endm
SAVE_REGS %rbx, %rcx, %rdx
.rept 4
nop
.endr
.irp reg, %rax, %rbx, %rcx
push \reg
.endr
.irpc c, abc
.byte '\c
.endr
.macro CHECK val
.if \val == 0
.exitm
.endif
.long \val
.endm
.purgem CHECK
기타 유용한 지시자
지시자 설명
.include "file"파일 내용 포함 (헤더 파일 삽입)
.print "message"어셈블 중 메시지 출력
.warning "msg"경고 출력 (어셈블 계속)
.error "msg"오류 출력 후 어셈블 중단
.abort즉시 어셈블 중단
.file "name"논리적 파일명 설정 (디버그 정보)
.line n논리적 줄 번호 설정
.loc file line [col]DWARF 위치 정보 설정
.org n [, fill]현재 섹션에서 오프셋 n으로 이동
.sleb128 expr부호 있는 LEB128 인코딩 정수
.uleb128 expr부호 없는 LEB128 인코딩 정수
.nops n [, max]n바이트 NOP 패딩 (최적 NOP 선택)
CFI 지시자 (DWARF 언와인드)
CFI(Call Frame Information) 지시자는 DWARF 언와인드 정보를 생성합니다. 스택 언와인드(예외 처리, backtrace(), perf callchain), 그리고 디버거가 콜 스택을 복원하는 데 필수적입니다. 리눅스 커널도 CONFIG_UNWINDER_FRAME_POINTER / CONFIG_UNWINDER_ORC 모드에서 CFI 정보를 활용합니다.
CFI 지시자와 스택 프레임
어셈블리 코드 + CFI 지시자
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register rbp
subq $32, %rsp
...
movq %rbp, %rsp
popq %rbp
.cfi_def_cfa rsp, 8
ret
.cfi_endproc
스택 프레임 구조
반환 주소 (caller가 push)
이전 %rbp (pushq %rbp)
%rbp →
로컬 변수 영역
(subq $32, %rsp)
%rsp →
CFA (Canonical Frame Address)
= 프레임 시작 전 %rsp 값
= %rbp + 16
(반환주소+saved rbp 크기)
주요 CFI 지시자
지시자 설명
.cfi_startproc [simple]CFI 정보 블록 시작. simple: 기본 초기화 생략
.cfi_endprocCFI 정보 블록 종료
.cfi_def_cfa reg, offsetCFA = reg + offset으로 정의
.cfi_def_cfa_register regCFA 계산에 사용할 레지스터만 변경 (오프셋 유지)
.cfi_def_cfa_offset offsetCFA 오프셋만 변경 (레지스터 유지)
.cfi_adjust_cfa_offset n현재 CFA 오프셋에 n을 더함
.cfi_offset reg, offset레지스터 reg가 CFA+offset에 저장됨을 기록
.cfi_rel_offset reg, offset레지스터 reg가 현재 CFA 기준 offset에 저장됨
.cfi_register reg1, reg2reg1의 이전 값이 reg2에 있음을 기록
.cfi_restore reg레지스터 reg가 함수 진입 시 값으로 복원됨
.cfi_undefined reg레지스터 reg의 이전 값을 추적 불가
.cfi_same_value reg레지스터 reg 값이 변경되지 않음
.cfi_remember_state현재 CFI 상태를 스택에 저장
.cfi_restore_state스택에서 CFI 상태 복원
.cfi_return_column reg반환 주소 레지스터 지정
.cfi_signal_frame시그널 프레임 표시 (특수 언와인드)
.cfi_window_save레지스터 윈도우 저장 (SPARC)
.cfi_escape expr [, ...]임의 DWARF CFA 표현식 삽입
.cfi_val_offset reg, offset레지스터의 현재 값 = CFA + offset
.cfi_gnu_args_size sizeGNU 확장: 피우시(pushed) 인자 크기
x86-64 함수 CFI 표준 패턴
.global example_func
.type example_func, @function
example_func :
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $32 , %rsp
leave
.cfi_def_cfa %rsp, 8
ret
.cfi_endproc
.size example_func, .-example_func
커널 특수 CFI 패턴 (프레임 포인터 없는 경우)
.cfi_startproc
subq $8 , %rsp
.cfi_adjust_cfa_offset 8
pushq %rbx
.cfi_adjust_cfa_offset 8
.cfi_rel_offset %rbx, 0
popq %rbx
.cfi_adjust_cfa_offset -8
.cfi_restore %rbx
addq $8 , %rsp
.cfi_adjust_cfa_offset -8
ret
.cfi_endproc
x86/x86_64 아키텍처 기능
x86/x86_64 타겟에서 as의 특수 기능을 설명합니다.
x86 전용 옵션
옵션 설명
--32 / --x32 / --64출력 코드 모드: 32비트 / x32 ABI / 64비트
-march=cpuCPU 타입 지정 (generic32, generic64, x86-64, znver4, sapphirerapids 등)
-mtune=cpu성능 최적화 타겟 CPU
-msse2avxSSE 명령어 인코딩을 VEX(AVX) 형식으로 출력
-mavxscalar=128AVX 스칼라 연산 크기 지정
-mno-shared공유 라이브러리 생성 안 함 (PLT/GOT 불필요)
-mamd64 / -mintel64AMD64 vs Intel64 확장 선택
AT&T 문법 — 명령어 접미사와 메모리 참조
movb $0x41 , %al
movw $0x1234 , %ax
movl $0x12345678 , %eax
movq $0x1234567890 , %rax
movl (%rax), %ebx
movl 4(%rax), %ebx
movl (%rax, %rcx), %ebx
movl (%rax, %rcx, 4), %ebx
movl 8(%rax, %rcx, 4), %ebx
leaq my_data(%rip), %rsi
movq global_var(%rip), %rax
명령어 프리픽스
프리픽스 설명
lock원자적 메모리 연산 (메모리 버스 잠금)
rep / repe / repne문자열 반복: 무조건 / ZF=1일 때 / ZF=0일 때
cs:, ds:, es:, fs:, gs:, ss:세그먼트 오버라이드 (Linux에서 fs:/gs:가 TLS용)
data16 / addr1632/64비트 모드에서 16비트 피연산자/주소 강제
rex64REX.W 프리픽스 명시적 지정
notrackCET 간접 분기 추적 비활성화
x86 특수 지시자
.intel_syntax noprefix
mov rax, 1
add rbx, [rsp+8]
.att_syntax prefix
.byte 0x0f , 0x1f , 0x00
.byte 0x66 , 0x2e , 0x0f , 0x1f , 0x84 , 0x00 , 0, 0, 0, 0
endbr64
SYM_CODE_START (entry_SYSCALL_64)
.cfi_startproc simple
.cfi_signal_frame
.cfi_def_cfa rsp, 0
.cfi_register rip, rcx
.cfi_endproc
SYM_CODE_END (entry_SYSCALL_64)
AArch64 아키텍처 기능
GNU Assembler의 AArch64(ARMv8+) 지원 기능입니다.
AArch64 전용 옵션
옵션 설명
-march=armv8-a[+ext]아키텍처 버전 및 확장 지정
-mcpu=cpu특정 CPU (cortex-a55, cortex-a78, neoverse-n2 등)
-mabi=ilp32 / -mabi=lp64ILP32 vs LP64 ABI 선택
-EB / -EL빅 엔디안 / 리틀 엔디안
아키텍처 확장 (+extension)
확장 설명
+sveScalable Vector Extension (가변 길이 벡터)
+sve2SVE2 (확장 벡터 명령어)
+lseLarge System Extension (원자 연산 강화)
+mteMemory Tagging Extension
+btcBranch Target Identification
+rcpcRelease-Consistent Processor Consistent 로드
+crypto암호화 확장 (AES, SHA 등)
+dotprod점적(dot product) 명령어
+sm4SM4 암호화 명령어
AArch64 머신 지시자
.arch armv8-a+sve+lse
.arch_extension sve2
.no_arch_extension sve
.cpu cortex-a78
.global aarch64_func
.type aarch64_func, %function
aarch64_func :
.cfi_startproc
stp x29, x30, [sp, #-16]!
.cfi_def_cfa_offset 16
.cfi_offset 29, -16
.cfi_offset 30, -8
mov x29, sp
.cfi_def_cfa_register 29
ldp x29, x30, [sp], #16
.cfi_def_cfa rsp, 0
ret
.cfi_endproc
.size aarch64_func, .-aarch64_func
AArch64 주소 지정 방식
ldr x0, [x1]
ldr x0, [x1, #8]
ldr x0, [x1, x2]
ldr x0, [x1, x2, lsl #3]
ldr x0, [x1, #8]!
ldr x0, [x1], #8
adr x0, my_label
adrp x0, my_page
add x0, x0, :lo12:my_page
RISC-V 아키텍처 기능
GNU Assembler의 RISC-V(RV32/RV64) 지원 기능입니다.
RISC-V 전용 옵션
옵션 설명
-march=rv64gcISA 문자열. rv32/rv64 + 확장 (g=IMAFD, c=압축)
-mabi=lp64dABI: ilp32, lp64, lp64d(하드 FP) 등
-mno-relax링커 릴랙세이션(instruction relaxation) 비활성화
-mcsr-checkCSR 읽기/쓰기 접근 권한 체크
RISC-V 머신 지시자
지시자 설명
.option rvc압축(C) 명령어 허용
.option norvc압축 명령어 금지 (정렬 보장)
.option relax릴랙세이션 활성화 (기본)
.option norelax릴랙세이션 비활성화
.option picPIC 코드 생성 모드
.option nopic비-PIC 모드
.option push현재 옵션 상태 저장
.option pop저장된 옵션 상태 복원
.attribute tag, valueABI/아키텍처 속성 설정 (RISC-V ELF ABI)
RISC-V 어셈블러 수정자
lui a0, %hi(symbol)
addi a0, a0, %lo(symbol)
auipc a0, %pcrel_hi(symbol)
addi a0, a0, %pcrel_lo(1b)
auipc a0, %got_pcrel_hi(symbol)
ld a0, %pcrel_lo(1b)(a0)
auipc a0, %tls_ie_pcrel_hi(tls_var)
ld a0, %pcrel_lo(1b)(a0)
.global riscv_func
.type riscv_func, @function
riscv_func :
.cfi_startproc
addi sp, sp, -16
.cfi_adjust_cfa_offset 16
sd ra, 8(sp)
.cfi_offset ra, -8
sd s0, 0(sp)
.cfi_offset s0, -16
addi s0, sp, 16
.cfi_def_cfa s0, 0
ld ra, 8(sp)
ld s0, 0(sp)
addi sp, sp, 16
.cfi_restore ra
.cfi_restore s0
.cfi_def_cfa sp, 0
ret
.cfi_endproc
.size riscv_func, .-riscv_func
커널에서 as 활용 실전
리눅스 커널은 as를 광범위하게 사용합니다. 커널 특유의 패턴과 매크로를 이해하면 커널 어셈블리 코드를 읽고 수정할 수 있습니다.
커널 심볼 매크로 (linkage.h)
SYM_FUNC_START (my_kernel_func)
movq %rdi, %rax
ret
SYM_FUNC_END (my_kernel_func)
SYM_FUNC_START _LOCAL(__helper)
ret
SYM_FUNC_END (__helper)
SYM_CODE_START (entry_SYSCALL_64)
.cfi_startproc simple
.cfi_signal_frame
.cfi_endproc
SYM_CODE_END (entry_SYSCALL_64)
SYM_DATA_START(my_kernel_data)
.long 0x12345678
SYM_DATA_END _LABEL(my_kernel_data, SYM_L_GLOBAL, my_kernel_data_end)
커널 섹션 활용 패턴
.section ".init.text" , "ax"
.global kernel_init_func
kernel_init_func :
ret
SYM_FUNC_START (patched_func)
.cfi_startproc
1: nop
.pushsection .altinstructions, "a"
.long 1b - .
.long 2f - .
.byte X86_FEATURE_SOMETHING
.byte 1b_size
.byte 2f_size
.popsection
.pushsection .altinstr_replacement, "ax"
2: pause
.popsection
ret
.cfi_endproc
SYM_FUNC_END (patched_func)
SYM_FUNC_START (safe_copy)
.cfi_startproc
1: movq (%rsi), %rax
movq %rax, (%rdi)
ret
.cfi_endproc
.pushsection __ex_table, "a"
.quad 1b
.quad .Lfixup
.popsection
.Lfixup :
xorl %eax, %eax
ret
SYM_FUNC_END (safe_copy)
커널 빌드에서 as 활용
# Kbuild가 .S 파일을 빌드할 때 내부적으로:
# 1) cpp로 전처리 (CONFIG_* 매크로 치환)
# 2) as로 어셈블
# 수동으로 동일한 과정 재현:
# 전처리만
gcc -E -D__ASSEMBLY__ -Iinclude -o foo.i arch/x86/kernel/foo.S
# 어셈블 리스팅 생성 (디버깅 용도)
as -aln=foo.lst --64 -o foo.o foo.i
# 커널 크로스 컴파일
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- arch/arm64/kernel/entry.o
# 특정 오브젝트만 상세 어셈블리 확인
objdump -dS arch/x86/kernel/entry_64.o | less
자주 쓰는 커널 GAS 패턴
lock addl $1 , (%rdi )
lock xaddl %eax , (%rdi )
lock cmpxchgl %ecx , (%rdi )
mfence
lfence
sfence
lock addl $0 , (%rsp )
.Lwait :
pause
cmpb $0 , (%rdi )
jne .Lwait
movq %gs :40, %rax
movq %rax , -8(%rbp )
관련 문서
공식 문서
관련 페이지
GNU Binutils — ar, nm, objcopy, objdump, readelf, strip 등 Binutils 도구 전체
어셈블리 종합 — GCC 인라인 어셈블리, AT&T/Intel 문법 비교, x86_64/ARM64 ABI
GCC 완전 가이드 — GCC 컴파일 파이프라인, 최적화, 인라인 어셈블리
ELF 파일 형식 — ELF 구조, 섹션 헤더, 심볼 테이블, 재배치 엔트리
GDB 완전 가이드 — DWARF 활용, 소스 레벨 디버깅, CFI 정보 확인
메모리 배리어 — x86 TSO, ARM64 약한 메모리 모델, 배리어 명령어