2014년 10월 23일 목요일

aptitude

ubuntu는 sw 관리가 windows 처럼 편하게 되어 있다.
데비안 계열은 가장 보편적으로 dpkg / apt-get을 이용하는데
나는 언젠가부터 aptitude를 사용하고 있다.
aptitude를 이용하면 SW 간에 dependency 오류를 자동으로 감지해주고 고쳐주기 때문에 사용했는데 다음utility들도 활용할 수 있다.
  • aptitude-curses: TUI로 보여줌
  • aptitude-create-state-bundle: 현재 aptitude 상태를 저장
  • aptitude-run-state-bundle: 복구
아주 편하다.

2014년 10월 21일 화요일

C main design

일반적으로 bootup code는 startup.S로 작성한다.
startup.S에는 다음 내용이 들어간다.
- vector table
- stack pointer 초기화
- 전역변수 초기화
- 기타 이미지 로딩
- pc를 main으로 이동, 꼭 main이 아니어도 되며, startup.S에서 branch 해주기만 하면 된다. 이 때 부터 C 코드를 사용 가능함.
- C code를 사용하려면 기본적으로 변수 초기화, sp 초기화 등을 해야 한다. 하지 않으면 당연히 data abort!

변수의 class
- register variable
- 예약어는 register이며, 쓴다고 항상 적용되지 않고, 성능에 영향을 준다고 판단하면 그냥 stack에 저장한다.
- 단, register를 사용하면, 주소값을 읽을 수 없다. 원래 register에 저장하라고 하는 것이니깐.
- local variable
- auto 예약어인데 일반적으로 생략된다.
- static variable
- global variable

memory layout
- 아래서부터,
- TEXT(program code)
- RODATA(constant)
- DATA(initialized data)
- BSS(uninitialized data)
- HEAP(BSS 이후로 커감)
- STACK(메모리 끝에서 부터 내려옴)
- 원하는 section에 넣으려면, linker를 이용한다.
- t32에서 section 확인하려면, sYmbol.List.SECtion

Inline assembly
- C보다 빠른 처리를 위함이고, __volatile__ 예약어를 사용해야 컴파일러가 맘대로 최적화 하지 않는다
- 일반적인 형태:
- __asm__ __volatile__("" : "" : "" : "")
- clobber는 개발자가 직접 register를 제어한다면 설정, compiler는 사용 못하도록 하며, 생략 가능하다.
- ex.
__asm__ __volatile__("add %0, %1, %2" : "=r" (result) : "r" (kind), "r" (embedded) : "r0", "r1")
r0, r1은 안쓴다는 의미임

Colling Convention
- ARM사에서 제시한 표준: PCS(procedure call standard), APCS(ARM procedure call standard), TPCS(Thumb ~), AAPCS(ARM Arch ~)
- r0, r1은 argument / result
- r2, r3는 argument / scratch register 등등..
- r13: stack pointer
- r14: link register

Call Assembly - C와 Assembly 상호 호출 방법
- Assembly에서 global keyword를 사용하여 선언하고 c에서 사용
- C symbol은 Assembly에서 .extern keyword 사용하여 호출
예시
startup.S
.extern main
BL main
.global MMU_DisableiCache
MMU_DisableiCache :
mrc p15,0,r0,c1,c0,0
bic r0, r0,#R1_I
mcr p15,0,r0,c1,c0,0
mov pc,lr

main.c
int main() {
MMU_DisableiCache();
return 0;
}

Call C++
- C와 Assembly처럼 심볼을 그대로 가져다 쓸 수 없음.
- compiler가 함수명을 가공하기 때문임.
- c++에서는 extern "C" { }를 이용해서 C의 symbol을 가져옴.

compile

C 파일을 compile 하면(main.c)
- 전처리기가 main.i를 생성
- compiler가 main.S를 생성
- assembler가 main.o를 생성
- 사람이 읽을 수 있는 언어를 hex code로 변경해 준다.
- objdump -S main.o <-- object의 정보를 확인할 수 있다.
- linker가 a.elf를 생성
- linker가 하는 역할은 object 파일을 메모리에 mapping을 하는 것
- input file들이 같은 메모리에 위치하여 서로 symbol을 사용할 수 있게 해준다.
- 각 object들의 .text(code), .data 들을 section별로 정리해서 elf로 저장한다.
- objcopy로 a.bin을 생성
- objcopy는 elf의 header, debug info 등을 날려버리고 binary만을 추출한다.
- 디버깅을 하려면 그래서 elf가 필요하다.
- gcc option인 --save-temps를 넣으면 위의 이미지들을 삭제하지 않는다.

booting mode

Booting mode는 어떤 메모리를 이용하는지에 따라 여러가지가 있다.
보통 Hardware pin으로 설정할 수 있도록 하며, 각 mode별로 memory mapping이 달라진다.
- NOR flash mode: NOR는 비싸고 부피가 큰 메모리로, 주소에 접근해서 실행 가능하다. 그래서 부트로더를 NOR flash에 넣고 부팅할 수 있다.
- NAND flash mode:
-NAND는 싸고 집적도가 높다. 하지만 주소가 없고 블락별로 data를 읽을 수 있도록 되어 있다. 즉 I/O port만 있어서 전자적 신호로 Page단위로 load하여 주소 접근이 가능한 다른 메모리에 복사하여 사용해야 한다.
- 일반적으로 MCU 내부에 IRAM에 복사해서 사용한다.
- 간단히 요약하자면, IROM이 NAND에서 IRAM으로 부트 코드를 복사한 다음에 PC를 IRAM으로 이동해 준다. IRAM은 DRAM을 초기화 하고 OS image를 얹은 다음 PC를 DRAM으로 이동해 주어 'stepping stone' 방식으로 부팅하게 된다.
- 일반적으로 boot code를 이동하는 IRAM 주소는 vendor datasheet에 있음.
- 그 외에 network booting mode / usb booting mode 등이 있다.

Linker script

일반적으로 복잡한 Memory map이 필요한 binary를 위해 사용 된다.
Linker가 필요한 정보
- 입력 section들이 어떻게 region으로 그룹화 되었는지 그룹화 정보
- Memory map에서 각 region이 어디에 위치한지 정보

Section name and macro
- .text: __TEXT_START__ / __TEXT_END__
- .bss: __BSS_START__ / __BSS_END__
- .data ..

Linker script를 사용하기 위한 옵션
- ld 옵션에 -T FILE 또는 --script FILE 으로 사용함.
- ex.
LDFLAGS = ~~~ -T linker.ld -Map $(OUTDIR)/$(BIN).map

Linker script 작성
- OUTPUT 형식
- OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm")
- OUTPUT_ARCH(arm)
- entry point
- ENTRY(_start)
- sections
- SECTIONS
{
section-command
section-command
..
}
- ex.
SECTIONS
{
        .text __TEXT_START__ :
        {
                __TEXT_START__ = .;
                *startup.o (.text);
                *main.o (.text);
                *(.text);
                . = ALIGN(4);
                __TEXT_END__ = .;
        }
}
작성 예시
OUTPUT_FORMAT("elf32-littlearm")
OUTPUT_ARCH(arm)

ENTRY(_start)

__TEXT_START__ = 0x20000000;

SECTIONS
{
        .text __TEXT_START__ :
        {
                __TEXT_START__ = .;
                *startup.o (.text);
                *main.o (.text);
                *(.text);
                . = ALIGN(4);
                __TEXT_END__ = .;
        }

        .rodata :
        {
                __RODATA_START__ = .;
                *startup.o (.rodata);
                *main.o (.rodata);
                *(.rodata);
                __RODATA_END__ = .;
                . = ALIGN(4);
        }
        .bss :
        {
                __BSS_START__ = .;
                *startup.o (.bss);
                *main.o (.bss);
                *(.bss);
                __BSS_END__ = .;
                . = ALIGN(4);
        }

        _end = .;
        __end = _end;
        PROVIDE(end = .);
}

2014년 10월 16일 목요일

man page 작성법..

linux 표준함수, 쉘 바이너리 등 terminal에서 실행하려는 것들에 대한 궁금증이 있으면,
가장 먼저 찾아보는 man page!

man page에 대해 알아보고 작성하는 방법을 정리한다.
man page를 열 때
$ man $(NUMBER) $(TARGET)
NUMBER에 해당하는 숫자는 무엇일까?
섹션 이름
1 누구나 실행할 수 있는 사용자 명령 (User Commands)
2 시스템 호출 (System Calls), 즉 커널에서 제공하는 함수
3 C 라이브러리 함수 (C Library functions)
4 디바이스와 네트워크 인터페이스 (Devices and Network Interfaces), 즉 /dev 디렉토리에 있는 특별한 파일들
5 파일 포맷 (File Formats), 예를 들어 /etc/passwd
6 게임과 데모 (Games and Demos)
7 환경, 테이블, 매크로 (Environments, Tables, and Macros) 등의 잡다한 여러 가지들
8 시스템 관리자만 실행할 수 있는 시스템 관리용 명령들 (Maintenance Commands)
9 커널 루틴에 관련된 문서들의 리눅스만의 독자적인 저장소
n 새로운 문서들의 저장소. 보다 적당한 장소로 이동된다.
o 오래된 문서들의 저장소. 유예 기간으로 보관되고 있는 것들이다.
l 특정 시스템에 대한 로컬 문서들
출처: 모름(잘 정리된 것이 있어 긁어왔는데 출처를 깜빡했다.. 원문의 작성자 분께 죄송하다는 말씀 드리며, 삭제 요청 시 삭제하도록 하겠습니다.)

본격적으로 파일은 어떻게 만드느냐...
파일명은 NUMBER에 해당하는 숫자를 확장자로 넣어야 한다.
즉, 예를 들면,
$ vi /usr/local/man/myman.1
이런 방식이다.
기본적으로 /usr/local/man과 /usr/share/man 등의 folder에 주로 넣으며, 확장하고 싶으면 $(PATH)에 넣어두면 된다.
man page를 열어보면 알겠지만 헤더도 있고 자동으로 인덴트도 되어 있는 것을 볼 수 있다.
.SH + NAME / SYNOPSIS / DESCRIPTION / OPTIONS / SEE ALSO / COPYRIGHT / AUTHOR 등 구분을 하고
.TP / .PP로 행을 구분한다.

예시는 위에 언급한 폴더에 아무거나 열어보면 된다.

2014년 10월 15일 수요일

merge remote git project!

다른 repository의 git project를 merge 하려면?

1. remote server의 정보를 추가
2. fetch(정보를 받아옴)
3. merge

간단하다.
.git/config를 열어보면 다음과 같이 remote server의 주소가 있다

[remote "A"]
         url = ssh://address = server/address
         projectname = project/name
         fetch = +x:x

나는 remote B의 git을 merge 할 것이다. 그러려면 remote B의 서버 주소가 필요하다.
 git remote add [-t ] [-m ] [-f] [--tags|--no-tags] [--mirror=]
<예시>
$ git remote add -t branch myname ssh://xxx
그러면, .git/config에 다음이 추가된다.
[remote "lap_8916_kk"]
         url = ssh://xxx
         fetch = +y:y

그런 뒤
$ git fetch myname
하면 fetch를 하고,
$git merge myname/branch
하면 merge가 시작된다.

참 쉽죠잉?