00 NDS makefile 및 NDS 파일 생성 과정 분석

원본 :  http://kkamagui.springnote.com/pages/415876

 

들어가기 전에...

 

0,시작하면서...

 새로운 프로젝트를 생성하고 소스파일을 생성한 후 컴파일 및 링크를 거치면 *.nds 파일이 생기게 된다. C/CPP 파일을 이용해서 어떻게 NDS 롬 파일 형태를 만드는 걸까?

 소스 파일에서 NDS 파일이 만들어지기까지 아래의 단계를 거치게 된다.

  1. c/cpp 파일을 컴파일 하여 .o 파일 생성
  2. .o 파일을 링크하여 .elf 파일 생성
  3. .elf 파일을 objcopy 프로그램으로 binary 포맷으로 변경, .arm9/.arm7 파일 생성
  4. .arm9/.arm7 파일을 ndstool.exe 프로그램으로 함께 묶어 .nds 파일 생성

  위의 과정을 확실히 알아보기 위해서는 소스가 컴파일 및 링크되는 규칙이 담긴 makefile을 분석해야 한다.

 

1.makefile 분석

 makefile은 아래와 같은 내용으로 되어있다

  1. #---------------------------------------------------------------------------------
    .SUFFIXES:
    #---------------------------------------------------------------------------------
  2. ifeq ($(strip $(DEVKITARM)),)
    $(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM)
    endif
  3. include $(DEVKITARM)/ds_rules
  4. #---------------------------------------------------------------------------------
    # TARGET is the name of the output
    # BUILD is the directory where object files & intermediate files will be placed
    # SOURCES is a list of directories containing source code
    # INCLUDES is a list of directories containing extra header files
    #---------------------------------------------------------------------------------
    TARGET  := $(shell basename $(CURDIR))
    BUILD  := build
    SOURCES  := gfx source data 
    INCLUDES := include build
  5. #---------------------------------------------------------------------------------
    # options for code generation
    #---------------------------------------------------------------------------------
    ARCH := -mthumb -mthumb-interwork
  6. # note: arm9tdmi isn't the correct CPU arch, but anything newer and LD
    # *insists* it has a FPU or VFP, and it won't take no for an answer!
    CFLAGS := -g -Wall -O2\
        -mcpu=arm9tdmi -mtune=arm9tdmi -fomit-frame-pointer\
       -ffast-math \
       $(ARCH)
  7. CFLAGS += $(INCLUDE) -DARM9
    CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
  8. ASFLAGS := -g $(ARCH)
    LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -mno-fpu -Wl,-Map,$(notdir $*.map)
  9. #---------------------------------------------------------------------------------
    # any extra libraries we wish to link with the project
    #---------------------------------------------------------------------------------
    LIBS := -lfat -lnds9   <== -lfat는 libfat를 사용하기 위함이다.
     
     
    #---------------------------------------------------------------------------------
    # list of directories containing libraries, this must be the top level containing
    # include and lib
    #---------------------------------------------------------------------------------
    LIBDIRS := $(LIBNDS)
     
    #---------------------------------------------------------------------------------
    # no real need to edit anything past this point unless you need to add additional
    # rules for different file extensions
    #---------------------------------------------------------------------------------
    ifneq ($(BUILD),$(notdir $(CURDIR)))
    #---------------------------------------------------------------------------------
     
    export OUTPUT := $(CURDIR)/$(TARGET)
     
    export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
    export DEPSDIR := $(CURDIR)/$(BUILD)
  10. CFILES  := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
    CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
    SFILES  := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
    BINFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.bin)))
     
    #---------------------------------------------------------------------------------
    # use CXX for linking C++ projects, CC for standard C
    #---------------------------------------------------------------------------------
    ifeq ($(strip $(CPPFILES)),)
    #---------------------------------------------------------------------------------
     export LD := $(CC)
    #---------------------------------------------------------------------------------
    else
    #---------------------------------------------------------------------------------
     export LD := $(CXX)
    #---------------------------------------------------------------------------------
    endif
    #---------------------------------------------------------------------------------
  11. export OFILES := $(BINFILES:.bin=.o) \
         $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
     
    export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
         $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
         $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
         -I$(CURDIR)/$(BUILD)
     
    export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
     
    .PHONY: $(BUILD) clean
     
    #---------------------------------------------------------------------------------
    $(BUILD):
     @[ -d $@ ] || mkdir -p $@  <== 확실한 뜻은 모르겠으나 $(BUILD) 디렉토리가 생성되어있거나 디렉토리 만들어라 인것 같음
     @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
     
    #---------------------------------------------------------------------------------
    clean:
     @echo clean ...
     @rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds $(TARGET).arm9 $(TARGET).ds.gba
     
     
    #---------------------------------------------------------------------------------
    else
     
    DEPENDS := $(OFILES:.o=.d)
     
    #---------------------------------------------------------------------------------
    # main targets
    #---------------------------------------------------------------------------------
    $(OUTPUT).nds :  $(OUTPUT).arm9
    $(OUTPUT).arm9 : $(OUTPUT).elf
    $(OUTPUT).elf : $(OFILES)
     
    #---------------------------------------------------------------------------------
    %.o : %.bin
    #---------------------------------------------------------------------------------
     @echo $(notdir $<)
     $(bin2o)
     
     
    -include $(DEPENDS)
     
    #---------------------------------------------------------------------------------------
    endif
    #---------------------------------------------------------------------------------------

 make에서 사용되는 괴 문자들이 많이 보이는데, 괴문자에 대한 자세한 내용은 02 간단한 Make 사용법에서 보도록하고 $@는 ':' 의 좌측을 의미하고 $<는 우측을 의미한다는 것 정도만 알아두자.

 재미있는 점은 코드가 thumb 모드로 생성된다는 것이다. thumb 모드는 32bit 명령이 16bit로 줄어든 형태로써 코드의 크기를 30% 정도 더 줄일 수 있는 장점이 있다.

 

 include하여 사용하고 있는 ds_rules 파일을 살펴보자. ds_rules 파일은 devkitARM 폴더 아래에 있고 아래와 같은 내용을 담고 있다.

  1. ifeq ($(strip $(DEVKITPRO)),)
    $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro)
    endif
  2. include $(DEVKITARM)/base_rules
  3. LIBNDS := $(DEVKITPRO)/libnds
  4. #---------------------------------------------------------------------------------
    %.ds.gba: %.nds
     @dsbuild $<
     @echo built ... $(notdir $@)
  5. #---------------------------------------------------------------------------------
    %.nds: %.arm9
     @ndstool -c $@ -9 $<
     @echo built ... $(notdir $@)
  6. #---------------------------------------------------------------------------------
    %.arm9: %.elf
     @$(OBJCOPY) -O binary $< $@
     @echo built ... $(notdir $@)
     
    #---------------------------------------------------------------------------------
    %.arm7: %.elf
     @$(OBJCOPY) -O binary $< $@
     @echo built ... $(notdir $@)
  7. #---------------------------------------------------------------------------------
    %.elf:
     @echo linking $(notdir $@)
     @$(LD)  $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@

 어떻게 하여 NDS 파일을 만드는지 위를 보면 확실히 알 수 있다 rule에 의해서 elf -> arm9 -> nds -> ds.gba 파일이 만들어지는 것이다. 그럼 ds_rules에서 inlcude하는 base_rules는 어떻게 구성되어있을까?

 

 아래는 base_rules 내용이다.

  1. #---------------------------------------------------------------------------------
    # path to tools - this can be deleted if you set the path in windows
    #---------------------------------------------------------------------------------
    export PATH  := $(DEVKITARM)/bin:$(PATH)
  2. #---------------------------------------------------------------------------------
    # the prefix on the compiler executables
    #---------------------------------------------------------------------------------
    PREFIX  := arm-eabi-
  3. export CC := $(PREFIX)gcc
    export CXX := $(PREFIX)g++
    export AS := $(PREFIX)as
    export AR := $(PREFIX)ar
    export OBJCOPY := $(PREFIX)objcopy
  4. #---------------------------------------------------------------------------------
    %.a:
    #---------------------------------------------------------------------------------
     @echo $(notdir $@)
     @rm -f $@
     $(AR) -rc $@ $^
  5. #---------------------------------------------------------------------------------
    %.arm.o: %.arm.cpp
     @echo $(notdir $<)
     $(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -marm-c $< -o $@
     
    #---------------------------------------------------------------------------------
    %.arm.o: %.arm.c
     @echo $(notdir $<)
     $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) -marm -c $< -o $@
  6. #---------------------------------------------------------------------------------
    %.iwram.o: %.iwram.cpp
     @echo $(notdir $<)
     $(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -marm -mlong-calls -c $< -o $@
     
    #---------------------------------------------------------------------------------
    %.iwram.o: %.iwram.c
     @echo $(notdir $<)
     $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) -marm -mlong-calls -c $< -o $@
  7. #---------------------------------------------------------------------------------
    %.itcm.o: %.itcm.cpp
     @echo $(notdir $<)
     $(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -marm -mlong-calls -c $< -o $@
     
    #---------------------------------------------------------------------------------
    %.itcm.o: %.itcm.c
     @echo $(notdir $<)
     $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) -marm -mlong-calls -c $< -o $@
  8. #---------------------------------------------------------------------------------
    %.o: %.cpp
     @echo $(notdir $<)
     $(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -c $< -o $@
     
    #---------------------------------------------------------------------------------
    %.o: %.c
     @echo $(notdir $<)
     $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) -c $< -o $@

  9. #---------------------------------------------------------------------------------
    %.o: %.s
     @echo $(notdir $<)
     $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@
  10. #---------------------------------------------------------------------------------
    %.o: %.S
     @echo $(notdir $<)
     $(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@
  11. #---------------------------------------------------------------------------------
    # canned command sequence for binary data
    #---------------------------------------------------------------------------------
    define bin2o
     bin2s $< | $(AS) $(ARCH) -o $(@)
     echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h
     echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h
     echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h
    endef

 각 파일의 확장자에 따라서 어떻게 빌드해야 할지에 대한 구체적인 내용이 나와있다. 그럼 이 make 파일을 이용하여 build를 하면 컴파일 되고 적당히 링크되어 1차적으로 elf 파일이 생성될 것이다.

 컴파일해서 나온 .o 파일은 별다른 의문이 없지만 링크되어 나온 elf 파일은 조금 생각해 봐야 한다. objcopy로 elf 파일을 binary 형태로 변화시키는데, 그렇다면 elf 파일도 NDS 롬 파일 형태에 맞도록 뭔가 달라져야 할 것이 아닌가? 링크는 LDFLAG 옵션에 들어있는 ds_arm9.specs 을 참고하도록 되어있으니 Linker 쪽을 살펴보자.

 

2. 링커 스크립트(Linker Script) 분석

 ds_arm9.specs 파일은 devkitARM\arm-eabi\lib 폴더 아래에서 살펴볼 수 있다.

  1. %rename link                old_link
    %rename link_gcc_c_sequence old_gcc_c_sequence
  2. *link_gcc_c_sequence:
    %(old_gcc_c_sequence) --start-group -lsysbase -lc --end-group
  3. *link:
    %(old_link) -T ds_arm9.ld%s
  4. *startfile:
    ds_arm9_crt0%O%s crti%O%s crtbegin%O%s

 구체적인 내용은 확실히 모르겠지만 일단 ds_arm9.ld 파일을 참조하고 ds_arm9_crt, crti, crtbegin 으로 시작하는 파일과 관계가 있음을 유추할 수 있다. ds_arm9.ld 파일을 한번 살펴보면 아래와 같다.

  1. OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
    OUTPUT_ARCH(arm)
    ENTRY(_start)
  2. MEMORY {
  3.  rom : ORIGIN = 0x08000000, LENGTH = 32M
     ewram : ORIGIN = 0x02000000, LENGTH = 4M - 4k
     dtcm : ORIGIN = 0x0b000000, LENGTH = 16K
     itcm : ORIGIN = 0x01000000, LENGTH = 32K
    }
  4. __itcm_start = ORIGIN(itcm);
    __ewram_end = ORIGIN(ewram) + LENGTH(ewram);
    __eheap_end = ORIGIN(ewram) + LENGTH(ewram);
    __dtcm_start = ORIGIN(dtcm);
    __dtcm_top = ORIGIN(dtcm) + LENGTH(dtcm);
    __irq_flags = __dtcm_top - 0x08;
    __irq_vector = __dtcm_top - 0x04;
  5. __sp_svc = __dtcm_top - 0x100;
    __sp_irq = __sp_svc - 0x100;
    __sp_usr = __sp_irq - 0x100;
  6. SECTIONS
    {
     .init :
     {
      __text_start = . ;
      KEEP (*(.init))
      . = ALIGN(4);  /* REQUIRED. LD is flaky without it. */
      } >ewram = 0xff
  7.  .plt : { *(.plt) } >ewram = 0xff
  8.  .text :   /* ALIGN (4): */
     {
      *(EXCLUDE_FILE (*.itcm*) .text)
  9.   *(.text.*)
      *(.stub)
      /* .gnu.warning sections are handled specially by elf32.em.  */
      *(.gnu.warning)
      *(.gnu.linkonce.t*)
      *(.glue_7)
      *(.glue_7t)
      . = ALIGN(4);  /* REQUIRED. LD is flaky without it. */
     } >ewram = 0xff
  10.  .fini           :
     {
      KEEP (*(.fini))
     } >ewram =0xff
  11.  __text_end = . ;
  12.  .rodata :
     {
      *(.rodata)
      *all.rodata*(*)
      *(.roda)
      *(.rodata.*)
      *(.gnu.linkonce.r*)
      SORT(CONSTRUCTORS)
      . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
     } >ewram = 0xff
  13.   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ewram
       __exidx_start = .;
      .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } >ewram
       __exidx_end = .;
      /* Ensure the __preinit_array_start label is properly aligned.  We
         could instead move the label definition inside the section, but
         the linker would then create the section even if it turns out to
         be empty, which isn't pretty.  */
      . = ALIGN(32 / 8);
      PROVIDE (__preinit_array_start = .);
      .preinit_array     : { KEEP (*(.preinit_array)) } >ewram = 0xff
      PROVIDE (__preinit_array_end = .);
      PROVIDE (__init_array_start = .);
      .init_array     : { KEEP (*(.init_array)) } >ewram = 0xff
      PROVIDE (__init_array_end = .);
      PROVIDE (__fini_array_start = .);
      .fini_array     : { KEEP (*(.fini_array)) } >ewram = 0xff
      PROVIDE (__fini_array_end = .);
  14.  .ctors :
     {
     /* gcc uses crtbegin.o to find the start of the constructors, so
      we make sure it is first.  Because this is a wildcard, it
      doesn't matter if the user does not actually link against
      crtbegin.o; the linker won't look for a file to match a
      wildcard.  The wildcard also means that it doesn't matter which
      directory crtbegin.o is in.  */
      KEEP (*crtbegin.o(.ctors))
      KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
      KEEP (*(SORT(.ctors.*)))
      KEEP (*(.ctors))
      . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
     } >ewram = 0xff
  15.  .dtors :
     {
      KEEP (*crtbegin.o(.dtors))
      KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
      KEEP (*(SORT(.dtors.*)))
      KEEP (*(.dtors))
      . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
     } >ewram = 0xff
  16.  .eh_frame :
     {
      KEEP (*(.eh_frame))
      . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
     } >ewram = 0xff
  17.  .gcc_except_table :
     {
      *(.gcc_except_table)
      . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
     } >ewram = 0xff
     .jcr            : { KEEP (*(.jcr)) } >ewram = 0
     .got            : { *(.got.plt) *(.got) *(.rel.got) } >ewram = 0
  18.  .ewram ALIGN(4) :
     {
      __ewram_start = ABSOLUTE(.) ;
      *(.ewram)
      *ewram.*(.text)
      . = ALIGN(4);   /* REQUIRED. LD is flaky without it. */
     } >ewram = 0xff

  19.  .data ALIGN(4) :
     {
      __data_start = ABSOLUTE(.);
      *(.data)
      *(.data.*)
      *(.gnu.linkonce.d*)
      CONSTRUCTORS
      . = ALIGN(4);
      __data_end = ABSOLUTE(.) ;
     } >ewram = 0xff

  20.  __dtcm_lma = . ;
  21.  .dtcm __dtcm_start : AT (__dtcm_lma)
     {
      *(.dtcm)
      *(.dtcm.*)
      . = ALIGN(4);
      __dtcm_end = ABSOLUTE(.);
     } >dtcm = 0xff

  22.  __itcm_lma = __dtcm_lma + SIZEOF(.dtcm);
  23.  .itcm __itcm_start : AT (__itcm_lma)
     {
      *(.itcm)
      *itcm.*(.text)
      . = ALIGN(4);
      __itcm_end = ABSOLUTE(.);
     } >itcm = 0xff
     
     .sbss __dtcm_end :
     {
      __sbss_start = ABSOLUTE(.);
      __sbss_start__ = ABSOLUTE(.);
      *(.sbss)
      . = ALIGN(4);    /* REQUIRED. LD is flaky without it. */
      __sbss_end = ABSOLUTE(.);
     } >dtcm

  24.  __bss_lma = __itcm_lma + SIZEOF(.itcm) ;
     __appended_data = __itcm_lma + SIZEOF(.itcm) ;
     .bss __bss_lma : AT (__bss_lma)
     {
      __bss_start = ABSOLUTE(.);
      __bss_start__ = ABSOLUTE(.);
      *(.dynbss)
      *(.gnu.linkonce.b*)
      *(.bss*)
      *(COMMON)
      . = ALIGN(4);    /* REQUIRED. LD is flaky without it. */
      __bss_end = ABSOLUTE(.) ;
      __bss_end__ = __bss_end ;
     } >ewram

  25.  _end = . ;
     __end__ = . ;
     PROVIDE (end = _end);
  26.  /* Stabs debugging sections.  */
     .stab 0 : { *(.stab) }
     .stabstr 0 : { *(.stabstr) }
     .stab.excl 0 : { *(.stab.excl) }
     .stab.exclstr 0 : { *(.stab.exclstr) }
     .stab.index 0 : { *(.stab.index) }
     .stab.indexstr 0 : { *(.stab.indexstr) }
     .comment 0 : { *(.comment) }
     /* DWARF debug sections.
      Symbols in the DWARF debugging sections are relative to the beginning
      of the section so we begin them at 0.  */
     /* DWARF 1 */
     .debug          0 : { *(.debug) }
     .line           0 : { *(.line) }
     /* GNU DWARF 1 extensions */
     .debug_srcinfo  0 : { *(.debug_srcinfo) }
     .debug_sfnames  0 : { *(.debug_sfnames) }
     /* DWARF 1.1 and DWARF 2 */
     .debug_aranges  0 : { *(.debug_aranges) }
     .debug_pubnames 0 : { *(.debug_pubnames) }
     /* DWARF 2 */
     .debug_info     0 : { *(.debug_info) }
     .debug_abbrev   0 : { *(.debug_abbrev) }
     .debug_line     0 : { *(.debug_line) }
     .debug_frame    0 : { *(.debug_frame) }
     .debug_str      0 : { *(.debug_str) }
     .debug_loc      0 : { *(.debug_loc) }
     .debug_macinfo  0 : { *(.debug_macinfo) }
     /* SGI/MIPS DWARF 2 extensions */
     .debug_weaknames 0 : { *(.debug_weaknames) }
     .debug_funcnames 0 : { *(.debug_funcnames) }
     .debug_typenames 0 : { *(.debug_typenames) }
     .debug_varnames  0 : { *(.debug_varnames) }
     .stack 0x80000 : { _stack = .; *(.stack) }
     /* These must appear regardless of  .  */
    }

 위에서 보면 링크에 관련된 온갖 정보들이 다 들어있음을 알 수 있다. 주의깊게 볼 부분은 파란색 부분인데, 이 부분의 주소값들이 다 NDS의 메모리맵과 관계가 있다. 링크 스크립트에 대한 내용은 06 링커 스크립트(Linker Scrpit) 페이지를 참조하도록 하고 위에서 계속 반복해서 나오는 섹션에 대한 정보만 간단히 알아보자.

  1.  .text :   /* ALIGN (4): */
     {
      *(EXCLUDE_FILE (*.itcm*) .text)
  2.   *(.text.*)
  3.   *(.stub)
      /* .gnu.warning sections are handled specially by elf32.em.  */
      *(.gnu.warning)
      *(.gnu.linkonce.t*)
      *(.glue_7)
      *(.glue_7t)
      . = ALIGN(4);  /* REQUIRED. LD is flaky without it. */
     } >ewram = 0xff

  >ewram = 0xff 의 뜻은 .text 영역의 섹션을 위에서 정의했던 ewram 영역에 밀어넣되, .text 섹션의 값은 0xff 값으로 초기화 하라는 것이다. 즉 ewram 영역에 .text 영역이 생길꺼고 그 영역은 0xff로 체워진다. sbss 및 bss 영역 역시 ewram에 위치하는 걸 봐서 데이터도 모두 ewram 영역에 위치하므로, 너무 큰 배열이나 값들의 연속은 메모리를 지나치게 사용할 우려가 있으므로 주의해야 한다. 

 위에서 보면 모든 많은 섹션들이 >ewram으로 표시된 것을 보아 거의 ewram 영역인 메인 메모리에 올라감을 알 수 있다. 그렇다면 ROM과 같은 GBA 카드리지 영역은 안쓰는 걸까? 현재( 2007/08/16 21:59:18 )까지 분석된 결과로는 거의 안쓰이는 것 같다.

 

3.map 파일 분석

 위에서 보았던 링크 스크립트 파일을 이용하여 링커가 elf 파일을 생성하게 된다. 이때 생성된 elf 파일에 구체적인 데이터는 map 파일을 열어보면 알 수 있다. 아래는 map 파일의 내용을 간단히 추린 것이다.

  1.  Memory Configuration
  2. Name             Origin             Length             Attributes
    rom              0x08000000         0x02000000
    ewram            0x02000000         0x003ff000
    dtcm             0x0b000000         0x00004000
    itcm             0x01000000         0x00008000
    *default*        0x00000000         0xffffffff
  3. Linker script and memory map
  4.                 0x01000000                __itcm_start = 0x1000000
                    0x023ff000                __ewram_end = 0x23ff000
                    0x023ff000                __eheap_end = 0x23ff000
                    0x0b000000                __dtcm_start = 0xb000000
                    0x0b004000                __dtcm_top = 0xb004000
                    0x0b003ff8                __irq_flags = (__dtcm_top - 0x8)
                    0x0b003ffc                __irq_vector = (__dtcm_top - 0x4)
                    0x0b003f00                __sp_svc = (__dtcm_top - 0x100)
                    0x0b003e00                __sp_irq = (__sp_svc - 0x100)
                    0x0b003d00                __sp_usr = (__sp_irq - 0x100)
  5. .init           0x02000000      0x22c
                    0x02000000                __text_start = .
     *(.init)
    .init          0x02000000      0x220 d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/../../../../arm-eabi/lib/thumb/ds_arm9_crt0.o
                    0x02000000                _start
     .init          0x02000220        0x4 d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/thumb/crti.o
                    0x02000220                _init
     .init          0x02000224        0x8 d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/thumb/crtn.o
                    0x0200022c                . = ALIGN (0x4)
  6. .plt
     *(.plt)
  7. .text           0x02000240    0x1cae0
     *(EXCLUDE_FILE(*.itcm*) .text)
     .text          0x02000240        0x0 d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/../../../../arm-eabi/lib/thumb/ds_arm9_crt0.o
     .text          0x02000240        0x0 d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/thumb/crti.o
     .text          0x02000240       0x70 d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/thumb/crtbegin.o
     .text          0x020002b0      0x178 2DRaster.o
                    0x020002b0                DrawLine(void*, int, int, int, int, unsigned short)
                    0x0200041c                DrawPixel(void*, int, int, unsigned short)
                    0x02000370                DrawBox(void*, int, int, int, int, unsigned short, bool)
     .text          0x02000428      0x124 BootStub.o
                    0x020004d4                InitVideoMode()
                    0x02000428                SetIrq()
                    0x02000474                IrqHandler()
  8. ........................
  9. .data           0x0201ff64    0x138e0
                    0x0201ff64                __data_start = <code 342> (.)
     *(.data)
     .data          0x0201ff64        0x0 d:/ndsl_develo
  10.  .data          0x0201ff64        0x4 d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/thumb/crtbegin.o
                    0x0201ff64                __dso_handle
     .data          0x0201ff68        0x0 2DRaster.o
     .data          0x0201ff68        0x0 BootStub.o
     .data          0x0201ff68        0x0 DC.o
     .data          0x0201ff68        0x0 DrawingWindow.o
     .data          0x0201ff68        0x0 FileManager.o
     .data          0x0201ff68    0x12c20 Font.o
                    0x0201ff68                g_vusHanFont
  11.  ........................
  12.  .bss            0x020339d4    0x18e68 load address 0x020339d4
                    0x020339d4                __bss_start = <code 342> (.)
                    0x020339d4                __bss_start__ = <code 342> (.)
     *(.dynbss)
     *(.gnu.linkonce.b*)
     *(.bss*)
     .bss           0x020339d4        0x0 d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/../../../../arm-eabi/lib/thumb/ds_arm9_crt0.o
     .bss           0x020339d4        0x0 d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/thumb/crti.o
     .bss           0x020339d4       0x1c d:/ndsl_develop/devkitpro/devkitarm/bin/../lib/gcc/arm-eabi/4.1.1/thumb/crtbegin.o
     .bss           0x020339f0        0x0 2DRaster.o
     .bss           0x020339f0        0x3 BootStub.o
                    0x020339f2                g_ucIPCCount
                    0x020339f1                g_ucKeysCount
                    0x020339f0                g_ucVBlankCount
     .bss           0x020339f3        0x0 DC.o
     .bss           0x020339f3        0x0 DrawingWindow.o
     .bss           0x020339f3        0x0 FileManager.o
     .bss           0x020339f3        0x0 Font.o
     .bss           0x020339f3        0x0 InformationWindow.o
     *fill*         0x020339f3        0x1 00
     .bss           0x020339f4    0x18568 Main.o
                    0x020339f4                g_clInformationWindow
                    0x02033b60                g_clBackGroundWindow


map 파일의 내용들을 보면 위와 같은 내용을 확인할 수 있는다. 우리가 작성한 코드는 .text 영역에 있으며, 우리가 사용한 변수들은 .bss 영역에 있는 것을 확인할 수 있다. 그리고 초기화 된 데이터 같은 경우(g_vusHanFont)는 .data에 존재하는 것을 발견할 수 있다.

 

 

4.Objcopy 분석

 일단 컴파일 및 링크의 결과로 elf 파일이 생성되었음을 알았다. 그럼 objcopy를 통해 binary 파일로 만들면 어떤 일이 발생하는 걸까? objcopy에 대한 내용은 http://www.gnu.org/software/binutils/manual/html_chapter/binutils_3.html 에서 찾을 수 있다. 내용을 조금 살피다 보면 -O binary 옵션에 대한 내용을 찾을 수 있는데 아래와 같다.

objcopy can be used to generate a raw binary file by using an output target of `binary' (e.g., use `-O binary'). When objcopy generates a raw binary file, it will essentially produce a memory dump of the contents of the input object file. All symbols and relocation information will be discarded. The memory dump will start at the load address of the lowest section copied into the output file.

 각 섹션들의 덤프를 해준다는 이야기 같다. 제일 Lowest한 주소부터 그대로 섹션 덤프를 해준다는데... 확인을 해보자.

 KKAMAGUI NOTEPAD의 .elf 파일 용량은 500Kbyte이다. 하지만 .arm9 파일 용량은 207Kbyte이다. 사이즈가 상당히 줄었음을 알 수 있다. 그럼 데이터는 어떻게 구성되어있는 걸까?

 

4.1 objdump로 .elf 분석 및 .arm9 파일 분석

 elf 파일의 정보는 devkitPro를 설치하면 있는 objdump.exe 프로그램을 이용하면 볼 수 있다. 이 프로그램과 파일의 정보를 hex로 보여주는 울트라 에디트와 같은 Hex Editor를 사용해서 파일 내용을 비교해 보면 된다. 여기서 Hex Editor는 HxD를 이용하기로 한다. HxD는 http://mh-nexus.de/hxd/ 페이지에서 받을 수 있다.

 아래는 HxD로 Notepad.arm9 파일을 연 화면이다.

 

HxD1.PNG

<HxD의 .arm9 분석 화면>

  일단 대충 이런 내용이구나 정보만 알아놓고 objdump 프로그램을 이용해서 Notepad.elf 파일의 섹션 정보를 보자.

 

HxD2.PNG

<objdump의 실행화면>

 LMAVMA의 의미에 대해서 잠깐 알아보자.

  • LMA(Loaded Memory Address) :  실제 elf 파일이 메모리에 로드되는 위치. VMA와 일반적으로 동일
  • VMA(Virtual Memory Address) : 프로그램이 실행될 때 메모리의 위치. ROM 같은 경우는 메모리에 로드된 후에 실행될 위치로 코드가 이동되어야 하므로 일반적으로 다름.

 

4.2 TCM의 위치 및 메모리 로드 분석

  여기서는 ITCM 영역이 링커 스크립트에 의해 여러가지 조건 때문에 제일 앞쪽(0번 섹션)에 위치하지 못하여 어긋난 것 같다. NDS 롬 헤더에 TCM에 대한 정보 필드가 없고, objcopy 프로그램이 단순히 섹션의 내용을 VMA 순서대로 덤프를 하는데... 저 섹션들이 의미가 있을까?

 "답은 의미가 있다"  이다.

 \devkitPro\devkitARM\arm-eabi\lib 폴더에 가면 ARM9의 CRT 코드를 찾을 수 있다. ds_arm9_crt0.s 파일이 그것인데, 아래와 같은 내용을 담고 있다.

  1.  ldr r1, =__itcm_lma  @ Copy instruction tightly coupled memory (itcm section) from LMA to VMA (ROM to RAM)
     ldr r2, =__itcm_start
     ldr r4, =__itcm_end
     bl CopyMemCheck
  2.  ldr r1, =__dtcm_lma @ Copy data tightly coupled memory (dtcm section) from LMA to VMA (ROM to RAM)
     ldr r2, =__dtcm_start
     ldr r4, =__dtcm_end
     bl CopyMemCheck

 crt0 코드는 링크될때 가장 앞부분에 위치하는 코드로써 홈브루의 초기화 및 기타 작업을 담당하는 것 같다. 위에서 보면 __itcm 같은 변수를 사용하는 것을 알 수 있는데, 이 값들은 링크 스크립트 파일(ds_arm9.ld)을 보면 잘 정의되어있다. 그러므로 binary로 생성될때는 정확한 위치에 있지 않지만, 실행하면서 해당 위치에 옮겨지기때문에 가능하다.

 

4.3 .elf의 Hex 데이터 분석

그럼 이제 첫번째 섹션 .init의 시작위치를 알았으므로 이제 HxD 프로그램을 이용해서 .elf 파일의 0x8000 영역으로 옮겨보자.

HxD3.PNG

 위의 .arm9 파일과 동일함을 알 수 있다.

 다른 섹션들도 따라가보면 똑같음을 볼 수 있고, 이로서 objcopy의 -O binary 옵션이 하는 일은 섹션의 VMA 순서에 따라서 첫번째 VMA 주소를 시작으로 메모리 내용을 덤프하는 것과 같음을 알 수 있다.

 .arm9파일의 내용은 단순한 코드와 데이터의 집합이다. 

 

5. NDS Tool 분석

 이제 마지막 단계만 남았다. .arm9 파일을 이용해서 .nds 파일을 생성하는 단계이다. NDS 파일의 헤더 정보에 대해서는 05 NDS 롬(ROM) 파일 포맷(Format) 분석를 살펴보자. 해당 페이지의 결과를 보면 단순히 헤더를 붙이고 ARM9과 ARM7 코드를 붙여넣는 작업이 전부임을 알 수 있다. ㅜ_ㅜ... 어찌나 간단한지...

 참고삼아 ndstool.exe 를 이용하여 분석한 Notepad.nds 파일 정보를 붙여넣었다.

  1. D:\ndsl_develop\MyProject\Notepad-업로드용>ndstool -i Notepad.nds
    Nintendo DS rom tool 1.33 - Jan 28 2007 21:02:20
    by Rafael Vuijk, Dave Murphy,  Alexei Karpenko
    Header information:
    0x00    Game title                      .
    0x0C    Game code                       ####
    0x10    Maker code
    0x12    Unit code                       0x00
    0x13    Device type                     0x00
    0x14    Device capacity                 0x01 (2 Mbit)
    0x15    reserved 1                      000000000000000000
    0x1E    ROM version                     0x00
    0x1F    reserved 2                      0x04
    0x20    ARM9 ROM offset                 0x200
    0x24    ARM9 entry address              0x2000000
    0x28    ARM9 RAM address                0x2000000
    0x2C    ARM9 code size                  0x33C74
    0x30    ARM7 ROM offset                 0x34000
    0x34    ARM7 entry address              0x37F8000
    0x38    ARM7 RAM address                0x37F8000
    0x3C    ARM7 code size                  0x12B8
    0x40    File name table offset          0x35400
    0x44    File name table size            0x9
    0x48    FAT offset                      0x35600
    0x4C    FAT size                        0x0
    0x50    ARM9 overlay offset             0x0
    0x54    ARM9 overlay size               0x0
    0x58    ARM7 overlay offset             0x0339d4

    0x5C    ARM7 overlay size               0x0
    0x60    ROM control info 1              0x007F7FFF
    0x64    ROM control info 2              0x203F1FFF
    0x68    Icon/title offset               0x0
    0x6C    Secure area CRC                 0x0000 (-, homebrew)
    0x6E    ROM control info 3              0x051E
    0x70    ARM9 ?                          0x0
    0x74    ARM7 ?                          0x0
    0x78    Magic 1                         0x00000000
    0x7C    Magic 2                         0x00000000
    0x80    Application end offset          0x00035600
    0x84    ROM header size                 0x00000200
    0xA0    ?                               0x4D415253
    0xA4    ?                               0x3131565F
    0xA8    ?                               0x00000030
    0xAC    ?                               0x53534150
    0xB0    ?                               0x00963130
    0x15C   Logo CRC                        0x9E1A (OK)
    0x15E   Header CRC                      0xAD78 (OK)

  2. D:\ndsl_develop\MyProject\Notepad-업로드용>

  실제 .arm9 파일의 크기는 0x339D4의 크기인데, NDS Rom 파일이 생성될때 해당 롬의 크기가 0x2A0 만큼 더 큰 것을 보아 약간의 공간이 더 추가된 듯 하다. 그럼 어떤 데이터가 추가된 것을까? 아래는 .arm9 파일과 .nds 파일의 마지막 부분의 내용을 비교한 것이다.

HxD4.PNG

 

HxD5(1).PNG

<.arm9 파일(좌측)과 .nds 파일(우측) 내용>

 그림이 좀 심하게 일그러졌는데, 첨부파일의 큰 그림을 참조하도록 하고, 여기서 알 수 있는 사실은 추가된 0x2A0 공간만큼 0으로 체워져있다는 사실이다. 따라서 .arm9 바이너리 파일을 그대로 덤프한다는 사실에는 변함이 없다.

 

마치면서...

 NDS 롬(ROM) 파일 생성을 분석하는데, 자그마치 이틀이나 걸렸다. 상당히 복잡했고 이것 저것 볼 것도 많았지만, 확실히 NDS 파일에 대해서 알 수 있는 좋은 기회였다. 결국 NDS 파일은 ARM9과 ARM7 코드와 데이터를 포함하는 간단한 구조였던 것이다.

 이제 NDS는 나의 장난감이 된 것이나 다름없다. >ㅁ<)/~ 기다려라~ NDS야. 하하하하핫~~!!!

 

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

+ Recent posts