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 각각 고유 지시자와 확장 문법이 있습니다.

단계별 이해

  1. 기본 변환 이해
    as -o foo.o foo.s로 어셈블리 파일을 오브젝트로 변환하고 objdump -d foo.o로 결과를 확인합니다.
  2. 문법 규칙 익히기
    AT&T 문법의 레지스터 접두사(%), 상수 접두사($), 명령어 접미사(b/w/l/q)를 익힙니다.
  3. 지시자 활용
    .section으로 섹션을 지정하고, .global/.type/.size로 심볼 속성을 설정합니다.
  4. CFI 추가
    함수에 .cfi_startproc/.cfi_endproc을 추가하여 DWARF 언와인드 정보를 생성합니다.
  5. 아키텍처 확장
    필요한 아키텍처(-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

심볼 명명 규칙

섹션과 재배치

어셈블러는 소스를 여러 섹션으로 나눠 처리합니다. 각 섹션은 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"가변+1NULL 종료 문자열
.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)          

관련 문서

공식 문서

관련 페이지