1000sj
SJ CODE
1000sj
전체 방문자
오늘
어제
  • 분류 전체보기
    • Algorithms
      • Crypto
      • Formal Methods
    • Security
      • Fuzzing
      • Exploit
    • System Programming
      • Kernel
      • Compiler
      • Device Driver
      • Emulator
      • Assembly
      • Memory
      • Network
    • Architecture
      • ARM
      • RISC-V
    • Cloud Computing
      • Infrastructure
      • SDN
    • TroubleShooting
    • Performance improvements
      • Parrelel Processing
      • HPC
    • ETC
      • 문화 생활
      • 커뮤니티

인기 글

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
1000sj

SJ CODE

System Programming/Emulator

Bochs v2.2.1 소스 코드 분석 #2 Bochs 구조와 동작방식

2026. 1. 18. 19:34

Bochs는 C++로 작성된 IA-32 아키텍쳐 PC 에뮬레이터이다. 쉽게 말해 소프트웨어로 PC 하드웨어 전체를 흉내내는 프로그램이다. Intel x86 CPU, 일반적인 IO 장치 및 BIOS를 에뮬레이션한다. 현재 Bochs는 386, 486, Pentium, Pentium Pro, AMD64 CPU를 에뮬레이트할 수 있으며 MMX, SSESSE, 3DNow! 명령어 세트를 포함한다.

Bochs가 실행할 수 있는 운영체제에는

  • Linux
  • DOS
  • Windows

가 포함된다. 다양한 모드의 컴파일을 사용할 수 있으며 일부는 아직 개발 중이다. 전형적인 응용은 완전한 x86 PC 에뮬레이터를 제공하는 것으로 x86 CPU, hw 장치 및 저장장치를 포함한다. 

http://bochs.sourceforge.net/ 에서 최신 버전을 다운로드 할 수 있다.

 

버전 2.2.1 소스코드 구성

Bochs v2.2.1 은 약 300 개의 소스파일과 20만줄의 코드로 구성되어있다. 주요 폴더로는 BIOS, Cpu, Memory, Iodev, GUI 등이 있다. 루트 디렉토리에는 VC 프로젝트 파일(.dsp, .dsw) 과 일부 헤더 파일(Bochs.h), main 함수가 있는 main.cpp 이 있다.

 

루트 디렉토리에는 다음과 같은 폴더가 있다.

├── bios
├── build
├── bx_debug
├── cpu
├── disasm
├── doc
├── docs-html
├── dynamic
├── font
├── fpu
├── gui
├── host
├── instrument
├── iodev
├── memory
├── misc
├── patches
└── plex86

이 중 일부 프로젝트는 Bochs 보조 도구를 컴파일하여 생성한다. 예를 들어 bximage 를 컴파일하려면 bximage.exe가 생성되는데 이 도구는 가상 하드디스크를 생성하는데 사용된다.

Bochs.exe는 bochs 프로젝트가 컴파일되어 생성된 것으로 다른 여러 프로젝트에 의존한다.

따라서 bochs.exe 를 컴파일하려면 먼저 iodev, cpu, memory 등의 의존 프로젝트를 컴파일하여 해당 lib 파일을 생성해야 사용할 수 있다.

프로젝트 클래스 구조

모든 클래스는 로그 기능을 제공하는 logfunctions 클래스를 상속받는다. 이 클래스는 간단한 로그 기능을 완성하여 디버깅 시 관련 정보를 출력하기 편하다. 

 

모든 클래스 중 핵심 클래스 세가지는 다음과 같다. 

  • BX_CPU: CPU 기술
  • BX_MEM_C: 메모리 기술
  • IO device 파생 클래스들: 각종 하드웨어 장치

그리고 PC 시스템 보드 를 기술하는 클래스 bx_pc_system_c 가 이들을 연결하여 완전한 PC 를 구성한다. 각 장치는 stub 클래스를 통해 통일된 인터페이스 (read/write) 를 제공한다.

통일된 인터페이스를 위해 Bochs는 각 장치에 대해 stub 클래스를 정의했다. stub 클래스의 가상 함수는 통일된 인터페이스를 제공하며 예를 들어 저장 장치 읽기/쓰기, I/O 읽기/쓰기 등이 있다. 이러한 가상 함수의 최종 구현은 각 구체적인 장치의 함수에서 완성된다.

 

프레임워크 구조 분석

Bochs 프로젝트의 중요 클래스

(1) VM 콘솔 인터페이스 클래스

class BOCHSAPI bx_gui_c : public logfunctions

bx_gui_c 에서 파생된 클래스

  • bx_win32_gui_c
  • bx_svga_gui_c
  • bx_term_gui_c
  • bx_sdl_gui_c
  • bx_wx_gui_c
  • bx_rfb_gui_c
  • bx_nogui_gui_c
  • bx_macintosh_gui_c
  • bx_beos_gui_c
  • bx_amigaos_gui_c
  • bx_carbon_gui_c
  • bx_x_gui_c

config.h 에서 설정하여 콘솔 인터페이스를 선택한다.

#define BX_WITH_X11 0
#define BX_WITH_BEOS 0
#define BX_WITH_WIN32 1      // ← 활성화됨
#define BX_WITH_MACOS 0
#define BX_WITH_CARBON 0
#define BX_WITH_NOGUI 0
#define BX_WITH_TERM 0
#define BX_WITH_RFB 0
#define BX_WITH_AMIGAOS 0
#define BX_WITH_SDL 0
#define BX_WITH_SVGA 0
#define BX_WITH_WX 0

 

(2) CPU 에뮬레이션

클래스 BX_CPU_C 는 CPU를 기술하는데 쓰인다.

class BOCHSAPI BX_CPU_C : public logfunctions

클래스 bxInstruction_c 는 하나의 명령어를 정의하며 기본 클래스가 없다.

 

(3) Memory 에뮬레이션

클래스 BX_MEM_C

class BOCHSAPI BX_MEM_C : public logfunctions    // memory/memory.h

변수

#if BX_PROVIDE_CPU_MEMORY==1
  #if BX_SMP_PROCESSORS==1
    BOCHSAPI extern BX_MEM_C    bx_mem;    // 외부 전역 변수
  #else
  BOCHSAPI extern BX_MEM_C    *bx_mem_array[BX_ADDRESS_SPACES]; // 다중 프로세서의 경우, 
                                                                  // 내부 그룹 사용
  #endif   /* BX_SMP_PROCESSORS */

#endif   /* BX_PROVIDE_CPU_MEMORY==1 */

전역 변수

BOCHSAPI BX_CPU_C    bx_cpu;
BOCHSAPI BX_MEM_C    bx_mem;

 

(4) I/O device 에뮬레이션

 

  • Class bx_devices_c : public logfunctions
  • Class bx_devmodel_c : public logfunctions    // iodev/iodev.h

파생 클래스 목록

 

  • bx_busm_stub_c   //bus mouse
  • bx_cmos_stub_c
  • bx_devmodel_c
  • bx_dma_stub_c
  • bx_floppy_stub_c
  • bx_hard_drive_stub_c
  • bx_keyb_stub_c
  • bx_ne2k_stub_c
  • bx_pci2isa_stub_c
  • bx_pci_ide_stub_c
  • bx_pci_stub_c
  • bx_pic_stub_c
  • bx_serial_stub_c
  • bx_speaker_stub_c
  • bx_usb_stub_c
  • bx_vga_stub_c

 

진입 함수 main() 및 Win32 Gui 초기화

Main.cpp 는 매크로 정의를 통해 컴파일러 진입 함수를 선택한다. 기본값은 main이다.

진입합수 선택

#if defined(__WXMSW__)    WinMain()      // wxWidgets/win32 사용
#if !defined(__WXMSW__)   main()

 

 

Main 함수

int main (int argc, char *argv[])
{
    bx_startup_flags.argc = argc;    // 전역 변수, 타입은 BOCHSAPI, 명령행 파라미터 기록용
    bx_startup_flags.argv = argv;
#if BX_WITH_SDL && defined(WIN32)
    // if SDL/win32, try to create a console window.
    RedirectIOToConsole ();
    #endif
    return bxmain ();
}

 

bxmain() 함수

int bxmain () {
  ...
  bx_init_siminterface ();   // create the SIM object
  
      if (bx_init_main (bx_startup_flags.argc, bx_startup_flags.argv) < 0) 
      return 0;
      ...
    int status = SIM->configuration_interface (ci_name, CI_START);
    if (status == CI_ERR_NO_TEXT_CONSOLE)
      BX_PANIC (("Bochs needed the text console, but it was not usable"));

호출 bx_init_siminterface ();

void bx_init_siminterface ()
{
  siminterface_log = new logfunctions ();
  siminterface_log->put ("CTRL");
  siminterface_log->settype(CTRLLOG);
  if (SIM == NULL) 
    SIM = new bx_real_sim_c();
}

 

bx_init_main()

#define BX_USE_TEXTCONFIG 1      // 모드를 텍스트 설정 모드로 정의
class bx_real_sim_c : public bx_simulator_interface_c {
...
  config_interface_callback_t ci_callback;
...
}

함수 ci_callback() 이 bx_real_sim_c::configuration_interface() 에서 호출되며 configuration_interface()는 두 곳에서 호출된다.

  • bx_gui_c::config_handler 중에서 config_handler 가 bx_gui_c::init에서 호출됨
  • bxmain()

 

begin_simulation()에서 bx_begin_simulation() 호출

함수 bx_begin_simulation()

1. load_and_init_display_lib() 호출 // GUI 객체 초기화

2. 만약 BX_DEBUGGER가 정의되어있으면 bx_dbg_main(argc, argv) 호출

3. bx_init_hardware() 호출 // 이 함수의 실행 전에 GUI가 초기화

4. bx_load32bitOSimagehack

5. SIM->set_init_done(1) // 초기화 완료 표시

6. ...

7. BX_CPU(0)->cpu_loop(1); //CPU 루프 진입

 

bx_init_hardware()

int bx_init_hardware()
{
  // all configuration has been read, now initialize everything.

  if (SIM->get_param_enum(BXP_BOCHS_START)->get ()==BX_QUICK_START) {
    for (int level=0; level<N_LOGLEV; level++) {
      int action = SIM->get_default_log_action (level);
#if !BX_USE_CONFIG_INTERFACE
      if (action == ACT_ASK) action = ACT_FATAL;
#endif
      io->set_log_action (level, action);
    }
  }

  bx_pc_system.init_ips(bx_options.Oips->get ());

  Bit32u memSize = bx_options.memory.Osize->get ()*1024*1024;

// 단일 프로세서 상황에서
#if BX_SMP_PROCESSORS==1
  // 메모리 초기화
  BX_MEM(0)->init_memory(memSize);

  // 로드 BIOS and VGABIOS
  BX_MEM(0)->load_ROM(bx_options.rom.Opath->getptr (), bx_options.rom.Oaddress->get (), 0);
  BX_MEM(0)->load_ROM(bx_options.vgarom.Opath->getptr (), 0xc0000, 1);

  // Then load the optional ROM images
  ...

  // CPU 초기화
  BX_CPU(0)->init (BX_MEM(0));
  BX_CPU(0)->set_cpu_id(0);
  // CPU 점검
  BX_CPU(0)->sanity_checks();
  // 뭘 하는지 모르겠음
  BX_INSTR_INIT(0);
  BX_CPU(0)->reset(BX_RESET_HARDWARE);

#if BX_DEBUGGER == 0
  // 외부 장치 초기화
  DEV_init_devices();
  DEV_reset_devices(BX_RESET_HARDWARE);
  bx_gui->init_signal_handlers ();
  bx_pc_system.start_timers();
#endif

  return(0);
}

 

 

win32 GUI의 시동과 초기화

plugin.h 중에 매크로 정의

#define PLUG_load_plugin(name,type) {lib##name##_LTX_plugin_init(NULL,type,0,NULL);}

libwin32_LTX_plugin_init 함수를 찾으려했지만 직접 정의된 곳은 없었다.

다음의 절차로 IMPLEMENT_GUI_PLUGIN_CODE(win32) 매크로가 컴파일 시점에 함수를 자동 생성한다.

 

(1) 플러그인 로드 매크로 (plugin.h)

#define PLUG_load_plugin(name, type) \
    { lib##name##_LTX_plugin_init(NULL, type, 0, NULL); }

이 매크로를 사용하면

PLUG_load_plugin(win32, PLUGTYPE_OPTIONAL)

다음과 같이 확장된다.

{ libwin32_LTX_plugin_init(NULL, PLUGTYPE_OPTIONAL, 0, NULL); }

 

2단계: GUI 플러그인 구현 매크로 (gui/gui.h)

#define IMPLEMENT_GUI_PLUGIN_CODE(gui_name)                              \
    int lib##gui_name##_LTX_plugin_init(plugin_t *plugin,               \
                                         plugintype_t type,              \
                                         int argc, char *argv[]) {       \
        genlog->info("installing %s module as the Bochs GUI", #gui_name);\
        theGui = new bx_##gui_name##_gui_c();                            \
        bx_gui = theGui;                                                  \
        return(0);                                                        \
    }

이 매크로는 함수 전체를 생성한다.

 

3단계: 실제 사용 (gui/win32.cpp)

IMPLEMENT_GUI_PLUGIN_CODE(win32)

이 한줄이 다음과 같이 확장된다.

int libwin32_LTX_plugin_init(plugin_t *plugin,
                              plugintype_t type,
                              int argc, char *argv[]) {
    genlog->info("installing win32 module as the Bochs GUI");
    theGui = new bx_win32_gui_c();    // Win32 GUI 객체 생성
    bx_gui = theGui;                   // 전역 포인터에 할당
    return(0);
}

 

다른 gui 정의도 같은 방식이다.

// gui/x11.cpp
IMPLEMENT_GUI_PLUGIN_CODE(x11)
→ libx11_LTX_plugin_init() 함수 생성
→ new bx_x11_gui_c() 호출

// gui/sdl.cpp
IMPLEMENT_GUI_PLUGIN_CODE(sdl)
→ libsdl_LTX_plugin_init() 함수 생성
→ new bx_sdl_gui_c() 호출

// gui/nogui.cpp
IMPLEMENT_GUI_PLUGIN_CODE(nogui)
→ libnogui_LTX_plugin_init() 함수 생성
→ new bx_nogui_gui_c() 호출

 

Bochs의 작업 방식

실제 CPU의 원리와 마찬가지로 Bochs 가상머신 CPU의 실행방식은

명령어 가져오기(fetch) -> 실행 (execute) -> 결과 출력 이다.

 

CPU 리셋 후 real mode에 진입하며

EIP = 0x0000FFF0;

CS의 cache 내의 Base = 0xFFFF0000,

두가지를 더하면 CS:EIP=FFFFFFF0이 된다. 이 위치는 BIOS ROM이 있는 위치이다. 사용자 프로그램은 이 곳에서부터 실행을 시작하며 CPU 루프에 진입 후 명령어 가져오기와 실행 작업 외에도 매번 이벤트 플래그 비트를 검사한다.

 

만약 리셋은 종료 명령이 없으면 CPU 루프는 계속 진행된다.  

 

 

'System Programming > Emulator' 카테고리의 다른 글

linux vm stack 개발 노트 #1 kvm 내부 구조  (0) 2025.12.19
KVM 가상화 환경 구성  (0) 2024.04.01
    'System Programming/Emulator' 카테고리의 다른 글
    • linux vm stack 개발 노트 #1 kvm 내부 구조
    • KVM 가상화 환경 구성
    1000sj
    1000sj

    티스토리툴바