Lesson 16-Source Code Analysis of Mainstream Assemblers

NASM Source Code Parsing (parser.c): Core Syntax Parsing Logic

// nasm/parser.c snippet
static void parse_instruction(void) {
    Token *tok = lex_token();
    switch (tok->type) {
        case TOKEN_INSTRUCTION:
            parse_operand_list();   // Parse operands
            encode_instruction();   // Generate machine code
            break;
        case TOKEN_DIRECTIVE:
            handle_directive();     // Handle pseudo-instructions
            break;
        // ...
    }
}

// Instruction encoding example
static void encode_instruction(void) {
    Instruction *insn = current_insn;
    switch (insn->opcode) {
        case OP_MOV:
            encode_mov(insn->operands);  // Encode MOV instruction
            break;
        // ...
    }
}

Key Features:

  • Multi-pass scanning architecture (preprocessing → parsing → encoding)
  • Modular instruction encoder
  • Symbol table management

GNU as Source Code Analysis (tc-i386.c): x86 Backend Core Structure

// gas/config/tc-i386.c snippet
static const struct insn_template i386_optab[] = {
    {"mov", 2, {X86_REG, X86_MEM}, 0, 0, 0, 0, 0, 0, 0, 0},
    {"add", 2, {X86_REG, X86_IMM}, 0, 0, 0, 0, 0, 0, 0, 0},
    // ...
};

// Instruction matching and encoding
static void md_assemble(char *line) {
    template = match_template(line);  // Match instruction template
    output_insn(template);            // Generate machine code
}

Distinctive Features:

  • Complex addressing mode handling
  • Automatic prefix byte insertion
  • Instruction length calculation

LLVM Assembler Architecture: Key Components of the X86 Backend

// llvm/lib/Target/X86/X86AsmPrinter.cpp
void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) {
    switch (MI->getOpcode()) {
        case X86::MOV32rr:
            EmitMOV32rr(MI);  // Output MOV instruction
            break;
        // ...
    }
}

// Instruction selection and optimization
void X86InstrInfo::copyPhysReg(MachineBasicBlock &MBB,
                               MachineBasicBlock::iterator I,
                               const DebugLoc &DL,
                               unsigned DestReg,
                               unsigned SrcReg,
                               bool KillSrc) const {
    // Physical register copy optimization
}

Core Advantages:

  • SSA-based intermediate representation
  • Instruction scheduling and register allocation
  • Target-independent optimizations

Operating System Kernel Assembly Implementations

Linux Kernel Assembly Code

Boot Code (arch/x86/boot/header.S):

; Real-mode boot code
bits 16
org 0x7C00
start:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00          ; Set stack pointer

    ; Load GDT
    lgdt [gdt_desc]

    ; Switch to protected mode
    mov eax, cr0
    or eax, 1
    mov cr0, eax
    jmp 0x08:protected_mode

System Call Entry (arch/x86/entry/entry_64.S):

; System call handler
SYSCALL_DEFINE0(exit) {
    asm volatile(
        "movq %%rsp, %0\t"      // Save stack pointer
        "movq %1, %%rsp\t"      // Switch to kernel stack
        "pushq %2\t"            // Save return address
        "jmp sys_exit\t"        // Jump to C handler function
        : "=m"(old_rsp)
        : "r"(kernel_stack), "r"(return_addr)
    );
}

Windows NT Kernel Assembly

NTOSKRNL Critical Assembly (ntoskrnl/ex/system.asm):

Context Switch Implementation:

; Context switching implementation
KiSwapContext:
    ; Save current thread context
    push rbx
    push rbp
    push r12-r15

    ; Load new thread context
    mov rax, [NewThread]
    mov rsp, [rax+THREAD_RSP]

    ; Restore new thread registers
    pop r15
    pop r14
    pop r13
    pop r12
    pop rbp
    pop rbx
    ret

System Call Dispatch (NTOSKRNL/ke/systemcall.asm):

; Fast system call entry
KiSystemCall64:
    swapgs
    mov [GS:CPU_KERNEL_RSP], rsp
    mov rsp, [GS:CPU_SYSCALL_RSP]
    push rcx
    jmp KiSystemService

Embedded Systems Assembly Optimization

Bootloader Assembly Code

U-Boot Startup Snippet (arch/arm/cpu/armv7/start.S):

; ARMv7 startup code
reset:
    mrc p15, 0, r0, c0, c0, 0   ; Read CPU ID
    bl cpu_init_cp15            ; Initialize CP15 coprocessor
    bl _main                    ; Jump to C code

; Exception vector table
vector_base:
    ldr pc, [pc, #-0x10]        ; Reset vector
    ldr pc, [pc, #-0x10]        ; Undefined instruction
    ldr pc, [pc, #-0x10]        ; SWI instruction
    ; ...

Embedded Real-Time System Scheduling

FreeRTOS ARM Cortex-M Scheduler (core_cm3.S):

; Context switching implementation
PendSV_Handler:
    mrs r0, psp                 ; Get process stack pointer
    stmdb r0!, {r4-r11}         ; Save registers
    str r0, [r1]                ; Save stack pointer to TCB

    ; Load new task stack pointer
    ldr r0, [r2]
    ldmia r0!, {r4-r11}         ; Restore registers
    msr psp, r0
    bx lr

Compiler and Interpreter Internals: Assembly Generation

GCC Backend Assembly Generation

Instruction Selection Process (gcc/config/i386/i386.c):

Instruction Template Matching:

// Instruction template matching
static const struct insn_template i386_optab[] = {
    {"add", 2, {X86_REG, X86_REG}, 0, 0, 0, 0, 0, 0, 0, 0},
    {"add", 2, {X86_REG, X86_IMM}, 0, 0, 0, 0, 0, 0, 0, 0},
    // ...
};

// Machine description driven
void expand_movsi(rtx operands[]) {
    rtx src = operands[1];
    rtx dest = operands[0];
    if (REG_P(src) && REG_P(dest))
        emit_insn(gen_movsi_regreg(src, dest));
    else
        emit_insn(gen_movsi_memreg(src, dest));
}

LLVM IR to Assembly Conversion

Optimization Process (llc tool):

Instruction Scheduling Optimization:

// Instruction scheduling optimization
void ScheduleDAGInstrs::schedule() {
    // Build dependency graph
    buildSchedGraph(AA);
    // Compute priorities
    computeDFSResult();
    // List scheduling
    ListScheduler LS(this);
    LS.schedule();
}

// Register allocation
void RAGreedy::allocatePhysRegs() {
    // Compute live intervals
    calculateLiveIntervals();
    // Perform register allocation
    runGreedyAllocation();
}

Comparative Technical Analysis

Instruction Encoding Strategy Comparison

FeatureNASMGNU asLLVM
Instruction MatchingHash table-basedTemplate-basedPattern matching
Prefix HandlingExplicit insertionAutomatic insertionAuto-generation
Error RecoveryStrict modePermissive modeIntermediate mode

Kernel Assembly Optimization Techniques

Context Switch Optimization Example:

// Linux switch_to macro optimization
#define switch_to(prev, next, last) \
do { \
    save_fp(prev); \
    restore_fp(next); \
    __switch_to_asm(prev, next); \
} while (0);

Windows Context Switch Optimization:

KiSwapContext:
    ; Use hardware-assisted context switching
    swapgs
    mov [GS:CPU_RSP], rsp
    mov rsp, [GS:SYSCALL_RSP]

Debugging and Reverse Engineering

Kernel Debugging Techniques

QEMU + GDB Kernel Debugging:

# Launch QEMU in debug mode
qemu-system-x86_64 -s -S -kernel bzImage

# Connect with GDB
gdb vmlinux
(gdb) target remote :1234
(gdb) break start_kernel
(gdb) continue

Kernel Disassembly Examples:

# Linux kernel disassembly
objdump -d -M intel vmlinux > kernel.asm

# Windows kernel disassembly
IDA Pro ntoskrnl.exe

By deeply analyzing these source codes and technologies, developers can:

  • Develop customized operating system components
  • Optimize critical performance paths
  • Implement secure kernel extensions
  • Build cross-platform compilers
  • Perform low-level performance tuning
  • Contribute to open-source projects
Share your love