[HTML5&CSS] Text based elements

다음 Text based elements를 살펴 보자.

  • Headings
  • Paragraphs
  • Inline text elements
  • Lists

HTML 안의 heading element들은 H1 에서 H6 까지 6단계까지 있다.

문단(paragraph를) 나타내는 태그는 p이다. 문단 단위로 p 태그를 사용해서 나눠준다.

Inline text elements는 여러가지가 있다.
강조(emphasized)를 원한다면 em을 사용한다. 예를 들면, 원하는게 있다면 <em>지금</em> 하라.
정말 중요하게 표시하고 싶다면, strong을 사용한다. 예를 들면, 이건 정말 <strong>중요</strong>한 거.
하이퍼링크를 사용하고 싶다면, a를 사용한다. 예를 들면, <a href=”https://blog.dasomoli.org”>다솜돌이의 블로그</a>
별 뜻 없이 어떤 내용을 다른 내용들과 나누고 싶을 때 사용하는 태그는 span이다. div와의 차이점은 inline으로 사용한다는 점이다. 일반적인 용도로 다른 text들과 다르게 특정 text의 색을 바꾸는데 많이 사용한다. 예를 들면 다음과 같다.

list는 세가지 타입이 있다. unordered list (ul), ordered list (ol), definition list (dl).

unordered list와 ordered list는 list item (li) 으로 항목을 나타낸다.

CSS를 사용해서 list-style 속성으로 square, circle, upper-alpha, upper-roman 등으로 list의 스타일을 정해줄 수 있다.

definition list (dl)은 자주 쓰이진 않는다. 어떤 용어의 정의와 설명을 서술할 때 사용한다. 용어(term)에는 dt를 사용하고, 설명(description)에는 dd를 사용한다. 예를 들면 다음과 같다.

CSS reset 하기

각기 다른 브라우저들이 서로 다르게 렌더링하기 때문에 이를 위해서 CSS를 reset한다.

Eric Mayer가 만든 좀 더 복잡한 CSS reset 코드가 있다. http://meyerweb.com/eric/tools/css/reset/ 에서 찾을 수 있다.

 

CSS text properties들을 알아보자.

두 그룹으로 나눌 수 있다. Text를 다루는 것과 Font를 다루는 그룹. 먼저 text를 보자.

color 속성은 text의 색상(color)를 바꾼다. hexadecimal 아니면 RGB로 표현하던가, 이름을 줄 수 있다. 예를 들면 다음과 같다.

text-align 속성은 left-aligned, centered 혹은 right-aligned 로 text의 정렬 방법을 정한다.

밑줄을 준다든지 하고 싶을 때는 text-decoration 속성을 준다.

취소선을 줄 때는 text-decoration에 line-through를 주면 된다.

대소문자 변환을 위해서는 text-transform 속성을 이용한다. 다음은 모두 대문자가 된다. lowercase를 쓰면 모두 소문자, capitalize를 쓰면 각 단어별 첫 글자만 대문자가 된다.

줄간격을 위해서는 line-height 속성을 이용한다. 각 줄 사이의 vertical spacing을 조절한다.

 

이제 font를 다루는 그룹의 속성들을 보자.

font-family 속성은 어떤 폰트를 쓸지를 결정한다. 첫번째 것을 시도하고 없으면 두번째, 그것도 없으면 세번째 이렇게 나아간다.

font-size 속성은 font 크기를 조절한다. px나 상대적 단위인 em을 사용할 수 있다. em을 사용하면 사용자가 정한 default font size에 따라 그 비율로 조절된다.

font-weight은 font 굵기를 조절한다. 일반적으로 굵은 (bold)를 사용할 때 사용한다. 기본값은 당연히 normal이다.

 

[HTML5&CSS] Box model

각 HTML element들은 여러 layer로 구성되는데, 가장 바깥쪽에 margin, 그 안에 외곽선인 border, 그 안에 padding 그리고 그 안에 width와 height의 content가 위치한다. 반대로 안쪽부터 보자.

 

Content Box는 실제 content가 들어가는 부분이다. 가장 중요한 property는 width와 height이다. 실제 px값이나 %로 준다.

width: 300px;

height: 200px;

 

Padding은 border와 content box 사이에 공간을 준다. top, bottom, left, right 모두에 줄 수도, 좌우와 상하에만 줄 수도, 원하는 곳에 padding-top, padding-bottom, padding-left, padding-right로 줄 수도 있다.

모두에 padding 값을 줄 때는 값 하나만 쓴다.

padding: 25px;

상하와 좌우에 같은 값들을 줄 때는 값 두개를 쓴다. 아래는 상하에 25px를, 좌우에는 0px를 준다

padding: 25px 0px;

 

border는 padding의 끝에서 margin의 시작까지 자리한다. 기본값은 visible하지 않아서 명시적으로 정해주어야만 볼 수 있다. padding처럼 각 direction마다 줄 수도 있다. 모든 값은 width, style, color의 3가지 값을 갖는다. 예제들은 다음과 같다.

border: 1px solid red;

border-top: 1px solid blue;

border-right: 2px dotted red;

border-bottom: 5px dashed yellow;

border-left: 10px double pink;

 

margin은 border의 끝과 페이지 상의 다른 element의 외곽 사이에 공간을 준다. 역시 padding처럼 모든 방향에, 상하/좌우를 나눠서 줄 수도, 각 direction 별 top, right, bottom, left에 따로 줄 수도 있다.

 

4 방향을 속성에 한꺼번에 써서 줄 때는 clock-wise로 돈다. top->right,->bottom->left

개발자 도구에서 각 속성 값을 확인할 수 있다.

[HTML5&CSS] CSS page layout – float, flex, grid

세가지 많이 쓰이는 layout 테크닉이 있다.

  1. float
  2. flex
  3. grid

 

float-based CSS layout은 이 셋 중 가장 오래되었지만 여전히 쓰인다.

float의 property는 왼쪽 아니면 오른쪽 어디에 둘 건지를 정한다.

float: left 혹은 float: right

float: none을 쓰기도 하는데 자주 안 쓴다. left나 right를 override하고 싶을 때 쓴다.

일반적으로 float되는 element의 width를 px나 %등을 사용해서 명시적으로 정해준다.

width: 100px 혹은 width: 50%

float이라는 이름에서 알 수 있듯이, non-floated element 위에 떠 있는 식으로 보이면서 layout이 깨지는 문제를 자주 겪는다. 많은 해결책이 있지만, 가장 간단한 해결책은 이를 감싸는 element에 다음 속성을 주는 것이다.

overflow: hidden

 

flex-based CSS layout은 float-based CSS layout을 좀 더 개선한 새로운 대안이다. flex에서는 float에서와 같은 floating element의 clearing 문제를 걱정할 필요가 없다. 사용을 위해서는 개별 item을 담는 flex container에 다음을 적용하면 된다.

display: flex;

기본값으로 한 줄에 모든 element를 두도록 되는데, 이를 바꾸기 위해서는 다음 속성을 적용한다.

flex-wrap: wrap;

flex item들의 width를 정하기 위해서는 다음과 같이 한다.

flex-basis: 25%;

 

grid-based CSS layout은 세가지 중 가장 최신 기술이다. grid container에 다음을 적용한다.

display: grid;

그리고 grid 내에 몇 개를 둘 건지, 크기는 어떻게 할지, 몇 개의 열을 둘 것 인가를 정한다. 아래처럼 하면 4개의 같은 크기를 가지는 열을 갖게 된다.

grid-template-columns: auto auto auto auto;

float나 flex에서는 item들의 width를 정해주어야 했지만, grid-based CSS layout에서는 그럴 필요가 없다.

 

[HTML5&CSS] CSS 개요

CSS는 HTML에 style을 더할 때 사용한다. 다음과 같은 형식이다.

selector or selector list {

CSS declarations (= property: value;)

}

예를 들면 다음과 같다.

p, h1, h2 {

background: black;

color: white;

}

 

이를 HTML에 적용하는 방법은 여러가지가 있다.

  1. HTML tag의 style attribute를 이용하는 방법. 예를 들면, <h1 style=”color: red;”>…</h1>

  2. <head>나 <body> 태그 내에 <style> 태그를 추가하는 방법. head안에 넣는 게 더 빠르다.

  3. <link>를 통해 외부 리소스로부터 적용하는 방법. 예를 들면, <link href=”styles/mystyle.css” rel=”stylesheet”>. 가장 추천.

 

CSSOM (CSS Object Model)은 HTML DOM과 비슷하게 style에 대한 in-memory representation이다.

javascript로 HTML DOM을 접근할 때 다음과 같은 코드를 사용해서 DOM element의 style property를 access할 수 있다.

const contentElement = document.querySelector(‘.content’);

contentElement.style.fontWeight = ‘bold’;

 

비슷하게 CSSOM은 window object의 getComputedStyle 메소드로 access할 수 있다.

const contentElement = document.querySeletor(‘.content’);

window.getComputedStyle(contentElement);

이건 contentElement class attribute로 그 element의 read-only computed style을 return한다.

 

CSS로 HTML 문서의 element를 지정하고자 할 때 selector를 사용한다.

3가지 일반적인 selector가 있다.

  1. Element type: 예를 들면, 모든 그 HTML 문서 내의 p element를 select하고자 한다면 CSS ruleset에 p selector를 쓴다.

  2. class attribute: . 으로 시작하는 class selector. <h1 class=”heading”>Heading</h1> 을 select하고 싶다면, .heading selector를 쓴다.

  3. id attribute: # 으로 시작하는 id selector. <div id=”dasomoli”> <!– login content –> </div> 라면, #dasomoli를 쓴다.

 

모든 element를 선택하고 싶으면 Universal Selector (*)를 쓴다. 다음과 같은 예제는 html에 값을 셋팅하고, 모든 자식 element들에 이를 inherit한다.

html {

box-sizing: border-box;

}

*, *:before, *:after {

box-sizing: inherit;

}

 

Attribute selector를 사용하면 attribute가 있는지 또는 attribute 값에 따라서 select할 수 있다. 대괄호([, ])를 쓴다.

  • [attribute]: 그 attribute가 있는 모든 element를 select한다. [href]는 href attribute가 있는 모든 element를 select한다.

  • [attribute=value]: attribute값이 value인 모든 element를 select한다. [lang=”en”] 은 lang attribute가 en인 모든 element를 select한다.

  • [attribute^=value]: attribute 값이 value로 시작하는 모든 element를 select한다. [href^=”https://] 는 href attribute가 https:// 로 시작하는 모든 element를 select한다.

  • [attribute$=value]: attribute 값이 value로 끝나는 모든 element를 select한다. [href$=”.com”] 은 href attribute가 .com으로 끝나는 모든 element를 select한다.

  • [attribute=value]: attribute 값 안에 value가 들어간 모든 element를 select한다. [href=”co.kr”] 은 href attribute가 co.kr과 들어간 모든 element를 select한다.

 

Pseudo-class는 특정 상태에 있는 element를 select한다. :keyword 같은 형식으로 쓴다. 대단히 많은 pseudo-class가 있는데, 대부분의 개발자들은 link를 styling할 때 처음 본다. link는 여러 상태를 갖는다.

  • href를 갖는 link는 :link pseudo-class를 갖는다.

  • 사용자가 link에 마우스를 올렸을 때, :hover pseudo-class가 적용된다.

  • 가 본 link일 때, :visited pseudo-class가 적용된다.

  • click될 때, :active pseudo-class가 적용된다.

예를 들면,

a:link, a:visited: {

color: blue;

text-decoration: none;

}

a:hover, a:active {

color: red;

text-decoration: dashed underline;

}

순서에 주의해야 하는데, 위의 순서를 바꾸면 hover 효과를 볼 수 없다. love-hate로 외우면 쉽다.

다른 유용한 pseudo-class로 :checked, :disabled, :focus가 있다.

 

중첩된 자식들의 패턴을 select할 때 도움이 되는 pseudo-class가 있다. :first-child, :last-child, :nth-child, :nth-last-child, :first-of-type, :last-of-type, :nth-of-type, :nth-last-of-type 등등.

예를 들어, 다음과 같이 하면 3줄마다 반복되는 패턴을 만들 수 있다.

li {

display: block;

padding: 16px;

}

li:nth-child(3n-1) {

background: deepskyblue;

color: white;

font-weight: bold;

}

li:nth-child(3n) {

background: skyblue;

color: white;

font-weight: bold;

}

위의 예제에서 3n-1 대신 odd 나 even 같은 keyword를 사용할 수도 있다.

 

element의 일부를 select하고 싶을 때는 Pseudo-element를 쓴다. ::keyword 형식으로 쓴다.

::first-letter, ::first-line, ::selection, ::backdrop 등등.

 

위에서 설명한 여러 방법을 합쳐서 사용하면 더 강력하다. 예를 들면, primary class인 li element를 select하고 싶다면, li.primary를 쓰면 된다.

  • ul element의 자식인 모든 li element를 select하고 싶으면. ul li

  • primary class인 ul element의 direct children인 모든 li를 select하고 싶으면, ul.primary > li

  • selected class인 li element의 다음 sibling을 select하고 싶으면, li.selected + li

  • selected class인 li element의 모든 다음 sibling들을 select하고 싶으면, li.selected ~ li

li.selected + li 는 다음 하나만, li.selected ~ li 는 다음부터 전부 다인 것을 주의하라.

 

CSS specificity는 어느 style이 적용될지를 결정하는 factor이다. 태그에 style attribute로 적용되는 inline style은 가장 높은 specificity 값을 갖는다. 그 다음이 ID selector, 그 다음이 class selector나 attribute selector, 그리고 다음이 element type이다. 일반적으로 이를 표현하는 방법이 ,로 나눈 integer list로 많이 표현한다. inline style은 1, 0, 0, 0이다. id selector는 0, 1, 0, 0. class selector나 attribute selector는 0, 0, 1, 0. h1 같은 element selector는 0, 0, 0, 1이 된다.

예를 들면, li,selected a[href] 는 2 element selectors(li, a), 하나의 class selector(.selected)와 하나의 attribute selector([herf])를 갖으므로, 0, 0, 2, 2가 된다.

다른 예로 #newItem #mainHeading span.smallPrint는 두 ID selector, 하나의 class selector(.smallPrint), 하나의 span element를 갖으므로, 0, 2, 1, 1이 된다.

위의 둘을 비교하면, 두번째 것이 첫번째 것보다 더 specific하다.

그런데 여기에 !important 를 쓰면 가장 우선한다. 위의 표현 방법에 따르면 1, 0, 0, 0, 0이 된다. inline보다 더 우선한다.

예를 들면, 다음과 같은 걸 보자.

<style>

div.media {

display: block;

width: 100%;

float: left;

}

.hide {

display: none;

}

</style>

<div class=”media hide”> 어쩌고 저쩌고 </div>

여기서 div는 media와 hide 둘 중 어느 것이 적용될까. div.media는 0, 0, 1, 1 이다. .hide는 0, 0, 1, 0이다. 따라서 .hide의 display: none을 div.media의 display: block이 override한다. .hide는 적용이 안된다. 이럴 때 아래처럼 하면 된다.

.hide {

display: none !important;

}

 

 

[HTML5&CSS] HTML5 기본 구조

처음 doc type을 정한다. HTML5부터는 매우 심플하다.

<!DOCTYPE HTML>

 

HTML 태그로 시작한다. lang attribute로 어느 언어인지를 명시한다.

<html lang=”en”></html>

 

html 태그 안에는 head와 body가 들어간다.

먼저 head 안에 들어갈 내용을 살펴보자.

head의 필수 태그는 title이다. title 태그 전에 meta 태그로 charset을 명시해준다.

meta 태그로 해당 페이지의 description을 적어주면 좋다.

meta 태그로 디바이스의 width와 초기 scale로 viewport도 적어준다.

<head>

<meta charset=”utf-8″>

<title>DasomOLI’s example1</title>

<meta name=”description” content=”The purpose of this page is for showing a example of HTML5 basic structure”>

<meta name=”viewport” content=”width=device-width, initial-scale=1″>

</head>

 

body 태그 안엔 보일 내용을 내용의 목적에 맞는 태그를 사용해서 적는다.

대표적인 태그는 제목을 적을 때 사용하는 heading <h1>, <h2> 등의 태그와 리스트를 작성할 때 사용하는 unordered list <ul>, ordered list <ol>, 그리고 그 아래 항목을 적을 때 사용하는 list item <li>, 문단을 작성할 때 사용하는 paragraph <p> 태그 등이 있다.

<body>
<h1>DasomOLI’s example1</h1>
<p>Hello HTML5!</p>
</body>

모두 합치면 다음과 같을 것이다.

<!DOCTYPE HTML>
<html lang=”en”>
  <head>
    <metacharset=”utf-8″>
    <title>DasomOLI’s example1</title>
    <metaname=”description”content=”The purpose of this page is for showing a example of HTML5 basic structure”>
    <metaname=”viewport”content=”width=device-width, initial-scale=1″>
  </head>
  <body>
    <h1>DasomOLI’s example1</h1>
    <p>Hello HTML5!</p>
  </body>
</html>

완성된 html 은 w3c에서 제공하는 validator (https://validator.w3.org/)를 통해 유효성을 검사할 수 있다.

 

[Linux] logrotate 설정

로그 남길 때 그냥 주구장창 남기면 디스크가 꽉 찬다.

/var/log 아래 남는 log 들처럼 주기적으로 gzip으로 압축하고, 오래된 로그는 저절로 지워지도록 하고 싶을 때 logrotate를 쓴다.

logrotate를 설정해서 주기마다 압축해서 남기고, 때 되면 지우도록 만들자.

아래 명령은 도움말인 man page를 보여 준다.

# man logrotate

 

logrotate의 설정 파일들은 아래에서 찾을 수 있다.

/etc/logrotate.conf : logrotate의 기본 설정 파일. 여기서 /etc/logrotate.d/ 아래의 파일들을 include 하도록 되어 있다.
/etc/logrotate.d/ : logrotate를 사용해서 로그를 남기고 싶은 유틸리티들이 여기다 설정 파일을 둔다.

 

예제를 보자.

# vi /etc/logrotate.d/mysqlmon
/var/log/mysqlmon/mysqlmon.log /var/log/mysqlmon/mysqlmon.err {
        daily
        missingok
        rotate 7
        compress
        notifempty
}

위의 예제의 각 옵션은 다음과 같다.

daily: 하루 주기

missingok: 로그 파일이 없어도 에러 메시지를 쓰지 않는다.

rotate 7: 오래된 로그를 7개 남긴다.

compress: gzip으로 압축한다.

notifempty: 로그 파일이 비어 있으면 rotate하지 않는다.

다른 자세한 옵션은 man page를 참고하자.

[Linux] 리눅스/x86 부트 프로토콜 (Linux/x86 boot protocol)

원문: https://www.kernel.org/doc/Documentation/x86/boot.txt

이 글은 GPL 라이센스를 따릅니다.

리눅스/x86 부트 프로토콜


번역: 양정석 (dasomoli@gmailREMOVETHIS.com)

x86 플랫폼 상에서 리눅스는 좀 복잡한 부트 컨벤션을 사용합니다. 이는 역사적인 관점에서 조금씩 진화한 것 뿐 아니라 커널 그 자체를 부팅가능한 이미지로 가졌던 초기 요구, 복잡한 PC 메모리 모델과 메인스트림 운영체제로서 리얼 모드 DOS의 실질적인 종말에 의한 PC 산업 내의 변화한 기대들로 인한 것입니다.

현재 리눅스/x86 부트 프로토콜의 다음 버전들이 존재합니다.

Old kernels:    zImage/Image만 지원. 극초기 몇몇 커널들은 커맨드 라인조차
                지원하지 않을 수 있음.

Protocol 2.00:  (커널 1.3.73) bzImage와 initrd 지원 추가 뿐만 아니라
                부트로더와 커널 간 통신을 위한 방법이 형식화됨.
                기존 셋업 공간이 writable한 것으로 가정되어도
                setup.S가 relocatable

Protocol 2.01:  (커널 1.3.76) 힙 오버런 경고를 추가.

Protocol 2.02:  (커널 2.4.0-test3-pre3) 새로운 커맨드 라인 프로토콜.
                더 낮은 관례적 memory ceiling. 기존 셋업 공간의 덮어쓰기 없앰,
                그래서 SMM이나 32비트 BIOS 엔트리 포인트로부터 EBDA를
                사용하는 시스템을 안전하게 부팅하도록 만듦.
                zImage를 지원은 하지만, 곧 사라지도록 함.

Protocol 2.03:  (커널 2.4.18-pre1) 명시적으로 부트로더에 가능한 가장 높은
                initrd 주소를 사용가능하도록 만듦.

Protocol 2.04:  (커널 2.6.14) syssize 필드를 4바이트로 확장.

Protocol 2.05:  (커널 2.6.20) 프로텍티드 모드 커널을 relocatable하게 만듦.
                relocatable_kernel과 kernel_alignment 필드를 도입.

Protocol 2.06:  (커널 2.6.22) 부트 커맨드 라인의 크기를 포함하는 필드를 추가.

Protocol 2.07:  (커널 2.6.24) paravirtualised 부트 프로토콜 추가.
                hardware_subarch와 hardware_subarch_data, 그리고
                load_flags 안의 KEEP_SEGMENTS 플래그를 도입.

Protocol 2.08:  (커널 2.6.26) crc32 체크섬과 ELF 형식 페이로드를 추가.
                payload_offset과 payload_length 필드를 페이로드를
                위치시키는데 조정할 수 있도록 도입.

Protocol 2.09:  (커널 2.6.26) setup_data 구조체의 링크드 리스트 하나로의
                64비트 물리 포인터 필드를 추가.

Protocol 2.10:  (커널 2.6.31) 추가된 kernel_alignment를 넘는 relaxed alignment를
                위한 프로토콜 추가, 새로운 new init_size와 pref_address 필드.
                확장된 부트로더 ID들 추가.

Protocol 2.11:  (커널 3.6) EFI 핸드오버 프로토콜 엔트리 포인트의 오프셋을 위한
                필드 추가.

Protocol 2.12:  (커널 3.8) 64비트에서 4G위로 bzImage와 ramdisk를 로딩하기 위한
                boot_params을 구조화하기 위한 xloadflags와 확장 필드들 추가.

**** 메모리 레이아웃
Image나 zImage 커널에 사용되었던 커널 로더를 위한 전통적인 메모리 맵은 보통 다음과 같습니다:

        |                        |
0A0000  +------------------------+
        |  BIOS를 위해 예약됨    |     사용하지 마세요.  BIOS EBDA를 위해 예약됨.
09A000  +------------------------+
        |  커맨드 라인           |
        |  스택/힙               |     리얼 모드 코드가 사용.
098000  +------------------------+  
        |  커널 셋업             |     커널 리얼 모드 코드.
090200  +------------------------+
        |  커널 부트 섹터        |     커널 레가시 부트 섹터.
090000  +------------------------+
        |  프로텍티드 모드 커널  |     대부분의 커널 이미지.
010000  +------------------------+
        |  부트로더              |  <- 부트 섹터 엔트리 포인트 0000:7C00
001000  +------------------------+
        |  MBR/BIOS에 의해 예약됨|
000800  +------------------------+
        |  일반적으로 MBR이 사용 |
000600  +------------------------+ 
        |  BIOS만 사용           |
000000  +------------------------+

bzImage를 사용하면 프로텍티드 모드 커널이 0x100000 (“하이 메모리”)에 재배치되어 있고, 커널 리얼 모드 블럭(부트 섹터, 셋업, 그리고 스택/힙)은 0x10000과 로우 메모리의 끝 사이의 어느 주소에 relocatable로 만들어져 있습니다. 불행히도, 프로토콜 2.00과 2.01에서는 0x9000+ 메모리 범위를 여전히 내부적으로 커널이 사용합니다; 2.02 프로토콜이 이 문제를 해결합니다.

새로운 BIOS들이 확장 BIOS Data 영역(EBDA)으로 부르는 메모리를 로우 메모리의 가장 위 가까이에 좀 많이 할당하기 시작했기 때문에 “memory ceiling” — 부트로더에 닿는 로우 메모리의 가장 높은 지점 –을 최대한 낮게 지키는 것이 바람직합니다. 부트로더는 로우 메모리가 얼마나 사용가능한지를 검증하기 위해서 “INT 12h” BIOS 콜을 사용해야 합니다.

불행히도, INT 12h가 메모리의 양이 너무 적다고 보고하면, 보통은 부트로더가 할 수 있는 게 없고, 사용자에게 에러만 보고할 수 있습니다. 부트로더는 그래서 로우 메모리 안에 필요한 만큼만 적은 공간을 취하도록 설계되어야 합니다. 0x90000 세그먼트 내로 쓰여지는 데이터가 필요한 zImage나 오래된 bzImage 커널을 위해 부트로더는 0x9A000 위의 메모리를 사용하지 않는지 확인해야 합니다; 너무나 많은 BIOS들이 이 지점 위를 깹니다.

근래의 부트 프로토콜 버전 >= 2.02인 bzImage 커널을 위해 다음과 같은 메모리 레이아웃이 제안되었습니다:

        ~                        ~
        |  프로텍티드 모드 커널  |
100000  +------------------------+
        |  I/O 메모리 홀         |
0A0000  +------------------------+
        |  BIOS를 위해 예약됨    |     가능한 사용하지 않은채로 두세요
        ~                        ~
        |  커맨드 라인           |     (X+10000 표시 아래도 사용 가능)
X+10000 +------------------------+
        |  스택/힙               |     커널 리얼 모드 코드가 사용하도록
X+08000 +------------------------+  
        |  커널 셋업             |     커널 리얼 모드 코드.
        |  커널 부트 섹터        |     커널 레가시 부트 섹터.
X       +------------------------+
        |  부트 로더             |  <- 부트 섹터 엔트리 포인트 0000:7C00
001000  +------------------------+
        |  MBR/BIOS를 위해 예약됨|
000800  +------------------------+
        |  일반적으로 MBR이 사용 |
000600  +------------------------+ 
        |  BIOS만 사용           |
000000  +------------------------+

여기서 주소 X는 부트로더의 디자인이 허용하는 한 낮습니다.

 

**** 리얼 모드 커널 헤더

다음 본문에서 커널 부트 시퀀스의 어디에서든, “섹터”는 512 바이트로 참조됩니다. 이는 밑바탕의 미디어의 실제 섹터 크기에 무관합니다.

리눅스 커널을 로딩하는 첫 단계는 리얼 모드 코드(부트 섹터와 셋업 코드)를 로드하고 난 후 오프셋 0x01f1에 뒤따르는 헤더를 검사하는 것이어야 합니다. 리얼 모드 코드는 부트로더가 처음 두 섹터 (1K)만 로드하고나서 부트업 섹터 크기를 검사하도록 골라질 수 있다 하더라도, 전부 32K까지 될 수 있습니다.

헤더는 다음과 같습니다:

오프셋  Proto   이름                  의미
/크기

01F1/1  ALL(1   setup_sects           섹터로 된 셋업의 크기
01F2/2  ALL     root_flags            셋팅되면 그 루트는 readonly로 마운트됨
01F4/4  2.04+(2 syssize               16-byte paras로 된 32비트 코드의 크기
01F8/2  ALL     ram_size              사용 금지 - bootsect.S만 사용
01FA/2  ALL     vid_mode              비디오 모드 제어
01FC/2  ALL     root_dev              디폴트 루트 디바이스 번호
01FE/2  ALL     boot_flag             0xAA55 매직 넘버
0200/2  2.00+   jump                  점프 명령
0202/4  2.00+   header                매직 시그니처 "HdrS"
0206/2  2.00+   version               지원되는 부트 프로토콜 버전
0208/4  2.00+   realmode_swtch        부트로더 훅 (아래 참조)
020C/2  2.00+   start_sys_seg         load-low 세그먼트 (0x1000) (구식)
020E/2  2.00+   kernel_version        커널 버전 스트링의 포인터
0210/1  2.00+   type_of_loader        부트로더 식별자
0211/1  2.00+   loadflags             부트 프로토콜 옵션 플래그
0212/2  2.00+   setup_move_size       하이 메모리로 옮기는 크기 (훅과 사용)
0214/4  2.00+   code32_start          부트로더 훅 (아래 참조)
0218/4  2.00+   ramdisk_image         initrd 로드 주소 (부트로더가 셋팅)
021C/4  2.00+   ramdisk_size          initrd 크기 (부트로더가 셋팅)
0220/4  2.00+   bootsect_kludge       사용 금지 - bootsect.S만 사용
0224/2  2.01+   heap_end_ptr          셋업 끝 이후 남는 메모리
0226/1  2.02+(3 ext_loader_ver        확장된 부트로더 버전
0227/1  2.02+(3 ext_loader_type       확장된 부트로더 ID
0228/4  2.02+   cmd_line_ptr          커널 커맨드 라인으로의 32비트 포인터
022C/4  2.03+   initrd_addr_max       가장 높이 허용되는 initrd 주소
0230/4  2.05+   kernel_alignment      커널을 위해 요구되는 물리 메모리 얼라인먼트
0234/1  2.05+   relocatable_kernel    커널이 재배치 가능한지 아닌지
0235/1  2.10+   min_alignment         2의 거듭제곱인 얼라인먼트의 최소값
0236/2  2.12+   xloadflags            부트 프로토콜 옵션 플래그
0238/4  2.06+   cmdline_size          커널 커맨드 라인의 최대 크기
023C/4  2.07+   hardware_subarch      하드웨어의 서브 아키텍처
0240/8  2.07+   hardware_subarch_data 서브 아키텍처 전용 Data
0248/4  2.08+   payload_offset        커널 페이로드의 오프셋
024C/4  2.08+   payload_length        커널 페이로드의 길이
0250/8  2.09+   setup_data            setup_data 구조체의 링크드 리스트로의 64비트 물리 포인터
0258/8  2.10+   pref_address          선호하는 로딩 주소
0260/4  2.10+   init_size             초기화하는 동안 필요한 리니어(Linear) 메모리
0264/4  2.11+   handover_offset       핸드오버 엔트리 포인트의 오프셋

(1) 하위 호환을 위해, setup_sects필드가 0을 가지면, 실제 값은 4.

(2) 2.04 이전의 부트 프로토콜을 위해, syssize 필드의 상위 2바이트는 사용 불가다.
    이는 bzImage 커널의 크기는 결정될 수 없음을 의미한다.

(3) 무시되지만 부트 프로토콜 2.02-2.09를 위해 셋팅하는 것이 안전하다.

“HdrS” (0x53726448) 매직 넘버가 오프셋 0x202에 없다면 부트 프로토콜 버전은 “오래된” 것입니다. 오래된 커널을 로딩하면 다음 파라미터들이 가정되어야 합니다:

이미지 타입 = zImage
initrd는 지원하지 않음
리얼 모드 커널은 0x90000에 위치해야 함.

아니면, “version” 필드가 프토토콜 버전을, 예를 들면 프로토콜 버전 2.01은 0x0201을 이 필드 안에 가지는 것처럼 가집니다. 헤더 안의 필드를 셋팅할 때 사용하는 프로토콜 버전이 지원하는 필드만 셋팅하는지 확인해야 합니다.

 

**** 헤더 필드의 상세 사항

각 필드에 대해 어떤 것들은 커널로부터 부트로더로의 정보이고(“read”), 어떤 것들은 부트로더에 의해 채워질 것으로 기대되고(“write”), 그리고 어떤 것들은 부트로더에 의해 읽히고 수정될 것으로 기대됩니다(“modify”).

모든 일반 목적의 부트로더들은 (obligatory) 표시된 필드들을 써야합니다. 커널을 비표준 주소에 로드하길 원하는 부트로더들은 (reloc) 표시된 필드들을 채워야 합니다; 다른 부트로더들은 이들 필드를 무시할 수 있습니다.

모든 필드의 바이트 오더는 리틀엔디안입니다 (이건 전부 x86입니다).

필드 이름:    setup_sects
타입:         read
오프셋/크기:  0x1f1/1
프로토콜:     ALL

셋업 코드의 크기는 512 바이트 섹터 수로 되어 있습니다. 이 필드가 0이면 실제 값은 4입니다. 리얼 모드 코드는 부트 섹터 (항상 512 바이트 하나) + 셋업 코드로 구성됩니다.

필드 이름:    root_flags
타입:         modify (옵션)
오프셋/크기:  0x1f2/2
프로토콜:     ALL

이 필드가 0이 아니면, 루트는 디폴트로 readonly. 이 필드는 앞으로 사라집니다; 대신 커맨드 라인 상에 “ro” 또는 “rw” 옵션을 사용하세요.

필드 이름:    syssize
타입:         read
오프셋/크기:  0x1f4/4 (프로토콜 2.04+) 0x1f4/2 (모든 프로토콜)
프로토콜:     2.04+

16 바이트 단위로 된 프로텍티드 모드 코드의 크기. 2.04 이전의 프로토콜 버전에서 이 필드는 2바이트 길이로만 사용되었기 때문에 LOAD_HIGH 플래그가 셋팅되어 있다면 커널의 크기를 믿을 수 없습니다.

필드 이름:    ram_size
타입:         kernel internal
오프셋/크기:  0x1f8/2
프로토콜:     ALL

이 필드는 구식입니다.

필드 이름:    vid_mode
타입:         modify (obligatory)
오프셋/크기:  0x1fa/2

특별한 커맨드 라인 옵션 절을 보세요.

필드 이름:    root_dev
타입:         modify (optional)
오프셋/크기:  0x1fc/2
프로토콜:     ALL

디폴트 루트 디바이스의 디바이스 넘버. 이 필드는 곧 사라집니다. 대신 커맨드 라인 상에서 “root=” 옵션을 사용하세요.

필드 이름:    boot_flag
타입:         read
오프셋/크기:  0x1fe/2
프로토콜:     ALL

0xAA55를 가짐. 이는 오래된 리눅스 커널들이 가지는 매직 넘버와 가장 비슷합니다.

필드 이름:    jump
타입:         read
오프셋/크기:  0x200/2
프로토콜:     2.00+

0xEB 다음 부호있는 0x202 바이트에서의 상대적인 오프셋이 뒤에 오는 x86 점프 명령을 가짐. 이는 헤더의 크기를 결정하는데 사용될 수 있습니다.

필드 이름:    header
타입:         read
오프셋/크기:  0x202/4
프로토콜:     2.00+

매직 넘버 “HdrS” (0x53726448)를 가짐.

필드 이름:    version
타입:         read
오프셋/크기:  0x206/2
프로토콜:     2.00+

(메이저 << 8) + 마이너 형식, 예를 들면, 버전 2.04는 0x0204, 가상의 버전 10.17은 0x0a11로 된 부트 프로토콜 버전을 가짐.

필드 이름:    realmode_swtch
타입:         modify (optional)
오프셋/크기:  0x208/4
프로토콜:     2.00+

부트로더 훅 (아래의 고급 부트로더 훅을 보세요)

필드 이름:    start_sys_seg
타입:         read
오프셋/크기:  0x20c/2
프로토콜:     2.00+

Load low 세그먼트 (0x1000). 구식.

필드 이름:    kernel_version
타입:         read
오프셋/크기:  0x20e/2
프로토콜:     2.00+

0이 아닌 값으로 셋팅되어 있다면 NUL로 끝나는 사람이 읽을 수 있는 커널 버전 넘버 스트링으로의 포인터보다 0x200보다 작은 포인터를 담습니다. 이는 유저에게 커널 버전을 보여주는데 사용될 수 있습니다. 이 값은 (0x200*setup_sects) 보다 작아야 합니다.

예를 들어, 이 값이 0x1c00으로 셋팅되어 있으면, 커널 버전 넘버 스트링은 커널 파일 내의 오프셋 0x1e00에서 찾을 수 있습니다. 이 값은 다음처럼 “setup_sects” 필드가 15 이상의 값을 담고 있을 때만 유효합니다:

0x1c00 < 15*0x200 (= 0x1e00) 그러나
0x1c00 >= 14*0x200 (= 0x1c00)

0x1c00 >> 9 = 14, 그래서 setup_sects의 최소값은 15.

필드 이름:    type_of_loader
타입:         write (obligatory)
오프셋/크기:  0x210/1
프로토콜:     2.00+

부트로더가 할당된 id를 가지면, 0xTV가 여기에 들어가는데, T는 부트로더의 identifier이고, V는 버전 넘버입니다. 아니면 0xFF가 여기에 들어갑니다.

T = 0xD 위의 부트로더 ID들을 위해서는, T = 0xE를 이 필드에 쓰고 확장된 ID – 0x10을 ext_loader_type 필드에 씁니다. 비슷하게 ext_loader_ver 필드는 부트로더 버전의 4비트 이상을 제공하는데 사용될 수 있습니다.

예를 들면, T = 0x15, V = 0x234는 다음처럼 씁니다:

  type_of_loader  <- 0xE4
  ext_loader_type <- 0x05
  ext_loader_ver  <- 0x23

할당된 부트로더 ID들 (16진수):

   0  LILO         (0x00이 pre-2.00 bootloader에 예약됨)
    1  Loadlin
    2  bootsect-loader  (0x20, 다른 모든 값은 예약됨)
    3  Syslinux
    4  Etherboot/gPXE/iPXE
    5  ELILO
    7  GRUB
    8  U-Boot
    9  Xen
    A  Gujin
    B  Qemu
    C  Arcturus Networks uCbootloader
    D  kexec-tools
    E  Extended     (ext_loader_type를 보세요)
    F  Special      (0xFF = undefined)
       10  Reserved
       11  Minimal Linux Bootloader <http://sebastian-plotz.blogspot.de>
       12  OVMF UEFI virtualization stack

부트로더 ID 값의 할당이 필요하면, <hpa@zytor.com>에 연락하세요.

필드 이름:    loadflags
타입:         modify (obligatory)
오프셋/크기:  0x211/1
프로토콜:     2.00+

이 필드는 비트마스크이다.

  Bit 0 (read):    LOADED_HIGH
    - 0이면, 프로텍티드 모드 코드가 0x10000에 로드됨.
    - 1이면, 프로텍티드 모드 코드가 0x100000에 로드됨.

  Bit 1 (커널 내부용): KASLR_FLAG
    - 압축된 커널이 적절히 커널로 KASLR 상태를 통신하는데 내부적으로 사용됨
      1이면, KASLR 켜짐.
      0이면, KASLR 꺼짐.

  Bit 5 (write): QUIET_FLAG
    - 0이면, 얼리(early) 메시지를 출력.
    - 1이면, 얼리(early) 메시지를 찍지 않음.
        이는 커널 (decompressor와 얼리 커널)에게 디스플레이 하드웨어에
        직접 접근이 필요한 얼리 메시지를 쓰지 않도록 요청함.

  Bit 6 (write): KEEP_SEGMENTS
    프로토콜: 2.07+
    - 0이면, 32비트 엔트리 포인트 내에 세그먼트 레지스터들을 다시 로드.
    - 1이면, 32비트 엔트리 포인트 내에 세그먼트 레지스터들을 다시 로드하지 않음.
        %cs %ds %ss %es 가 모두 베이스 0에 (또는 그들의 환경에 동등하게)
        플랫(flat) 세그먼트로 셋팅되었음을 가정.

  Bit 7 (write): CAN_USE_HEAP
    heap_end_ptr 내에 들어있는 값이 유효함을 나타내기 위해서 이 비트를 1로 셋팅.
    이 필드가 셋팅되어 있지 않으면, 어떤 셋업 코드 기능은 꺼짐.
필드 이름:    setup_move_size
타입:         modify (obligatory)
오프셋/크기:  0x212/2
프로토콜:     2.00-2.01

프로토콜 2.00 또는 2.01을 사용할 때, 리얼 모드 커널이 0x90000에 로드되지 않으면, 로딩 시퀀스 안에서 나중에 옮겨집니다. 리얼 모드 커널 자체 뿐만 아니라 추가로 옮길 (커널 커맨드 라인 같은) 추가 데이터를 원한다면 이 필드를 채우세요.

단위는 부트 섹터의 시작에서 시작하는 바이트 수입니다.

이 필드는 프로토콜 2.02 이상 또는 리얼 모드 코드가 0x90000에 로드된다면 무시될 수 있습니다.

필드 이름:    code32_start
타입:         modify (optional, reloc)
오프셋/크기:  0x214/4
프로토콜:     2.00+

프로텍티드 모드 내에서 점프할 주소. 이는 커널의 로드 주소가 디폴트이고 부트로로가 적절한 로드 주소를 결정하는데 사용될 수 있습니다.

이 필드는 두가지 목적으로 바뀔 수 있습니다:

1. 부트로더 훅으로써 (아래의 고급 부트로더 훅을 보세요)

2. 혹이 설치되지 않은 부트로더가 재배치 가능한 커널을 비표준 주소에 로드하면 로드 주소를 가리키는 이 필드를 바꿔야 합니다.

필드 이름:    ramdisk_image
타입:         write (obligatory)
오프셋/크기:  0x218/4
프로토콜:     2.00+

32비트 초기 램디스크나 ramfs의 리니어(linear) 주소. 초기 램디스크/ramfs가 없으면 0으로 두세요.

필드 이름:    ramdisk_size
타입:         write (obligatory)
오프셋/크기:  0x21c/4
프로토콜:     2.00+

초기 램디스크나 ramfs의 크기. 초기 램디스크/ramfs가 없으면 0으로 두세요.

필드 이름:    bootsect_kludge
타입:         kernel internal
오프셋/크기:  0x220/4
프로토콜:     2.00+

이 필드는 구식입니다.

필드 이름:    heap_end_ptr
타입:         write (obligatory)
오프셋/크기:  0x224/2
프로토콜:     2.01+

셋업 스택/힙의 끝의 (리얼 모드 코드의 시작으로부터의) 오프셋 – 0x0200로 이 필드를 셋팅하세요.

필드 이름:    ext_loader_ver
타입:         write (optional)
오프셋/크기:  0x226/1
프로토콜:     2.02+

이 필드는 type_of_loader 필드 내의 버전 넘버의 확장으로 사용됩니다. 합친 버전 넘버는 (type_of_loader & 0x0f) + (ext_loader_ver << 4)로 처리됩니다.

이 필드의 사용은 부트로더에 따라 다릅니다. 안쓰이면 0입니다.

2.6.31 이전의 커널은 이 필드를 인식하지 않습니다만 프로토콜 버전 2.02 이상을 위해 쓰이는 것이 안전합니다.

필드 이름:    ext_loader_type
타입:         write (obligatory if (type_of_loader & 0xf0) == 0xe0)
오프셋/크기:  0x227/1
프로토콜:     2.02+

이 필드는 type_of_loader 필드 내의 타입 넘버의 확장으로 사용됩니다. type_of_loader의 타입이 0xE이면, 실제 타입은 (ext_loader_type + 0x10)입니다.

이 필드는 type_of_loader가 0xE가 아니면 무시됩니다.

2.6.31 이전의 커널은 이 필드를 인식하지 않습니다만, 프로토콜 버전 2.02 이상을 위해 쓰이는 것이 안전합니다.

필드 이름:    cmd_line_ptr
타입:         write (obligatory)
오프셋/크기:  0x228/4
프로토콜:     2.02+

커널 커맨드 라인의 리니어 주소로 이 필드를 셋팅하세요. 커널 커맨드 라인은 셋업 힙의 끝에서 0xA0000 사이의 어디에나 위치될 수 있습니다; 리얼 모드 코드 그 자체와 같은 64K 세그먼트내에 있을 필요 없습니다.

부트로더가 커맨드 라인을 지원하지 않는다해도 이 필드를 빈 스트링으로 (또는 더 나은 “auto” 스트링으로) 가리킬 수 있게 채우세요. 이 필드가 0으로 남아있다면 커널은 부트로더가 2.02+ 프로토콜을 지원하지 않는다고 가정할 겁니다.

필드 이름:    initrd_addr_max
타입:         read
오프셋/크기:  0x22c/4
프로토콜:     2.03+

초기 램디스크/ramfs 내용으로 채워질 수 있는 최대 주소. 부트 프로토콜 2.02 이전에는 이 필드가 없고, 최대 주소가 0x37FFFFFF입니다. (이 주소는 가장 안전한 바이트 주소로 정의되므로, 램디스크가 정확히 131072 바이트 길이이고 이 필드는 0x37FFFFFF라면 램디스크는 0x37FE0000에서 시작할 수 있습니다)

필드 이름:    kernel_alignment
타입:         read/modify (reloc)
오프셋/크기:  0x230/4
프로토콜:     2.05+ (read), 2.10+ (modify)

(relocatable_kernel 이 true라면) 커널에 필요한 얼라인먼트(alignment) 단위. 이 필드 내에 값으로 얼라인먼트에 맞지 않는 곳에 로드된 재배치가능 커널은 커널 초기화시에 다시 얼라인될 겁니다.

프로토콜 버전 2.10부터, 이는 최적의 성능을 위해 선호되는 커널 얼라인먼트를 반영합니다; 로더가 더 적은 얼라인먼트를 허용하기 위해 이 필드를 바꾸는 것이 가능합니다. 아래의 min_alignment와 pref_address 필드를 보세요.

필드 이름:    relocatable_kernel
타입:         read (reloc)
오프셋/크기:  0x234/1
프로토콜:     2.05+

이 필드가 0이 아니면, 커널의 프로텍티드 모드 부분이 kernel_alignment 필드를 만족하는 어느 주소에도 로드될 수 있습니다. 로딩 후에 부트로더는 로드된 코드 또는 부트로더 훅을 가리키도록 code32_start 필드를 셋팅해야 합니다.

필드 이름:    min_alignment
타입:         read (reloc)
오프셋/크기:  0x235/1
프로토콜:     2.10+

이 필드가 0이 아니면, 선호되는 것과 반대로, 커널이 부팅하기 위해 필요한 최소 얼라인먼트의 2의 거듭제곱 값을 나타냅니다. 부트로더가 이 필드를 사용한다면, 원하는 얼라인먼트 단위로 kernel_alignment 필드를 업데이트해야 합니다; 일반적으로:

kernel_alignment = 1 << min_alignment

지나치게 정렬되지 않은 커널에는 상당한 성능 비용이 있을 수 있습니다. 그래서 로더는 일반적으로 kernel_alignment로부터 2의 거듭제곱씩 이 얼라인먼트로 내려가면서 시도하는 것이 좋습니다.

필드 이름:    xloadflags
타입:         read
오프셋/크기:  0x236/2
프로토콜:     2.12+

이 필드는 비트 마스크입니다.

  Bit 0 (read):    XLF_KERNEL_64
    - 1이면, 이 커널은 0x200에 레가시 64비트 엔트리 포인트를 갖습니다.

  Bit 1 (read): XLF_CAN_BE_LOADED_ABOVE_4G
        - 1이면, kernel/boot_params/cmdline/ramdisk은 4G 위에 있을 수 있습니다.

  Bit 2 (read): XLF_EFI_HANDOVER_32
    - 1이면, 커널이 handover_offset에서 주어지는 32비트 EFI 핸드오프 엔트리 포인트를 지원합니다.

  Bit 3 (read): XLF_EFI_HANDOVER_64
    - 1이면, 커널이 handover_offset + 0x200에서 주어지는 64비트 EFI 핸드오프 엔트리
          포인트를 지원합니다.

  Bit 4 (read): XLF_EFI_KEXEC
    - 1이면, 커널이 EFI 런타임 지원으로 kexec EFI 부트를 지원합니다.
필드 이름:    cmdline_size
타입:         read
오프셋/크기:  0x238/4
프로토콜:     2.06+

0으로 끝나는 것을 뺀 커맨드 라인의 최대 크기. 이는 커맨드 라인이 최대 cmdline_size 문자를 담을 수 있음을 의미합니다. 프로토콜 버전 2.05 이전에는 그 최대 크기가 255였습니다.

필드 이름:    hardware_subarch
타입:         write (optional, defaults to x86/PC)
오프셋/크기:  0x23c/4
프로토콜:     2.07+

paravirtualized 환경에서 인터럽트 처리, 페이지 테이블 처리, 그리고 레지스터를 제어하는 접근 처리와 같은 하드웨어 로우 레벨 아키텍처적인 부분은 다르게 수행될 필요가 있습니다.

이 필드는 부트로더가 커널에게 우리가 아래와 같은 환경 내에 있음을 알릴 수 있도록 합니다.

  0x00000000   기본 x86/PC 환경
  0x00000001    lguest
  0x00000002    Xen
  0x00000003    Moorestown MID
  0x00000004    CE4100 TV 플랫폼
필드 이름:    hardware_subarch_data
타입:         write (subarch-dependent)
오프셋/크기:  0x240/8
프로토콜:     2.07+

하드웨어 서브아키(subarch) 전용 데이터로의 포인터. 이 필드는 현재 기본 x86/PC 환경에선 사용되지 않으므로, 수정하지 마세요.

필드 이름:    payload_offset
타입:         read
오프셋/크기:  0x248/4
프로토콜:     2.08+

0이 아니면, 이 필드는 프로텍티드 모드 코드의 시작에서 페이로드까지의 오프셋을 담습니다.

페이로드는 압축될 수 있습니다. 압축된, 그리고 압축되지 않은 데이터 둘 모두의 형식은 표준 매직 넘버를 사용해서 결정되어야 합니다. 현재 지원하는 압축 형식은 gzip(매직 넘버는 1F 8B 또는 1F 9E), bzip2 (매직 넘버 42 5A), LZMA (매직 넘버 5D 00), XZ (매직 넘버 FD 37), 그리고 LZ4 (매직 넘버 02 21) 입니다. 압축되지 않은 페이로드는 현재 항상 ELF (매직 넘버 7F 45 4C 46) 입니다.

필드 이름:    payload_length
타입:         read
오프셋/크기:  0x24c/4
프로토콜:     2.08+

페이로드의 길이

필드 이름:    setup_data
타입:         write (special)
오프셋/크기:  0x250/8
프로토콜:     2.09+

NULL로 끝나는 struct setup_data의 싱글 링크드 리스트로의 64비트 물리 포인터. 메카니즘을 넘기는 더 확장된 부트 파라미터를 정의하는데 사용됩니다. struct setup_data의 정의는 다음과 같습니다:

  struct setup_data {
      u64 next;
      u32 type;
      u32 len;
      u8  data[0];
  };

여기서 next는 링크드 리스트의 다음 노드로의 64비트 물리 포인터이고, 마지막 노드의 next 필드는 0입니다; type은 데이터의 내용을 식별하는데 사용됩니다; len은 data 필드의 길이입니다; data는 진짜 페이로드를 담습니다.

이 리스트는 부팅 과정 중에 여러 곳에서 수정될 수 있습니다. 그래서 이 리스트를 수정할 때 항상 이 링크드 리스트가 이미 엔트리를 담고 있는 경우를 고려해 확인해야 합니다.

필드 이름:    pref_address
타입:         read (reloc)
오프셋/크기:  0x258/8
프로토콜:     2.10+

0이 아니면, 이 필드는 커널을 위한 선호하는 로드 주소를 나타냅니다. 재배치 중인 부트로더는 가능하면 이 주소에 로드를 시도해야 합니다.

재배치 가능이 아닌 커널은 무조건 이 주소로 그 자신을 옮기고 실행할 겁니다.

필드 이름:    init_size
타입:         read
오프셋/크기:  0x260/4

이 필드는 커널이 그 메모리 맵을 시험할 수 있기도 전에 커널이 필요로 하는 커널 런타임 시작 주소에서 시작하는 리니어하게 연속된 메모리의 양을 나타냅니다. 이는 커널이 부팅하기 위해 필요한 전체 메모리의 양과는 다릅니다만, 재배치 중인 부트로더가 커널을 안전하게 로드할 주소를 고르는데 도움을 주는데 사용될 수 있습니다.

커널 런타임 시작 주소는 다음 알고리듬으로 결정됩니다:

  if (relocatable_kernel)
    runtime_start = align_up(load_address, kernel_alignment)
  else
    runtime_start = pref_address
필드 이름:    handover_offset
크기:         read
오프셋/크기:  0x264/4

이 필드는 커널 이미지에서 EFI 핸드오버 프로토콜 엔트리 포인트까지의 오프셋입니다. 커널을 부팅하는데 EFI 핸드오버 프로토콜을 사용하는 부트로더는 이 오프셋으로 점프해야 합니다.

더 자세한 사항은 EFI 핸드오버 프로토콜을 보세요.

 

**** 이미지 체크섬

부트 프로토콜 버전 2.08 부터 CRC-32가 특유의 폴리노미얼 0x04C11DB7과 초기 나머지 0xffffffff를 사용해서 전체 파일에 걸쳐 계산됩니다. 체크섬은 파일에 덧붙여집니다; 그래서 헤더의 syssize 필드 내에 지정된 한계까지의 그 파일의 CRC는 항상 0입니다.

 

**** 커널 커맨드 라인

커널 커맨드 라인은 부트로더와 커널이 통신하는 중요한 수단이 되었습니다. 그 옵션 중 일부는 또한 부트로더 그 자신과 관련됩니다. 아래의 “특별한 커맨드 라인 옵션”을 보세요.

커널 커맨드 라인은 null로 끝나는 스트링입니다. 최대 길이는 필드 cmdline_size로부터 얻을 수 있습니다. 프로토콜 버전 2.06 전에는 최대 255 문자였습니다. 너무 긴 스트링은 커널에 의해 자동으로 잘릴 겁니다.

부트 프로토콜 버전이 2.02 이후라면, 커널 커맨드 라인의 주소는 헤더 필드 cmd_line_ptr에 의해 주어집니다 (위를 보세요). 이 주소는 setup heap의 끝과 0xA0000 사이의 어느 곳에든 될 수 있습니다.

프로토콜 버전이 2.02가 아니거나 더 높다면, 커널 커맨드 라인은 다음 프로토콜을 사용해서 들어갑니다:

   오프셋 0x0020 (word)에, "cmd_line_magic", 매직 넘버 0xA33F가 들어갑니다.

    오프셋 0x0022 (word)에, "cmd_line_offset", (리얼 모드 커널의 시작과 관련된)
    커널 커맨드 라인의 오프셋이 들어갑니다.
    
    커널 커맨드 라인은 *반드시* setup_move_size로 커버되는 메모리 영역 내에 있어야 하므로,
    이 필드는 조정될 필요가 있을 수 있습니다.

 

**** 리얼 모드 코드의 메모리 레이아웃

리얼 모드 코드는 셋업되기 위해 스택/힙뿐 만 아니라 커널 커맨드 라인을 위해 할당된 메모리를 필요로 합니다. 이는 아래쪽 메가바이트 내에 리얼 모드 접근 가능한 메모리 내에서 수행될 필요가 있습니다.

근래의 머신들은 종종 상당히 큰 확장된 BIOS 데이터 영역(EBDA = Extended BIOS Data Area)을 갖는다는 것을 주의해야 합니다. 그래서 가능한한 적게 낮은 메가바이트를 사용하는 것이 권고됩니다.

불행히도 다음 상황 아래에서 0x90000 메모리 세그먼트가 사용되어야 합니다:

   - zImage kernel ((loadflags & 0x01) == 0)을 로딩할 때.
    - 2.01 또는 그 이전의 부트 프로토콜 커널을 로딩할 때.

      -> 2.00과 2.01 부트 프로토콜에서는 리얼 모드 코드가 다른 주소에
         로드될 수 있지만 내부적으로 0x90000에 재배치됩니다.
         "오래된" 프로토콜에서는 리얼 모드 코드는 0x90000에 로드됩니다.

0x90000에 로딩될 때, 0x9a000 위의 메모리 사용을 피하세요.

부트 프로토콜 2.02 또는 그 이상에서는, 커맨드 라인이 리얼 모드 셋업 코드와 같은 64K 세그먼트 내에 있을 필요는 없습니다; 그래서 스택/힙에 64K 세그먼트 전체를 주고, 그 위쪽에 커맨드 라인을 두는 것이 허용됩니다.

커널 커맨드 라인은 리얼모드 코드 아래 쪽 또는 하이 메모리 내에 두지 않아야 합니다.

 

**** 샘플 부트 설정

샘플 설정으로 리얼 모드 세그먼트의 아래 레이아웃을 가정합니다:

    0x90000 아래에 로딩할 때, 전체 세그먼트를 사용합니다:

    0x0000-0x7fff   리얼 모드 커널
    0x8000-0xdfff   스택과 힙
    0xe000-0xffff   커널 커맨드 라인

    0x90000에 로딩할 때 또는 프로토콜 버전이 2.01 또는 그 이전일 때:

    0x0000-0x7fff   리얼 모드 커널
    0x8000-0x97ff   스택과 힙
    0x9800-0x9fff   커널 커맨드 라인

부트로더는 그 헤더 내에 다음 필드를 넣어야 합니다:

   unsigned long base_ptr; /* 리얼 모드 세그먼트의 베이스 주소 */

    if ( setup_sects == 0 ) {
        setup_sects = 4;
    }

    if ( protocol >= 0x0200 ) {
        type_of_loader = <type code>;
        if ( loading_initrd ) {
            ramdisk_image = <initrd_address>;
            ramdisk_size = <initrd_size>;
        }

        if ( protocol >= 0x0202 && loadflags & 0x01 )
            heap_end = 0xe000;
        else
            heap_end = 0x9800;

        if ( protocol >= 0x0201 ) {
            heap_end_ptr = heap_end - 0x200;
            loadflags |= 0x80; /* CAN_USE_HEAP */
        }

        if ( protocol >= 0x0202 ) {
            cmd_line_ptr = base_ptr + heap_end;
            strcpy(cmd_line_ptr, cmdline);
        } else {
            cmd_line_magic  = 0xA33F;
            cmd_line_offset = heap_end;
            setup_move_size = heap_end + strlen(cmdline)+1;
            strcpy(base_ptr+cmd_line_offset, cmdline);
        }
    } else {
        /* 매우 오래된 커널 */

        heap_end = 0x9800;

        cmd_line_magic  = 0xA33F;
        cmd_line_offset = heap_end;

        /* 매우 오래된 커널은 그 리얼 모드 코드를 0x90000에
           로드해야만 한다 */

        if ( base_ptr != 0x90000 ) {
            /* 리얼 모드 커널 복사 */
            memcpy(0x90000, base_ptr, (setup_sects+1)*512);
            base_ptr = 0x90000;      /* 재배치 */
        }

        strcpy(0x90000+cmd_line_offset, cmdline);

        /* 32K 표시까지 메모리를 깨끗이 하는 것이 권장된다 */
        memset(0x90000 + (setup_sects+1)*512, 0,
               (64-(setup_sects+1))*512);
    }

 

**** 커널의 나머지 로딩

32비트 (리얼 모드가 아닌) 커널은 커널 파일 내의 오프셋 (setup_sects+1)*512에서 시작합니다(다시 한번, setup_sects == 0이면, 실제 값은 4). Image/zImage 커널에서는 주소 0x10000에, bzImage 커널에서는 주소 0x100000에 로드되어야 합니다.

프로토콜 >= 2.00 이고, loadflags 필드 내의 0x01 비트 (LOAD_HIGH)가 셋팅되어 있으면, 커널은 bzImage 커널입니다:

   is_bzImage = (protocol >= 0x0200) && (loadflags & 0x01);
    load_address = is_bzImage ? 0x100000 : 0x10000;

Image/zImage 커널은 그 크기로 512K까지 될 수 있음을, 그래서 메모리의 0x10000-0x90000 범위 전체를 사용함을 주의하세요. 이는 이들 커널이 리얼 모드 부분을 0x90000에 로드하는 중요한 요구사항임을 의미합니다. bzImage 커널은 더 유연하게 허용합니다.

 

**** 특별한 커맨드 라인 옵션

부트로더가 제공한 커맨드 라인이 사용자에 의해 입력되었다면, 사용자는 다음 커맨드 라인 옵션이 동작할 거라고 기대할 수 있습니다. 그들은 보통 커널 커맨드 라인으로부터 그들 전체가 실제로 커널에 의미가 없다 하더라도 지우지 말아야 합니다. 부트로더 그 자신을 위한 추가 커맨드 라인 옵션이 필요한 부트로더 작성자들은 지금 또는 그 이후에 실제 커널 옵션과 충돌하지 않음을 확인하기 위해서 Documentation/admin-guide/kernel-parameters.rst 내에 등록된 것들을 보아야 합니다.

  vga=<mode>
    <mode> 여기는 정수(C 표기로, 10진수, 8진수, 또는 16진수)거나
    "normal" (0xFFFF를 의미), "ext" (0xFFFE를 의미) 또는 "ask"(0xFFFD를 의미)
    중 하나의 스트링입니다. 이 값은 커맨드 라인이 파싱되기 전에 커널이 사용하는
    vid_mode 필드 내에 입력되어야 합니다.

  mem=<size>
    <size> 는 (대소문자 구별 없는) K, M, G, T, P 또는 E (<< 10, << 20,
    << 30, << 40, << 50 or << 60을 의미)가 옵션으로 붙는 C 표기의 정수입니다.
    이는 메모리의 끝을 커널에 지정합니다. 이는 initrd를 메모리의 끝 근처에
    두어야 하기 때문에 initrd의 가능한 배치에 영향을 미칩니다.
    이는 커널과 부트로더 *둘 모두*에 옵션임을명심하세요!

  initrd=<file>
    initrd가 로드되어야 합니다. <file>의 의미는 부트로더에 따라 명확히 다르고,
    어떤 부트로더들(e.g. LILO)은 이런 커맨드를 갖지 않습니다.

추가로 어떤 부트로더들은 다음 옵션을 사용자 지정 커맨드 라인으로 추가합니다:

  BOOT_IMAGE=<file>
    로드되었던 부트 이미지. 다시 한번, <file>의 의미는 부트로더에 따라 
    명확히 다릅니다.

  auto
    명확한 사용자 간섭없이 커널이 부팅되었습니다.

이들 옵션을 부트로더가 추가하면, 그들을 사용자 지정 혹은 설정을 지정하는 커맨드 라인보다 먼저 두는 것을 강력히 권합니다. 아니면 “auto” 옵션에 의해 “init=/bin/sh”가 혼란스럽게 됩니다.

 

**** 커널 실행

커널은 리얼 모드 커널의 시작으로부터 세그먼트 오프셋 0x20 에 위치한 커널 엔트리 포인트로 점프하면서 시작됩니다. 이는 여러분의 리얼 모드 커널 코드를 0x90000에 로드하였다면, 커널 엔트리 포인트는 9020:0000 이라는 뜻입니다.

엔트리에서, ds = es = ss 는 리얼 모드 커널 코드의 시작 (0x90000에 코드가 로드되었다면 0x9000)을 가리키고, sp는 적절히, 보통 힙의 가장 높은 곳을 가리키도록 셋업되어야 하고, 인터럽트들이 꺼져있어야 합니다. 추가로 커널 내의 버그를 막기 위해서 부트로더가 fs = gs = ds = fs = ss로 셋팅하는 것이 권장됩니다.

위의 우리의 예제에서 우리는 이렇게 할 겁니다:

   /* 주의: "오래된" 커널 프로토콜의 경우, 이 시점에 base_ptr 은 == 0x90000여야
       합니다; 이전의 샘플 코드를 보세요. */

    seg = base_ptr >> 4;

    cli();  /* 인터럽트가 꺼진 상태로 들어감! */

    /* 리얼 모드 커널 스택을 셋업 */
    _SS = seg;
    _SP = heap_end;

    _DS = _ES = _FS = _GS = seg;
    jmp_far(seg+0x20, 0);   /* 커널 실행 */

여러분의 부트 섹터가 플로피 드라이브를 접근한다면, 커널 부트는 인터럽트를 끈 상태로 놔두고, 그래서 그 모터를 끄지 않을 것이기 때문에 특별히 로드된 커널이 플로피 드라이버를 demand-loaded 모듈로 가진다면, 커널이 실행되기 전에 플로피 모터를 끄는 것을 권합니다!

 

**** 고급 부트로더 훅

부트로더가 (DOS 하에서 실행되는 LOADLIN 같은) 특정 우호적이지 않은 환경에서 실행된다면, 표준 메모리 위치 요구사항을 따르는 것이 불가능할 수 있습니다. 그런 부트로더는 셋팅되었다면, 적절한 시점에 커널이 실행하는 다음 훅을 사용할 수 있습니다. 이들 훅의 사용은 아마 최후의 수단으로 고려되어야 합니다!

중요사항: 모든 훅은 호출하고 나서 %esp, %ebp, %esi, 그리고 %edi를 보존하는 것이 요구됩니다.

  realmode_swtch:
    프로텍티드 모드로 들어가기 전에 바로 실행되는 16비트 모드 far 서브루틴.
    디폴트 루틴은 NMI를 끕니다. 그래서 아마 여러분의 루틴 역시 그렇게 동작하여야 할 겁니다.

  code32_start:
    프로텍티드 모드로 전이된 후, 그러나 커널 압축이 해제되기 전에 바로 *점프되는*
    32비트 flat 모드 루틴. CS를 제외한 세그먼트가 셋업됨(현재 커널은 합니다만,
    합니다만, 오래된 것들은 하지 않습니다)이 보장되지 않습니다; 여러분 스스로
    BOOT_DS (0x18)로 그들을 셋업해야 합니다.

    여러분의 훅이 완료된 후, 여러분의 부트로더가 덮어씌우기 전의 이 필드 안에 있던
    (적당하다면 재배치된) 주소로 점프해야 합니다.

 

**** 32비트 부트 프로토콜

레가시 BIOS보다 새로운 BIOS의 머신들에는 EFI, LinuxBIOS, 기타, 그리고 kexec, 레가시 BIOS에 기초한 커널 내의 16비트 리얼 모드 셋업 코드가 사용될 수 없어서, 32비트 부트 프로토콜이 정의될 필요가 있습니다.

32비트 부트 프로토콜내에서, 리눅스 커널을 로딩하는데 있어 첫번째 단계는 (전통적으로 “zero page”로 알려진 struct boot_params) 부트 파라미터들을 셋업하는 것이어야 합니다. struct boot_params를 위한 메모리는 모두 0으로 초기화되어 할당되어야 합니다. 그러고나면 커널 이미지의 오프셋 0x01f1으로부터의 셋업 헤더가 struct boot_params내로 로드되어 시험되어야 합니다. 셋업 헤더의 끝은 다음과 같이 계산될 수 있습니다:

   0x0202 + byte value at offset 0x0201

16비트 부트 프로토콜의 것처럼 struct boot_param의 셋업 헤더를 읽고/바꾸고/쓰는 것에 더해, 부트로더는 또한 zero-page.txt 내에 설명된 것처럼 struct boot_params의 추가 필드들을 채워야 합니다.

struct boot_params를 셋업하고 나서, 부트로더는 32/64비트 커널을 16비트 부트 프로토콜의 것과 같은 방식으로 로드할 수 있습니다.

32비트 부트 프로토콜에서는 커널은 로드된 32/64비트 커널의 시작 주소인 32비트 커널 엔트리 포인트로 점프함으로써 시작됩니다.

엔트리에서 CPU는 페이징이 꺼진 32비트 프로텍티드 모드 내에 있어야 합니다; GDT는 셀렉터 __BOOT_CS(0x10)과 __BOOT_DS(0x18)을 위한 디스크립터로 로드되어야 합니다; 두 디스크립터 모두 4G flat 세그먼트여야 합니다; __BOOT_CS는 execute/read 권한을 가져야 하고, __BOOT_DS는 read/write 권한을 가져야 합니다; CS는 __BOOT_CS여야 하고, DS, ES, SS는 __BOOT_DS여야 합니다; 인터럽트는 꺼져 있어야 합니다; %esi는 struct boot_params의 베이스 주소를 갖고 있어야 합니다; %ebp, %edi 그리고 %ebx는 0이어야 합니다.

 

**** 64비트 부트 프로토콜

64비트 CPU와 64비트 커널의 머신에서 우리는 64비트 부트로더를 사용할 수 있고, 64비트 부트 프로토콜이 필요합니다.

64비트 부트 프로토콜에서 리눅스 커널을 로딩하는 첫번째 단계는 (전통적으로 “zero page”로 알려진 struct boot_params) 부트 파라미터들을 셋업하는 것이어야 합니다. struct boot_params를 위한 메모리는 어느 곳에나 (4G 위 조차도) 할당될 수 있고, 모두 0으로 초기화됩니다. 그럼 커널 이미지 상의 offset 0x01f1의 셋업 헤더가 struct boot_params내로 로드되고 시험되어야 합니다. 셋업 헤더의 끝은 다음과 같이 계산될 수 있습니다:

   0x0202 + byte value at offset 0x0201

16비트 부트 프로토콜의 것처럼 struct boot_params의 셋업 헤더를 읽고/바꾸고/쓰는 것에 더해, 부트로더는 또한 zero-page.txt 내에 설명된 것처럼 struct boot_params의 추가 필드들을 채워야 합니다.

struct boot_params를 셋업한 후, 부트로더는 16비트 부트 프로토콜과 같은 방식으로 64비트 커널을 로드할 수 있습니다만, 커널은 4G 위에 로드될 수 있습니다.

64비트 부트 프로토콜에서 커널은 로드된 64비트 커널의 시작 주소 + 0x200인 64비트 커널 엔트리 포인트로 점프함으로써 시작됩니다.

엔트리에서, CPU는 페이징이 켜진 64비트 모드 내에 있어야 합니다. 로드된 커널의 시작 주소로부터 setup_header.init_size로의 범위와 zero page와 커맨드 라인 버퍼는 ident 맵핑을 얻습니다; GDT는 셀렉터 __BOOT_CS(0x10)과 __BOOT_DS(0x18)을 위한 디스크립터로 로드되어야 합니다; 두 디스크립터 모두 4G flat 세크먼트여야 합니다; __BOOT_CS는 execute/read 권한을 가져야 하고, __BOOT_DS는 read/write 권한을 가져야 합니다; CS는 __BOOT_DS여야 하고, DS, ES, SS는 __BOOT_DS여야 합니다; 인터럽트들은 꺼져 있어야 합니다; %rsi는 struct boot_params의 베이스 주소를 갖고 있어야 합니다.

 

**** EFI 핸드오버 프로토콜

이 프로토콜은 부트로더가 EFI 부트 스텁으로 초기화하는 것을 미루도록 할 수 있습니다. 부트로더는 커널/initrd(s)를 부트 미디어로부터 로드하고, startup_{32,64}의 시작으로부터 hdr->handover_offset 바이트인 EFI 핸드오버 프로토콜 엔트리 포인트로 점프하는 것이 요구됩니다.

핸드오버 엔트리 포인트를 위한 함수 원형은 다음처럼 보입니다.

    efi_main(void *handle, efi_system_table_t *table, struct boot_params *bp)

‘handle’은 EFI 펌웨어가 부트로더로 넘기는 EFI 이미지 핸들이고, ‘table’은 EFI 시스템 테이블 – 이들은 UEFI 스펙의 2.3절 “handoff state”의 첫 두 아규먼트입니다. ‘bp’는 부트로더가 할당한 boot params입니다.

부트로더는 bp 내의 다음 필드를 반드시 채워놔야 합니다.

    o hdr.code32_start
    o hdr.cmd_line_ptr
    o hdr.ramdisk_image (사용 가능하다면)
    o hdr.ramdisk_size  (사용 가능하다면)

다른 모든 필드들은 0이어야 합니다.

 

[Linker] GNU linker LD 사용하기 – Linker Scripts (.ld)

그냥 내용이나 조금 보려고 했는데, 보다보니 번역을 하고 있다.. 왜지…..

찾아보니 korea.gnu.org에 이미 번역된 문서(http://korea.gnu.org/manual/release/ld/ld-mahajjh/ld_3.html#SEC6)가 있다. 내건 부끄러우니 이걸 보도록 하자.

이 문서는 “GNU Free Documentation License”를 따른다.

 

Linker Scripts

원문: https://sourceware.org/binutils/docs/ld/Scripts.html#Scripts

번역: 양정석 (dasomoli@gmailREMOVETHIS.com)

모든 link는 linker script로 제어된다. 이 스크립트는 linker script language로 쓰여진다.

linker script의 주목적은 input file 내의 section을 output file로 어떻게 map해야 하는지, 그리고 output file의 memory layout을 어떻게 해야 하는지 설명하는 거다. 대부분의 linker script는 이 이상을 하지 않는다. 그러나 필요하면 linker script는 linker에게 아래에 설명된 command들을 사용해서 다른 동작을 수행할 수 있다.

linker는 언제나 linker script를 사용한다. 제공되지 않으면 linker는 linker executable 내부에 compile된 default script를 사용한다. '--verbose' command line option을 사용해서 default linker script를 볼 수 있다. '-r' 이나 '-N' 같은 command line option은 default linker script에 영향을 준다.

자신만의 linker script를 '-T' command line option을 사용해서 줄 수 있다. 이렇게 하면 그 linker script가 default linker script를 대신한다.

또한, linker에게 그것들이 link될 파일들이라도 input 파일로 그들을 이름 붙여서 암묵적으로 linker scripts를 사용할 수도 있다. Implicit Linker Scripts를 보라.

Linker Script 기본 개념

linker script language를 설명하기 위해서 기본적인 개념과 용어를 define할 필요가 있다.

링커는 input file들을 하나의 output file로 합친다. output file과 각 input file은 object file format이라고 알려진 special data format으로 되어 있다. 그래서 각 파일을 object file이라고 부른다. output file은 executable이라고 자주 부르지만, 우리의 목적에 따라 object file이라고도 부를거다. object file은 무엇보다도 sections들의 리스트를 가진다. input file 안에 있는 section을 input section이라고 하고, output file 안에 있는 section을 output section이라고 한다.

각 object file 안의 section은 하나의 name과 하나의 size를 갖는다. 거의 모든 섹션은 section contents라고 하는 data의 associated block도 갖는다. loadable로 표시된 section은 output file이 실행될 때 memory로 load된다. content가 없는 section은 allocatable일 수 있는데 이는 메모리는 set되지만 거기에 특별한 게 load되지는 않는다(특정 경우에 이 메모리는 0으로 초기화된다)는 뜻이다. loadable도 allocatable도 아닌 섹션은 일반적으로 디버깅 정보를 갖는다.

모든 loadable 또는 allocatable output section은 두가지 address를 갖는다. 먼저 VMA(virtual memory address)는 output file이 실행될 때 갖을 address이다. 다음으로 LMA(load memory address)는 그 section이 load될 address이다. 대부분의 경우에 두 address는 같다. 다를 수 있는 예는 data section이 ROM에 load되고 program이 시작될 때 RAM으로 복사될 때이다(이 테크닉은 ROM based system에서 global variables을 초기화할 때 자주 쓰인다). 이 경우 ROM address는 LMA가 되고, RAM address는 VMA가 될 것이다.

objdump프로그램의 '-h' 옵션으로 object file의 section들을 볼 수 있다.

모든 object file은 symbol table이라고 부르는 symbols의 list를 갖는다. symbol은 defined 이거나 undefined일 수 있다. 각 symbol은 이름을 갖고, 각 defined symbol은 다른 정보들보다 address를 갖는다. C/C++을 object file로 컴파일하면 모든 defined function과 global/static variable의 defined symbol을 얻게 된다. input file에서 참조되는 모든 undefined function과 global variable은 undefined symbol이 될 거다.

nm이나 objdump 프로그램의  '-t' option로 object file의 symbol들을 볼 수 있다.

Linker Script 형식

Linker script는 text file이다.

command를 연결해서 linker script를 작성한다. 각 command는 argument가 붙을 수 있는 keyword이거나 또는 symbol로의 assignment 둘 중 하나다. ';'으로 각 command를 구분할 수 있다. white space는 일반적으로 무시된다.

file이나 format name같은 string은 직접 들어갈 수 있다. file name에 다른 file name을 구별할 때 쓰이는 ','같은 문자가 포함되어 있으면, ""로 감쌀 수 있다. “”를 파일 이름으로 쓸 수 있는 방법은 없다.

C처럼 linker script안에 '/*', '*/'로 comment를 달 수도 있다. C에서처럼 comment는 문법적으로 whitespace와 동등하다.

간단한 Linker Script 예제

많은 linker script들은 정말 간단하다.

가능한 가장 간단한 linker script는 딱 하나의 'SECTIONS' command를 갖는 것이다. 'SECTIONS' command는 output file의 memory layout을 설명하는데 쓰인다.

'SECTIONS' command는 강력한 command이다. 여기서 우리는 가장 간단히 사용하는 것을 설명할거다. 여러분의 프로그램이 code, initialized data, uninitialized data로만 구성되어 있다고 하면 거기에는 각각 '.text', '.data', '.bss' section이 있게 된다. 이에 더해서 여러분의 input files안에 있는 section들만 있다고 하자.

이 예제를 위해 그 code가 address 0x10000에 load되어야 하고, 그 data는 address 0x8000000에서 시작되어야 한다고 하자.

아래가 이를 위한 link script이다.

SECTIONS
{
  . = 0x10000;
  .text : { *(.text) }
  . = 0x8000000;
  .data : { *(.data) }
  .bss : { *(.bss) }
}

'SECTIONS' command는 'SECTIONS' 키워드, 그 뒤에 symbol assignments과 output section 설명을 ‘{‘, ‘}’로 감싸서 쓴다.

위 예제의 첫 줄은 location counter인 special symbol ‘.’을 지정한다. 다른 방법(이건 뒤에 설명)으로 output section의 주소를 지정하지 않으면, location counter의 현재 값으로부터 그 address가 지정된다. 그러고 나면 location counter는 output section의 size만큼 더해진다. 'SECTIONS' command를 시작할 때  location counter는 값 '0'을 갖는다.

두번째 줄은 output section ‘.text’를 define한다. ':'이 필요하다. '{', '}' 안에, output section name 뒤에 이 output section에 두어야 할 input section들의 이름을 나열한다. '*'는 모든 파일 이름에 매치되는 wildcard이다. 표현식 '*(.text)' 은 모든 input file들 안의 '.text' input section을 뜻한다.

location counter가 '0x10000'이었기 때문에, output section '.text'가 defined될 때, linker는 output file내의 '.text' section의 주소를 '0x10000'으로 지정할 거다.

뒤의 줄들은 output file의 '.data''.bss'를 define한다. linker는 '.data' output section을 '0x8000000'에 둔다. 그러고나면 location counter는 '0x8000000' + '.data' output section의 size가 된다. 그 결과 linker는 '.bss' output section을 메모리 내에서 '.data' output section 바로 뒤에 두게 된다.

linker는 필요하다면 location counter를 더해서 각각의 output section에 필요한 alignment를 맞춘다. 이 예제에서는 '.text''.data' section들의 지정된 주소들은 아마 alignment constraint를 만족할 것이고 linker는 '.data''.bss' section 사이에 작은 갭을 만들어야 했을 수 있다.

이게 다다. 간단하고 완전한 하나의 linker script다.

간단한 Linker Script Command

간단한 linker script command들을 설명한다.

  • Entry Point: entry point를 설정
  • File Command들: 파일을 다루는 command들
  • Format Command들: object file format을 다루는 command들
  • REGION ALIAS: memory region에 별칭 붙이기
  • 기타 Command들: 다른 linker script command들

Entry point 설정

프로그램 내의 실행되는 첫번째 instruction을 entry point라고 부른다. ENTRY linker script command로 이 entry point를 설정할 수 있다. 그 argument는 symbol name이다:

ENTRY(symbol)

entry point를 설정하는 여러 방법이 있다. linker는 다음 방법들을 순서대로 실행하면서 entry point를 설정하고, 그 중 하나가 성공하면 멈춘다:

  • '-e' entry command-line 옵션;
  • linker script 안의 ENTRY(symbol) command;
  • defined되면, target-specific symbol의 값; 많은 target들에 이는 start지만, 예를 들면 PE- 그리고 BeOS-based system은 가능한 entry symbols의 list를 check해서 처음 맞는 것을 찾는다.
  • '.text' section이 있다면 그 첫 byte의 address;
  • address 0.

파일을 다루는 command들

여러 linker script command들이 파일을 다룬다.

INCLUDE filename

linker script filename 을 이 지점에 include한다. 현재 디렉토리에서 찾고 나서 -L 옵션으로 지정된 디렉토리 내에서 찾는다. INCLUDE는 10 레벨까지 중첩 호출이 가능하다.

MEMORY 또는 SECTIONS commands 안에, 또는 output section descriptions 안에, top level에서 INCLUDE 지시자를 둘 수 있다.

INPUT(filefile, ...)

INPUT(file file ...)

INPUT command 는 linker에게 그 link 내에 command line 상에서 이름을 정한 것처럼 이름 붙인 file들을 include하도록 지시한다.

예를 들어, 'subr.o'를 link할 때마다 include하고 싶지만 매번 command line에 이를 입력하고 싶지는 않을 때 'INPUT (subr.o)'를 linker script 내에 적을 수 있다.

사실 원한다면 linker script 안에 모든 input files를 적어두고, linker를 아무 것도 없이 '-T' 옵션으로 실행할 수 있다.

sysroot prefix가 설정되어 있고, 그 filename이 '/' character로 시작하고, 처리될 그 script가 sysroot prefix 내부에 있는 경우, 그 filename을 sysroot prefix 내에서 찾을 거다. 아니라면, linker는 먼저 현재 디렉토리 내에서 그 파일을 열 수 있는지 시도해보고, 없으면, archive library search path를 통해 찾는다. 또한 sysroot prefix는 filename path안의 첫번째 character로 = 를 지정하거나 $SYSROOT를 filename path 앞에 붙임으로써 강제할 수 있다. Command Line Options section 내의 '-L' 의 설명을 한번 보라.

'INPUT (-lfile)'을 쓰면 ld 는 command line argument '-l'로 한 것 처럼 libfile.a로 그 이름을 바꾼다.

implicit linker script 내에서 INPUT command를 사용하면, 그 파일들은 그 link상의 linker script file이 included된 그 지점에서 include될 것이다. 이는 archive searching에 영향을 준다.

GROUP(filefile, ...)

GROUP(file file ...)

GROUP command는 named file들이 모두 archive되어야만 한다는 것, 그리고 새로운 undefined reference가 만들어지기 전까지 반복적으로 찾아진다는 것을 제외하면 INPUT과 비슷하다. Command Line Options section 내의 '-(' 의 설명을 보라.

AS_NEEDED(filefile, …)

AS_NEEDED(file file …)

이 construct는 INPUT이나 GROUP commands 안에서만 다른 filenames와 나올 수 있다. 나열된 files들은 ELF shared libraries의 그들이 진짜 필요할 때만 추가되는 예외를 제외하면, 그 INPUT이나 GROUP commands 안에 직접 나온 것처럼 처리될 거다. 이 construct는 본질적으로 그 안에 나열된 모든 files들에 대해 --as-needed 옵션을 enable하고, --no-as-needed 셋팅 이후에 이전의 --as-needed를 각각 복구한다.

OUTPUT(filename)

OUTPUT command는 output file의 이름을 정한다. linker script 안에서 OUTPUT(filename) 을 사용하는 것은 command line (Command Line Options section을 보라) 상에서 '-o filename' 을 사용하는 것과 정확히 같다. 둘 다 사용되면, command line option이 더 우선된다.

OUTPUT command는 보통 기본 값인 'a.out' 말고 다른 file을 기본 값으로 define하려고 할 때 사용할 수 있다.

SEARCH_DIR(path)

SEARCH_DIR command는 ld가 library들을 archive할 때 보는 경로의 리스트에 path를 추가한다. SEARCH_DIR(path)를 사용하는 것은 command line (Command Line Options section을 보라) 상에서 '-L path'를 사용하는 것과 정확히 같다. 둘 다 사용되면, linker는 두 path 모두 찾는다. command line option에서 지정된 path를 먼저 찾는다.

STARTUP(filename)

STARTUP command는 command line 상에서 먼저 정해졌던 것처럼 그 filename이 link될 첫 input file이 되는 것을 제외하면 INPUT command와 똑같다. 이는 entry point가 언제나 첫번째 file의 시작인 시스템을 사용할 때 유용할 거다.

Object file format을 다루는 command들

두 linker script command가 object file format을 다룬다.

OUTPUT_FORMAT(bfdname)

OUTPUT_FORMAT(defaultbiglittle)

OUTPUT_FORMAT command는 output file을 위해 사용하는 BFD format을 지정한다. (BFD를 보라). OUTPUT_FORMAT(bfdname)은 command line 상의 '--oformat bfdname'(Command-line Options를 보라)을 사용하는 것과 정확히 같다. 둘 다 사용되면, command line option이 우선된다.

OUTPUT_FORMAT'-EB''-EL' command-line options에 기초하는 다른 foramts을 사용하도록 세 개의 arguments로 사용할 수 있다. 이는 linker script가 원하는 endianness 상의 output format으로 set되도록 한다.

'-EB' 또는 '-EL' 둘 다 사용되지 않으면, output format은 첫번째 argument인 default가 될 거다. '-EB'가 사용되면 output format은 두번째 argument인 big이 될 거다. '-EL'이 사용되면 output format은 세번째 argument인 little이 될거다.

예를 들어, MIPS ELF target의 default linker script는 다음 command를 사용한다:

OUTPUT_FORMAT(elf32-bigmips, elf32-bigmips, elf32-littlemips)

이는 그 output file을 위한 그 default format이 'elf32-bigmips'지만, 사용자가 '-EL' command-line option을 사용하면 그 output file은 'elf32-littlemips' format으로 만들어질 것을 이야기한다.

TARGET(bfdname)

TARGET command는 input file을 읽을 때 사용하는 BFD format의 이름을 지정한다. 이는 이어지는 INPUTGROUP commands에 영향을 미친다. 이 command는 command line 상의 '-b bfdname'(Command-line Options를 보라)의 사용과 같다. TARGET command가 사용되지만 OUTPUT_FORMAT이 없으면, 마지막 TARGET command가 그 output file을 위한 format을 set하는데도 사용된다. BFD를 보라.

memory region에 별칭 붙이기

MEMORY command로 생성한 existing memory regions에 별칭을 붙일 수 있다. 각 이름은 하나의 memory region에만 해당한다.

REGION_ALIAS(alias, region)

REGION_ALIAS function은 memory region region에 별칭 alias를 만든다. 이는 memory regions로의 유연한 output section의 mapping을 할 수 있도록 한다. 다음에 예제가 나온다.

다양한 memory storage devices를 갖고 있는 embedded system을 위한 application이 있다고 가정하다. 뭐든 code 실행 또는 data 저장을 할 수 있는 일반 목적의 volatile memory RAM을 갖는다. 어떤 것은 code 실행과 read-only data access를 할 수 있는 read-only, non-volatile memory ROM을 갖을 수 있다. 마지막 variant는 read-only data access와 code 실행을 할 수 없는 read-only, non-volatile memory ROM2이다. 우리는 4가지 output section을 갖는다:

  • .text program code:
  • .rodata read-only data;
  • .data read-write initialized data;
  • .bss read-write zero initialized data.

목적은 output section을 정의하는 system independent한 부분과 그 시스템 상의 available한 memory regions로 output sections를 mapping하는 system dependent한 부분을 포함하는 linker command file을 제공하는 것이다. 우리의 embedded system은 3가지 다른 memory setups A, B 그리고 C로 된다.

Section Variant A Variant B Variant C
.text RAM ROM ROM
.rodata RAM ROM ROM2
.data RAM RAM/ROM RAM/ROM2
.bss RAM RAM RAM

RAM/ROM 이나 RAM/ROM2 표기는 이 section이 region ROM 이나 region ROM2로 각각 loaded된다는 뜻이다. .data section의 load address가 .rodata section의 끝에서 모든 세가지 variants가 시작함에 주의하라.

base linker script는 아래의 output section으로 처리한다. memory layout을 describe하는system dependent한 linkcmds.memory file을 포함한다.

INCLUDE linkcmds.memory

SECTIONS
  {
    .text :
      {
        *(.text)
      } > REGION_TEXT
    .rodata :
      {
        *(.rodata)
        rodata_end = .;
      } > REGION_RODATA
    .data : AT (rodata_end)
      {
        data_start = .;
        *(.data)
      } > REGION_DATA
    data_size = SIZEOF(.data);
    data_load_start = LOADADDR(.data);
    .bss :
      {
        *(.bss)
      } > REGION_BSS
  }

이제 memory regions들과 별칭을 define하는 세가지 다른 linkcmds.memory file이 필요하다. 세가지 다른 variants A, B C를 위한 linkcmds.memory의 내용은 다음과 같다:

A

여기선 모두 RAM내로 간다.

MEMORY
  {
    RAM : ORIGIN = 0, LENGTH = 4M
  }

REGION_ALIAS("REGION_TEXT", RAM);
REGION_ALIAS("REGION_RODATA", RAM);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);

 

B

Program code와 read-only data는 ROM내로 간다. Read-write data는 RAM내로 간다. initialized data의 image는 ROM내에 load되고 system이 시작되는 동안 RAM내로 copy될 거다.

MEMORY
  {
    ROM : ORIGIN = 0, LENGTH = 3M
    RAM : ORIGIN = 0x10000000, LENGTH = 1M
  }

REGION_ALIAS("REGION_TEXT", ROM);
REGION_ALIAS("REGION_RODATA", ROM);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);

 

C

Program code는 ROM내로 간다. Read-only data는 ROM2내로 간다. Read-write data는 RAM내로 간다. initialized data는 ROM2내에 load되고, system이 start하는 동안 RAM내로 copy될 거다..

MEMORY
  {
    ROM : ORIGIN = 0, LENGTH = 2M
    ROM2 : ORIGIN = 0x10000000, LENGTH = 1M
    RAM : ORIGIN = 0x20000000, LENGTH = 1M
  }

REGION_ALIAS("REGION_TEXT", ROM);
REGION_ALIAS("REGION_RODATA", ROM2);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);

 

필요하다면, ROM이나 ROM2로부터 RAM내로 .data section을 복사하는 common system initialization routine을 작성할 수 있다.

#include <string.h>

extern char data_start [];
extern char data_size [];
extern char data_load_start [];

void copy_data(void)
{
  if (data_start != data_load_start)
    {
      memcpy(data_start, data_load_start, (size_t) data_size);
    }
}

다른 linker script command들

다른 linker script command들이 몇 있다.

ASSERT(expmessage)

exp 가 0이 아님을 확인한다. 0이라면, linker가 error code와 message를 찍으면서 종료된다.

모든 assertions들은 linking의 final stage가 시작되기 전에 checked된다. 이는 section definitions 내에 symbols PROVIDE와 관련된 expression은 user가 이들 symbols의 값을 set하지 않았다면 fail됨을 의미한다. 이 rule의 단 하나의 예외는 .을 reference하는 PROVIDE symbol이다. 그래서 다음과 같은 assertion은 __stack_size가 다른 곳에 defined되지 않았다면 실패한다:

  .stack :
  {
    PROVIDE (__stack = .);
    PROVIDE (__stack_size = 0x100);
    ASSERT ((__stack > (_end + __stack_size)), "Error: No room left for the stack");
  }

section definitions 밖의 symbols PROVIDE는 미리 evaluated되므로 ASSERT 안에서 사용될 수 있다. 그래서 다음은 동작한다:

  PROVIDE (__stack_size = 0x100);
  .stack :
  {
    PROVIDE (__stack = .);
    ASSERT ((__stack > (_end + __stack_size)), "Error: No room left for the stack");
  }

EXTERN(symbol symbol ...)

output file에 들어갈 symbol을 undefined symbol로 강제한다. 예를 들면 이는 standard library들로부터 추가 모듈들의 linking을 trigger할 수 있다. 각 EXTERN마다 여러 symbol들을 나열할 수 있고, EXTERN은 여러 번 사용할 수 있다. 이 command는 '-u' command-line option과 같은 효과를 갖는다.

FORCE_COMMON_ALLOCATION

이 command는 '-d' command-line option과 같은 효과를 갖는다. relocatable output file이 지정 ('-r') 되었다 하더라도, common symbol들에 ld가 space를 assign하도록 만든다.

INHIBIT_COMMON_ALLOCATION

이 command는 ‘–no-define-common’ command-line option과 같은 효과를 갖는다: ld가 non-relocatable output file조차 common symbols로의 address assignment를 생략하게 만든다.

FORCE_GROUP_ALLOCATION

이 command는 ‘–force-group-allocation’ command-line option과 같은 효과를 갖는다: ld가 보통 input sections같은 members를 section group으로 두도록, 그리고 relocatable output file이 지정 ('-r') 되었다 하더라도 그 section groups를 delete하도록 만든다.

INSERT [ AFTER | BEFORE ] output_section

이 command는 일반적으로 '-T' argument로 default SECTIONS으로 예를 들면 overlays로 지정하는 script내에서 사용된다. 이는 output section 후에 (또는 전에) 모두에 우선하는 linker script statements를 삽입하고, 또한 default linker script를 override하지 않도록 '-T'를 야기한다. 정확한 삽입 지점은 이름 없는 sections을 위한 것과 같다. Location Counter를 보라. 그 삽입은 linker가 output sections들로 input sections들을 mapped한 후에 일어난다. 삽입 전에 ‘-T’ scripts는 default linker script 전에 parsed되고, ‘-T’ script 내의 statements들이 default linker script statements 전에 그 script의 internal linker representation 내에서 일어난다. 특별히 input section assignments는 default script 내의 그것들 전에 ‘-T’ output sections로 만들어질 거다. 여기 ‘-T’ script가 INSERT를 어떻게 사용하는지 볼 수 있는 예제다:

SECTIONS
{
  OVERLAY :
  {
    .ov1 { ov1*(.text) }
    .ov2 { ov2*(.text) }
  }
}
INSERT AFTER .text;

NOCROSSREFS(section section ...)

이 command는 ld에게 어떤 output section들 사이의 모든 reference들에 관한 error를 올리라고 말하는데 사용될 수 있다.

어떤 종류의 program에서, 특히 임베디드 시스템 상에서 overlay가 사용될 때, 한 section이 메모리로 load되면, 다른 section은 아닐 것이다. 두 section 사이의 모든 direct reference는 error가 된다. 예를 들어, 한 section의 code가 다른 section에 defined된 함수를 호출한다면 에러일 거다.

NOCROSSREF command는 output section 이름의 list를 가져온다. ld가 section들 사이에 어떤 cross references가 발견하면 error를 보고하고, 0이 아닌 exit status로 return한다. NOCROSSREF command는 input section name이 아닌 output section name을 사용한다.

NOCROSSREFS_TO(tosection fromsection …)

이 command는 ld에게 다른 sections의 list로부터 한 section으로의 어떠한 reference라도 error로 올리도록 이야기할 때 쓸 수 있다.

NOCROSSREFS command는 둘 이상의 output sections이 전부 independent하지만 한 방향의 de[endency가 필요한 경우를 보장할 때 유용하다. 예를 들어 multi-core application에서 각 core로부터 호출될 수 있는 code를 공유하지만 안전을 위해 call back을 사용되지 않아야 할 수 있다.

NOCRESSREFS_TO command는 output section names의 list를 취한다. 첫번째 section은 다른 어떤 sections로부터도 referenced될 수 없다. ld가 다른 어떤 sections으로부터 첫번째 section으로의 어떤 references라도 탐지하면, error를 report하고 non-zero exist status를 return한다. NOCRESSREFS_TO command는 input section names가 아닌, output section names를 사용함을 주의하라.

OUTPUT_ARCH(bfdarch)

특정 output machine architecture를 지정한다. argument는 BFD library (BFD section을 보라) 에 의해 사용되는 이름 중 하나다. objdump'-f' 옵션을 사용해서 object file의 architecture를 볼 수 있다.

LD_FEATURE(string)

이 command는 ld의 behavior를 변경하도록 만들 수 있다. string이 “SANE_EXPR”이면 script 내의 absolute symbols와 numbers는 모든 곳에서 간단히 numbers로 취급된다. Expression Section절을 보라.

Symbol에 값 assign하기

linker script 안에서 symbol에 값을 assign할 수 있다. 이는 symbol을 global symbol로 define한다.

간단한 assignment

C assignment operators의 하나를 사용해서 symbol을 assign할 수 있다.

symbol = expression ;

symbol += expression ;

symbol -= expression ;

symbol *= expression ;

symbol /= expression ;

symbol <<= expression ;

symbol >>= expression ;

symbol &= expression ;

symbol |= expression ;

첫번째 경우는 expression의 값으로 symbol을 define한다. 다른 모든 경우는, symbol이 이미 defined되어 있어야 하고, 값이 그에 따라 조정될 것이다.

special symbol name '.' 은 location counter를 나타낸다. SECTIONS command 내에서만 이를 사용할 거다.

expression 뒤에 ';'을 꼭 써야 한다.

Expression들은 아래에 defined되어 있다. Linker Script내의 expression 절을 보라.

symbol assignment는 command들의 오른쪽에 command처럼, SECTIONS command 내의 statement처럼, SECTIONS command 내의 output section description의 일부처럼 작성될 수 있다.

symbol의 section은 그 expression의 section으로부터 정해진다. Expression의 Section 절을 보라.

여기 symbol assignment가 사용될 수 있는 세가지 다른 곳을 보여주는 예제가 있다.

floating_point = 0;
SECTIONS
{
  .text :
    {
      *(.text)
      _etext = .;
    }
  _bdata = (. + 3) & ~ 4;
  .data : { *(.data) }
}

이 예제에서 symbol 'floating_point'는 0으로 defined될 것이다. symbol '_etext'는 ‘.text’ input section 뒤의 address로 defined될 것이다. symbol '_bdata'는 ‘.text’ output section 뒤에 4 byte boundary로 올려서 aligned된 address로 defined될 것이다.

HIDDEN

ELF target의 포팅을 위해, 숨겨지고, export되지 않는 symbol을 define하라. syntax는 HIDDEN(symbol = expression) 이다.

여기 간단한 Assignments로부터 HIDDEN을 사용해서 재작성한 예제다:

HIDDEN(floating_point = 0);
SECTIONS
{
  .text :
    {
      *(.text)
      HIDDEN(_etext = .);
    }
  HIDDEN(_bdata = (. + 3) & ~ 3);
  .data : { *(.data) }
}

이 경우 세 symbol 중 어떤 것도 이 모듈 밖에서 보이지 않을 거다.

PROVIDE

어떤 경우에 어떤 symbol이 reference되긴 하지만 그 link에 포함된 어떤 object에도 defined되어 있지 않을 때 linker script가 그 symbol만 define하고 싶을 수 있다. 예를 들면, symbol 'etext'가 defined된 traditional linker들 말이다. 그러나 ANSI C는 error 없이 'etext'를 function name처럼 사용할 수 있어야 함을 요구한다. PROVIDE keyword는 reference되지만, defined되지는 않는 'etext'같은 symbol을 define하는데 사용될 수 있다. syntax는 PROVIDE(symbol = expression) 이다.

여기 'etext'를 define하는 PROVIDE 사용 예제다.

SECTIONS
{
  .text :
    {
      *(.text)
      _etext = .;
      PROVIDE(etext = .);
    }
}

이 예제에서 프로그램이 '_etext'를 (앞에 _를 붙여서) define한다면, linker는 multiple definition error가 날 거다. 다르게 말하면, 그 프로그램이 'etext'를 (앞에 _를 붙이지 않고) define한다면, linker는 프로그램에서 그 definition을 조용히 사용할 것이다. program이 'etext'를 reference하긴 하지만, define하지는 않았다면, linker는 그 linker script 안의 definition을 사용할 것이다.

주의 – PROVIDE directive는 그런 symbol이 PROVIDE가 만드는 symbol과 combined될 수 있다하더라도 defined되는 common symbol로 취급된다. 이는 common symbols로 자주 defined되는 '__CTOR_LIST__'같은 constructor와 destructor list symbols를 고려할 때 특히 중요하다.

PROVIDE_HIDDEN

PROVIDE와 비슷하다. ELF target의 포팅을 위해, 그 symbol은 숨겨지고, export되지 않을 거다.

SECTIONS command

SECTIONS command는 linker에게 input section을 어떻게 output sections으로 map할지, 그리고 output section을 메모리 내에 어떻게 둘지를 알린다.

SECTIONS command의 형식은 다음과 같다.

SECTIONS
{
  sections-command
  sections-command
  ...
}

sections-command는 다음 중 하나가 될 수 있다.

  • ENTRY command (entry point 설정하기 절을 보라)
  • symbol assignment (Symbol에 값 assign하기)
  • output section description
  • overlay description

ENTRY command와 symbol assignment는 편의를 위해 이들 command들 내에 location counter를 사용해서 SECTIONS command 내에 있을 수 있다. 이는 또한 output file의 layout 내에서 의미 있는 지점에서 이들 command를 사용할 수 있기 때문에 linker script를 이해하기 쉽게 만들어 준다.

Output section description과 overlay description은 아래에서 설명한다.

linker script에서 SECTIONS command를 사용하지 않으면, linker는 각 input section을 input file에서 처음 나오는 section 순서대로 이름이 같은 output section내에 둘 것이다. 예를 들어, 첫번째 파일 내에 모든 input section들이 있으면 output file 내의 section들의 순서는 첫번째 파일 내의 순서와 같을 거다. 첫 section은 address 0에 있을 것이다.

Output section description

output section의 전체 description은 다음과 같다.

section [address] [(type)] : [AT(lma)]
  {
    output-section-command
    output-section-command
    ...
  } [>region] [:phdr :phdr ...] [=fillexp]

대부분의 output section은 거의 대부분의 output section attribute를 사용하지 않는다.

section 앞 뒤로 whitespace가 있어야 하므로 section 이름은 모호하지 않게 된다. ‘:’과 ‘{‘, ‘}’ 또한 있어야 한다. 줄을 바꾸거나 다른 white space는 맘대로 하면 된다.

output-section-command는 다음 중 하나일 수 있다.

  • symbol assignment (Symbol에 값 assign하기 절을 보라)
  • input section description (Input section description 절을 보라)
  • 직접 포함하는 data 값 (Output section data 절을 보라)
  • special output section keyword (Output section keyword 절을 보라)

Output section 이름

output section의 이름은 section이다. section은 output format에 따라 제약 사항을 만족해야 한다. 적은 수의 section만을 지원하는 format에서는 a.out같은 이름이 그 format에 의해 지원되는 이름 중의 하나여야 한다(예를 들어, a.out'.text', '.data' 또는 '.bss'만 허용한다). output format이 (Oasys 처럼) 이름이 아닌 숫자로만 모든 section을 지원하면 그 이름은 따옴표로 둘러싼 숫자로된 string으로만 넣어야 한다. section 이름은 문자들로 아무렇게나 구성될 수 있지만 ‘,’같은 일반적인 문자가 있는 이름은 따옴표로 감싸야 한다.

output section 이름 '/DISCARD/'는 좀 특별하다; Output section 폐기 절 참고

Output section address

address는 output section의 VMA (virtual memory address) 에 대한 expression이다. address를 주지 않으면, linker는 region이 있다면 이를 기준으로 설정하고, 아니면 location counter의 현재 값을 기준으로 설정한다.

address를 주면 output section의 address는 그걸로 정확히 설정된다. addressregion 둘 다 주지 않으면, output section의 address는 location counter의 현재 값을 output section의 alignment 요구사항에 맞춰 align된 값으로 설정된다. output section의 alignment 요구사항은 output section 내에 포함된 모든 input section 중 가장 엄격한 alignment가 적용된다.

예를 들어,

.text . : { *(.text) }

.text : { *(.text) }

는 미묘하게 다르다. 첫번째 것은 '.text' output section의 address를 location counter의 현재 값으로 설정할 것이다. 두번째 것은 location counter의 현재 값이 '.text' input section의 가장 엄격한 alignment로 aligned된 값으로 설정된다.

address는 임의의 expression이 될 수 있다; Linker Script내의 Expression 절을 참고. 예를 들어, 0x10 boundary 상에 section을 align하길 원하면, 그 section address의 마지막 4 비트는 0이 되고, 다음처럼 하면 된다.

.text ALIGN(0x10) : { *(.text) }

이는 ALIGN이 현재 location counter를 지정된 값으로 올려서 aligned해서 return하므로 잘 동작한다.

section에 address를 지정하는 것은 location counter의 값을 바꾼다.

Input section description

가장 일반적인 output section command는 input section description이다.

input section description은 가장 기본적인 linker script operation이다. linker에게 메모리 내에 프로그램을 어떻게 lay out할지를 이야기하기 위해 output sections을 사용한다. linker에게 memory layout내로 input file들을 어떻게 map할지를 이야기하기 위해 input section description을 사용한다.

Input section 기본

input section description은 file name으로 구성된다. file name 뒤에는 괄호로 둘러싸인 section name들의 list가 option으로 붙을 수 있다.

file name과 section name은 아래에서 더 설명될 wildcard pattern(Input section wildcard pattern)이 될 수 있다.

가장 일반적인 input section description은 모든 input section을 output section내의 특정 name으로 포함시키는 것이다. 예를 들어, 모든 input '.text' section을 포함시키려면, 아래처럼 쓸 수 있다:

*(.text)

여기서 '*'은 모든 file name에 매치되는 wildcard이다.

둘 이상의 section을 포함하는 두가지 방법이 있다.

*(.text .rdata)
*(.text) *(.rdata)

이 둘의 차이점은 '.text''.rdata' input section이 output section에서 나타날 순서다. 첫번째 예제에서는 이들이 섞인다. 두번째 예제에서는 모든 '.text' input section이 먼저 나타나고 그 다음에 모든 '.rdata' input section들이 뒤따른다.

특정 파일로부터 section들을 포함시킬 때 file name을 지정할 수 있다. 하나 또는 그 이상의 file들이 메모리 내의 특정 위치에 있을 필요가 있는 special data를 갖고 있다면 예를 들어 이렇게 쓸 수 있다.

data.o(.data)

section의 list 없이 file name을 사용한다면, input file내의 이들 모든 section들이 output section 안에 포함될 것이다. 이는 보통 하진 않지만 가끔은 유용할 수 있다. 예를 들면 다음과 같다.

data.o

wild card character가 들어있지 않은 file name을 사용하면 linker는 먼저 linker command line이나 INPUT command 안에 file name이 같이 지정되어 있는지를 먼저 볼 거다. 아니라면 linker는 command line 상에 있을 때처럼 input file로 그 file을 열려고 할거다. 이는 linker가 archive search path에서 그 file을 찾지 않으므로 INPUT command와는 차이가 있다는 것에 주의해라.

Input section wildcard 패턴

input section description에서 file name 또는 section name 아니면 둘 다 wildcard pattern이 될 수 있다.

많은 예제 내에서 봤던 '*'의 file name은 file name에 대한 간단한 wildcard pattern이다.

wildcard pattern은 Unix shell에서 사용되는 것과 비슷하다.

'*'

어떤 수의 character들과도 match

'?'

어떤 single character와 match

'[chars]'

chars의 어떤 single instance와 match; '-' 문자는 소문자를 match하기 위한 '[a-z]'에서처럼 문자의 range를 정하는데 사용된다.

'\'

이 뒤의 다음 character를 quote한다.

file name이 wildcard와 match될 때 wildcard characters는 (Unix 상에서 directory를 나누는데 사용되는) '/' character와는 match되지 않는다. '*'  character를 포함하는 pattern은 예외다; 이는 '/'이 들어 있든 말든 항상 어떤 file name과도 match된다. section 이름에서는 wildcard character가 '/' character와 match된다.

file name wildcard pattern은 command line이나 INPUT command 상에서 명확히 지정된 file들만 match한다. linker는 wildcard를 확장해서 directory를 찾지 않는다.

어떤 file이 하나 또는 그 이상의 wildcard pattern과 match되거나, 또는 어떤 file이 그 file name이 명확히 나타나면서 하나의 wildcard pattern에도 맞으면, linker는 linker script 내에서 먼저 match되는 것을 사용한다.  예를 들어, 다음 순서의 input section description은 'data.o' rule이 사용되지 않기 때문에 아마 error일 거다.

.data : { *(.data) }
.data1 : { data.o(.data) }

보통 linker는 link하는 동안 보인 순서대로 wildcard에 맞는 file과 section을 둔다. 이를 괄호로 싸인 wildcard pattern 전에 (예를 들면, SORT(.text*)처럼) SORT keyword를 써서 바꿀 수 있다. SORT keyword를 쓰면 linker는 output file 안에 file과 section을 두기 전에 name으로 오름차순 정렬한다.

input section들이 가는데가 헷갈린다면 map file을 생성하는 '-M' 링커 옵션을 써라. 이 map 파일은 input section이 output section으로 정확히 어떻게 mapped되는지를 보여준다.

다음 예제는 wildcard pattern이 file을 나누는데 어떻게 사용되는지를 보여준다. 이 linker script는 linker에게 '.text' section을 '.text'안에, 모든 '.bss' section을 '.bss'안에 두도록 지시한다. linker는 대문자로 시작하는 모든 파일로부터의 '.data' section을 '.DATA' 안에 둘 거다. 다른 모든 파일은 linker가 '.data' 안에 '.data'를 둘 거다.

SECTIONS {
  .text : { *(.text) }
  .DATA : { [A-Z]*(.data) }
  .data : { *(.data) }
  .bss : { *(.bss) }
}

common symbol에 대한 input section

common symbol을 위해서는 특별한 표기법이 필요하다. 왜냐하면 많은 object file format에서 common symbol들은 특정 input section을 갖지 않기 때문이다. linker는 common symbol들을 그이름이 'COMMON'인 input section 내에 있는 것처럼 다룬다.

다른 input section들로 하는 것처럼 그냥 'COMMON' section과 file name을 사용할 수 있다. 다른 input file들로부터의 common symbol들을 또 다른 section 내에 두는 것에 비해 한 section 내에 특정 input file로부터의 common symbol들을 두는데 이를 사용할 수 있다.

대부분의 경우에 input file내의 common symbol들은 output file내의 '.bss' section내에 두게 된다. 예를 들면:

.bss { *(.bss) *(COMMON) }

어떤 object file format들은 하나 이상의 common symbol 타입을 갖는다. 예를 들면, MIPS ELF object file format은 standard common symbol들과 small common symbol을 구별한다. 이 경우, linker는 다른 common symbol 타입을 위한 다른 특별한 section name과 '.scommon'을 small common symbol을 위해 사용할 거다. 이는 common symbol의 다른 타입을 메모리의 다른 위치에 map할 수 있도록 한다.

오래된 linker scripts에서는 '[COMMON]'을 볼 수도 있다. 이 표기는 지금 구식으로 취급된다. 이는 '*(COMMON)'과 동등하다.

Input section과 garbage collection

link-time garbage collection이 사용중이면 ('--gc-sections'), 제거되지 말아야할 section들을 표시하는 것이 꽤 유용하다. 이는 KEEP(*(.init)) 이나 KEEP(SORT(*)(.ctors)) 처럼 input section의 wildcard entry를 KEEP()으로 둘러쌈으로써 할 수 있다.

Input section 예제

다음 예제는 완전한 linker script이다. 이는 linker에게 file 'all.o'로부터 모든 section들을 읽어서 그들을 ‘0x10000’위치에서 시작하는 output section 'outputa'의 시작에 두도록 이야기 한다. 그 바로 뒤에 'foo.o' file로부터의 section '.input1' 전부가 같은 output section내에 따른다. 'foo.o'로부터의 section '.input2' 전부는 output section 'outputb'내로 가고, 그 뒤에 'foo1.o'로부터의 section '.input1'이 따른다. 남은 다른 모든 파일로부터의 '.input1''.input2' section들 전부가 output section 'outputc'로 쓰여진다.

SECTIONS {
  outputa 0x10000 :
    {
    all.o
    foo.o (.input1)
    }
  outputb :
    {
    foo.o (.input2)
    foo1.o (.input1)
    }
  outputc :
    {
    *(.input1)
    *(.input2)
    }
}

Output section data

output section command로 output section 내에 BYTE, SHORT, LONG, QUAD 또는 SQUAD를 사용해서 byte 수를 정하는 data를 넣을 수 있다. 각 keyword 뒤에는 괄호 안에 저장할 값을 쓴 expression이 따른다 (Linker Scripts안의 Expression절을 보라). expression의 값은 location counter의 현재 값에 저장된다.

BYTE, SHORT, LONG과 QUAD command는 1, 2, 4, 그리고 8 bytes를 (각각) 저장한다. Byte들을 저장하고 나서 location counter는 저장된 byte의 수만큼 증가한다.

예를 들어, 다음은 byte 1을 저장하고 symbol ‘addr’의 4 byte 값이 뒤에 온다:

BYTE(1)
LONG(addr)

64 bit host 또는 target을 사용하면 QUADSQUAD는 같다; 둘 다 8 byte 또는 64 bit 값을 저장한다. host와 target 모두 32 bit를 사용하면, expression은 32 bit로 계산된다. 이 경우 QUAD는 64 bit로 zero extended된 32 bit 값을 저장하고, SQUAD는 64 bit로 sign extended된 32 bit 값을 저장한다.

output file의 보통의 경우처럼 output file format이 endianness를 명시하면, 그 값은 그 endianness에 따라 저장된다. object file format이 endianness를 명시하지 않으면, 예를 들어 S-records가 true인 경우, 그 값은 첫 input object file의 endianness에 따라 저장된다.

현재 section의 fill pattern을 설정하려면 FILL command를 사용할 수 있다. 이것 뒤에는 괄호 안의 expression이 온다. 그 section 내의 다른 모든 지정되지 않은 memory regions (예를 들어, input sections에 요구되는 alignment로 인해 남는 gap들)은 그 expression의 two least significant bytes로 필요하면 반복하면서 채워진다. FILL statement는 그 section definition 내에 나타나는 지점 이후의 memory 위치에 적용된다. 하나 이상의 FILL statement를 넣으므로써 output section의 다른 부분에 다른 fill pattern을 쓸 수 있다.

다음 예제는 값 '0x9090'으로 지정되지 않는 메모리 region을 어떻게 채우는지를 보여준다.

FILL(0x9090)

FILL command는 ‘=fillexp‘ output section attribute와 비슷하지만 (Output section 채우기 절을 보라), 전체 section이 아닌, FILL command 다음의 section 부분에만 영향을 미친다. 둘 다 사용되면 FILL command가 우선된다.

Output section keywords

output section commands처럼 쓸 수 있는 두가지 keyword가 있다.

CREATE_OBJECT_SYMBOLS

이 command는 linker에게 각 input file을 위한 symbol을 만들라고 이야기한다. 각 symbol의 이름은 해당하는 input file의 이름이 된다. 각 symbol의 section은 CREATE_OBJECT_SYMBOLS command가 있는 그 output section이 된다. 이는 a.out object file format에 편리하다. 다른 object file에서는 거의 사용되지 않을 것이다.

CONSTRUCTORS

a.out object file format을 사용해서 linking할 때 linker는 C++ global constructors와 destructors를 지원하기 위해 unusual set construct를 사용한다. ECOFF나 XCOFF 같은 arbitrary section을 지원하지 않는 object file format들을 linking할 때 linker는 알아서 그 이름으로 C++ global constructors와 destructors를 인식한다. 이들 object file format들을 위해서 CONSTRUCTORS command가 linker에게 CONSTRUCTORS command가 나오는 output section 내에 constructor information을 두도록 알려준다. CONSTRUCTORS command는 다른 object file format들에서는 무시된다. symbol __CTOR_LIST__ 는 global constructors의 시작을 표시하고, symbol __DTOR_LIST는 그 끝을 표시한다. list 내의 첫번째 word는 entries의 수이고, 그 뒤에 각 consructor나 destructor의 address가, 그 뒤에 zero word가 하나 붙는다. compiler는 실제로 code를 실행하기 위헤서 조정을 해야 한다. 이들 object file format들을 위해 GNU C++은 보통 subroutine __main으로부터 constructors를 호출한다; __main의 호출은 자동으로 main을 위한 startup code안으로 들어간다. GNU C++은 보통 destructors를 atexit를 사용하거나 아니면 exit로부터 그 funtion을 직접 호출해서 실행한다. arbitrary section name을 지원하는 COFF나 ELF 같은 object file format들에는 GNU C++은 .ctors.dtors sections 안에 global constructors와 destructors의 addresses를 두도록 조정할 것이다. 다음 sequence를 linker script에 두는 것은 GNU C++ runtime code가 보는 table을 build할 것이다.

      __CTOR_LIST__ = .;
      LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
      *(.ctors)
      LONG(0)
      __CTOR_END__ = .;
      __DTOR_LIST__ = .;
      LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
      *(.dtors)
      LONG(0)
      __DTOR_END__ = .;

initialization priority에 global constructors가 실행되는 순서 상의 control을 제공하는 GNU C++ support를 사용하고 있다면, 반드시 정확한 순서로 그들이 실행됨을 보장하기 위해 link time에 constructors를 sort해야 한다. CONSTUCTORS command를 사용하려면, 대신 'SORT(CONSTRUCTORS)'를 사용하라. .ctors.dtors section을 사용하려면, 그냥 '*(.ctors)''*(.dtors)' 대신에 '*(SORT(.ctors))''*(SORT(.dtors))'를 사용하라. 보통 compiler와 linker는 이들 이슈를 알아서 처리할 것이고, 이에 대해 신경쓸 필요가 없을 것이다. 그러나 C++을 사용하고 linker scripts를 작성한다면 이를 고려할 필요가 있을 수 있다.

Output section discarding

linker는 아무 내용도 없는 output section을 만들지 않을 것이다. 이는 어떤 input files내에 있을 수 있는, 혹은 없을 수 있는 input section들을 참조하는데 편리하다. 예를 들어:

.foo { *(.foo) }

는 ‘.foo’ section이 적어도 하나의 input file내에 있을 때만 ‘.foo’ section을 output file 내에 만들거다.

input section description보다는 symbol assignment같은 output section command를 사용한다면, 맞는 input section들이 없어도 output section은 언제나 만들어 질 것이다.

special output section name ‘/DISCARD/’는 input sections을 discard할 때 사용될 수 있다. 이름이 ‘/DISCARD/’인 output section으로 assigned되는 모든 input section들은 output file내에 포함되지 않는다.

Output section attributes

다음처럼 생긴 output section의 full description을 위에서 보았다.

section [address] [(type)] : [AT(lma)]
  {
    output-section-command
    output-section-command
    ...
  } [>region] [:phdr :phdr ...] [=fillexp]

이미 section, address, output-section-command에 대해 설명했다. 이번 절에서는 남은 section attributes를 설명할 거다.

Output section type

각 output section은 type을 가질 수 있다. 이 type은 괄호 안의 keyword이다. 다음 type들이 정의된다:

NOLOAD

그 section은 not loadable로 표시되어야 한다. 그래서 프로그램이 실행될 때 memory로 loaded되지 않을 거다.

DSECT

COPY

INFO

OVERLAY

이들 type 이름들은 backward compatibility를 위해서 지원된다. 잘 쓰이진 않는다. 모두 같은 효과를 갖는다. 그 section은 not allocatable로 표시되어야 한다. 그래서 프로그램이 실행될 때 그 section을 위한 memory가 allocated되지 않는다.

linker는 보통 그에 map되는 input section에 기초해서 output section의 attributes를 set한다. section type을 사용해서 이를 override할 수 있다. 예를 들어, 아래의 script sample에서 ‘ROM’ section은 memory location ‘0’에 지정되고, 프로그램이 실행될 때 loaded될 필요는 없다. ‘ROM’ section의 내용은 linker output file내에 평소처럼 나올거다.

SECTIONS {
  ROM 0 (NOLOAD) : { ... }
  ...
}

Output section LMA

모든 section은 virtual address (VMA)와 load address (LMA)를 갖는다; 기본적인 개념 절을 보라. output section description에 있을 수 있는 address expression은 VMA를 set한다 (Output section address절을 보라).

linker는 보통 LMA를 VMA와 같도록 set한다. 이를 AT keyword를 사용해서 바꿀 수 있다. AT keyword 뒤의 expression lma는 그 section의 load address를 지정한다.

이 기능은 ROM image를 쉽게 build하도록 design되었다. 예를 들어, 다음 linker script는 3 output section을 만든다: 0x1000에서 시작하는 '.text', VMA는 0x2000에서 시작하지만 '.text' section의 끝에 load되는 '.mdata', 그리고, address 0x3000에 uninitialized data를 잡는 '.bss'. symbol _data0x2000으로 정의되는데 이는 location counter가 LMA 값이 아닌 VMA 값을 담는다는 것을 보여준다.

SECTIONS
  {
  .text 0x1000 : { *(.text) _etext = . ; }
  .mdata 0x2000 : 
    AT ( ADDR (.text) + SIZEOF (.text) )
    { _data = . ; *(.data); _edata = . ;  }
  .bss 0x3000 :
    { _bstart = . ;  *(.bss) *(COMMON) ; _bend = . ;}
}

이 linker script로 generate되는 프로그램을 위한 실제 run-time initialization code는 ROM image로부터 initialized data를 그 runtime address로 복사하기 위해서 다음과 같은 것을 포함할거다. 이 코드가 linker script가 define하는 symbol들을 어떻게 유용하게 쓰는지에 주목하라.

extern char _etext, _data, _edata, _bstart, _bend;
char *src = &_etext;
char *dst = &_data;

/* ROM has data at end of text; copy it. */
while (dst < &_edata) {
  *dst++ = *src++;
}

/* Zero bss */
for (dst = &_bstart; dst< &_bend; dst++)
  *dst = 0;

Output section region

'>region'을 사용해서 한 section을 이전에 정의된 memory region으로 assign할 수 있다. MEMORY command 절을 보라.

여기 간단한 예제다:

MEMORY { rom : ORIGIN = 0x1000, LENGTH = 0x1000 }
SECTIONS { ROM : { *(.text) } >rom }

Output section phdr

':phdr'을 사용해서 한 section을 이전에 define된 program segment로 assign할 수 있다. PHDRS command 절을 보라. 한 섹션이 하나 또는 그 이상의 segments로 assign되면, 명시적으로 :phdr modifier가 사용되기 전까지 모든 subsequent allocated sections들이 그들 segments로 assign될 것이다. :NONE으로 linker에게 모든 segment에 그 section을 두지 않을 것임을 알려줄 수 있다.

여기 간단한 예제다:

PHDRS { text PT_LOAD ; }
SECTIONS { .text : { *(.text) } :text }

Output section fill

'=fillexp'를 사용해서 전체 section을 위한 fill pattern을 set할 수 있다. fillexp는 expression이다 (Linker Scripts에서 Expression절을 보라). 그 output section 내의 memory의 모든 unspecified regions (예를 들어, input sections의 required alignment로 인해 남는 gaps) 은 그 값의 2 least significant bytes로 필요하면 반복적으로 채워질거다.

output section commands내에 FILL command로 fill value를 바꿀 수 있다; Output section data 절을 보라.

여기 간단한 예제다:

SECTIONS { .text : { *(.text) } =0x9090 }

Overlay description

overlay description은 single memory image의 일부로 load되지만 같은 memory address에서 실행되는 section들을 describe하는 쉬운 방법을 제공한다. run time에 어떤 overlay manager가 overlaid sections를 요구사항에 맞게 아마 간단한 addressing bit를 조작해서 runtime memory address의 안과 밖에 copy할 거다. 이 방식은 예를 들면 특정 memory region이 다른 곳보다 빠를 때 유용할 수 있다.

Overlays는 OVERLAY command를 사용해서 describe한다. OVERLAY comand는 output section description처럼 SECTIONS command 안에서 사용된다. OVERLAY command의 full syntax는 다음과 같다:

OVERLAY [start] : [NOCROSSREFS] [AT ( ldaddr )]
  {
    secname1
      {
        output-section-command
        output-section-command
        ...
      } [:phdr...] [=fill]
    secname2
      {
        output-section-command
        output-section-command
        ...
      } [:phdr...] [=fill]
    ...
  } [>region] [:phdr...] [=fill]

OVERLAY (키워드) 를 빼고 모두 옵션이고, 각 section은 name (위에서 secname1secname2)을 가져야 한다. OVERLAY construct 내의 section definitions은 OVERLAY 내에 section들을 위해 define되는 address와 memory regions들이 없다는 것을 제외하면 일반적인 SECTIONS contruct (SECTIONS command를 보라)내의 것들과 동일하다.

그 sections들은 모두 같은 starting address로 defined된다. 그 sections들의 load addresses는 전체에 OVERLAY를 위해 사용되는 load address에서 시작하는 memory 내에 consecutive하게 조정된다 (보통 section definitions처럼 load address는 옵션이고 default는 start address이다; start address 또한 옵션이고, default는 현재 location counter 값이다).

NOCROSSREFS keyword가 사용되면 sections 사이에 어떤 reference가 있다면, linker는 error로 report할 거다. 같은 address에서 section 모두가 실행되면, 보통 한 section이 다른 section에 직접 refer하는 것은 부적절하다. 다른 linker script command 절을 보라.

OVERLAY 내의 각 모든 section에 대해 linker는 자동으로 2개의 symbol을 define한다. symbol __load_start_secname이 그 section의 starting load address로 defined된다. symbol __load_stop_secname이 그 section의 final load address로 defined된다. C identifiers내에 있지 않은 secname 내의 모든 문자는 제거된다. C (또는 assembler) code는 이들 symbol을 필요에 따라 overlaid section 근처로 옮기는데 사용할 수 있다.

overlay의 끝에서 location counter의 값은 overlay의 address + 가장 큰 section의 size가 된다.

여기 예제다. SECTION construct 안에 이게 있어야 함을 기억해라.

  OVERLAY 0x1000 : AT (0x4000)
   {
     .text0 { o1/*.o(.text) }
     .text1 { o2/*.o(.text) }
   }

이는 address 0x1000에서 시작하는 '.text0''.text1' 모두를 define한다. '.text0'는 address 0x4000에 load되고, '.text1''.text0' 바로 뒤에 load될 것이다. 다음 symbol들이 defined된다: __load_start_text0__load_stop_text0__load_start_text1__load_stop_text1.

overlay .text1을 overlay area로 복사하는 코드는 다음과 같을 것이다.

  extern char __load_start_text1, __load_stop_text1;
  memcpy ((char *) 0x1000, &__load_start_text1,
          &__load_stop_text1 - &__load_start_text1);

OVERLAY command는 단지 유용한 문법임을 모든 것은 좀 더 많은 basic command를 사용해서 할 수 있음을 알아둬라. 위의 예제는 아래처럼 똑같이 쓰여질 수 있다.

  .text0 0x1000 : AT (0x4000) { o1/*.o(.text) }
  __load_start_text0 = LOADADDR (.text0);
  __load_stop_text0 = LOADADDR (.text0) + SIZEOF (.text0);
  .text1 0x1000 : AT (0x4000 + SIZEOF (.text0)) { o2/*.o(.text) }
  __load_start_text1 = LOADADDR (.text1);
  __load_stop_text1 = LOADADDR (.text1) + SIZEOF (.text1);
  . = 0x1000 + MAX (SIZEOF (.text0), SIZEOF (.text1));

MEMORY command

linker의 default configuration은 모든 available memory의 allocation을 허용한다. MEMORY command를 사용해서 이를 override할 수 있다.

MEMORY command는 target 내의 memory block의 location과 size를 describe한다. 이걸로 linker에게 어떤 memory regions들은 사용할지, 어떤 memory regions들은 사용하지 말아야 할지를 describe할 수 있다. 그러고 나면 sections들을 특정 memory regions에 assign할 수 있다. linker는 section addresses를 memory regions에 기초해서 set하고. 너무 꽉 차는 regions들에 대해 경고할 것이다. linker는 available regions내로 맞추기 위해서 section들을 섞지 않는다.

linker script는 최대 하나의 MEMORY command를 사용할 수 있다. 하지만, 원하는 만큼 그 안에 많은 memory의 blocks를 define할 수 있다. 문법은 다음과 같다:

MEMORY 
  {
    name [(attr)] : ORIGIN = origin, LENGTH = len
    ...
  }

name은 linker script 안에서 그 region을 참조하는 이름이다. region name은 linker script 밖에서는 의미가 없다. Region names들은 분리된 name space 안에 저장되고, symbol names, file names, 또는 section names와 conflict이 일어나지 않는다. 각 memory region은 구별되는 name을 가져야 한다.

attr string은 특정 명시적으로 linker script 내에서 mapped되지 않는 input section을 위한 memory region을 사용할지를 지정하는 attributes의 optional list이다. SECTION command 절에서 설명한대로, input section에 대해 output section을 지정하지 않으면, linker는 input section과 같은 이름으로 output section을 만들 것이다. region attributes를 define하면, linker는 생성되는 output section위한 memory region을 선택하는데 그것들을 사용할 거다.

attr string은 다음 characters 중 하나로 구성되어야 한다:

R'</samp>
<p style="padding-left: 40px;">Read-only section</p>
<samp>
W’

Read/write section

X'</samp>
<p style="padding-left: 40px;">Executable section</p>
<samp>
A’

Allocatable section

I'</samp>
<p style="padding-left: 40px;">Initialized section</p>
<samp>
L’

`I’와 같다

`!’

앞에 붙은 attributes 모두를 뒤집기

unmapped section이 '!' 아닌 listed attributes 중 어느 하나에 match되면, memory region내에 두게 된다. '!' attributes는 이 test를 뒤집는다. 그래서 unmapped section이 listed attributes 중 어느 하나에도 match되지 않으면 그 memory region내에 두게 된다.

origin은 memory region의 start address를 위한 expression이다. 그 expression은 memory allocation을 하기 전에 constant로 evaluate해야 한다. 이는 즉, relative symbol을 사용할 수 없다는 뜻이다. keyword ORIGINorg 또는 o로 줄여 쓸 수 있다(그러나 ORG같은건 안된다).

len은 memory region의 bytes로 된 size를 위한 expression이다. origin expression과 같이 쓰면, 그 expression은 memory allocation을 하기 전에 constant로 evaluate해야 한다. keyword LENGTHlen이나 l로 줄여 쓸 수 있다.

다음 예제에서 allocation을 위해 available한 2개의 memory regions이 있다고 지정한다: 하나는 '0'에서 시작하는 256 kilobytes 하나, 4 megabytes인 '0x40000000'에서 시작하는 다른 하나. linker는 명시적으로 memory region 내로 mapped하지 않는, read-only 또는 excutable 중 하나인 모든 section울 'rom' memory region 내로 둘 거다. linker는 명시적으로 memory region내로 mapped하지 않는 다른 sections들을 'ram' memory region 내에 둘 것이다.

MEMORY 
  {
    rom (rx)  : ORIGIN = 0, LENGTH = 256K
    ram (!rx) : org = 0x40000000, l = 4M
  }

memory region을 define하고 나면, '>region' output section attributes를 사용해서 linker에게 그 memory region내로 지정한 output section을 두도록 지시할 수 있다. 예를 들어, 'mem'이라는 memory region이 있으면, 그 output section definition 내에 '>mem'를 사용할 수 있다. Output section region 절을 보라. output section에 아무 address도 지정되어 있지 않으면, linker는 그 memory region 내의 다음 available address로 그 address를 set할 거다. 한 memory region으로 direct된 combined output section이 그 region에 비해 너무 크면, linker는 error message를 올릴 거다.

PHDRS command

ELF object file format은 segments로 알려진 program headers를 사용한다. program headers는 어떻게 program이 memory 내로 load되어야 하는지를 describe한다. objdump 프로그램에 ‘-p’ 옵션을 사용해서 이를 출력할 수 있다.

native ELF system 상에서 ELF program을 실행하면, system loader는 그 program을 load할지를 살펴보기 위해서 program headers를 읽는다. 이는 그 program header가 맞게 set되어있어야만 동작한다. 이 매뉴얼은 어떻게 system loader가 program header를 interpret하는지 세부사항은 describe하지 않는다; 더 자세히는 ELF ABI를 보라.

linker는 default로 reasonable한 program headers를 생성한다. 그러나 어떤 경우에 더 세부적인 program headers를 지정할 필요가 있을 수 있다. 이 목적으로 PHDRS command를 사용할 수 있다. linker가 PHDRS command를 linker script안에서 보면, 지정된 것 외에 어떤 program headers도 만들지 않을 거다.

linker는 ELF output file을 generate할 때만 PHDRS command를 신경쓴다. 다른 때는 PHDRS를 그냥 무시할거다.

PHDRS command의 문법은 다음과 같다. words PHDRS, FILEHDR, AT 그리고 FLAGS가 keywords이다.

PHDRS
{
  name type [ FILEHDR ] [ PHDRS ] [ AT ( address ) ]
        [ FLAGS ( flags ) ] ;
}

name은 linker script의 SECTIONS command 내에서 참조를 위해서만 사용된다. 이를 output file내에 두지는 않는다. Program header names은 분리된 name space에 저장되고, symbol names, file names, 또는 section names와 conflict이 일어나지 않는다. 각 program header는 구별되는 이름을 가져야 한다.

특정 program header types들은 system loader가 file로부터 load할 memory의 segments를 describe한다. linker script내에서 그 segments내에 allocatable output sections를 둠으로써 이들 segments의 contents를 지정한다. 특정 segment내에 section을 두는데는 ':phdr' output section attribute를 사용한다. Output section phdr 절을 보라.

하나 이상의 segment 내에 특정 sections들을 두는 것이 보통이다. 이는 그냥 다른 것을 하나의 memory segment가 포함함을 의미한다. section을 포함할 각 segment마다 한번씩 사용해서 ':phdr'를 반복할 수 있다.

':phdr'를 사용해서 하나 이상의 segments 내에 한 section을 두면, linker는 같은 segments 내에 ':phdr'을 지정하지 않은 모든 이어지는 allocatable sections들을 둘 거다. 이는 일반적으로 single segment 내에 contiguous sections 전체를 두기 때문에 편의를 위한 것이다. default segment를 override하기 위해서, 그리고 linker에게 어떤 segment에도 그 section을 전혀 두지 않겠다고 알리기 위해서 :NONE을 사용할 수 있다.

segment의 contents를 더 많이 describe하기 위해서 program header type 뒤에 FILEHDRPHDRS keyword를 사용할 수 있다. FILEHDR keyword는 그 segment가 ELF file header를 포함해야 함을 의미한다. PHDRS keyword는 그 segment가 ELF program headers 그 자체를 포함해야 함을 의미한다.

type은 다음 중 하나일 수 있다. 숫자는 keyword의 값을 나타낸다.

PT-NULL (0)

unused program header를 나타냄.

PT_LOAD (1)

이 program header는 file로부터 load되는 segment를 describe함을 나타냄.

PT_DYNAMIC (2)

dynamic linking information이 있을 수 있는 segment를 나타냄

PT_INTERP (3)

program interpreter의 name이 있을 수 있는 segment를 나타냄

PT_NOTE (4)

note information을 담고 있는 segment를 나타냄.

PT_SHLIB (5)

ELF ABI에 의해 지정되지 않지만 defined된 reserved program header type

PT_PHDR (6)

program header가 있을 수 있는 segment를 나타냄

expression

program header의 numeric type을 주는 expression. 이는 위에서 defined되지 않은 type을 위해 사용될 수 있음.

AT expression을 사용해서 memory 내의 특정 address에 load되어야 하는 segment를 지정할 수 있다. 이는 output section attribute로 사용되는 AT command와 완전히 같다 (Output section LMA 절을 보라). program header를 위한 AT command는 output section attribute를 override한다.

linker는 보통 segment를 구성하는 section에 기초해서 segment flags를 set할거다. segment flags를 명시적으로 지정하기 위해서 FLAGS keyword를 사용할 수 있다. flags의 값은 integer여야 한다. 이는 program header의 p_flags field를 set하는데 사용될 수 있다.

여기 PHDRS의 예제다. 이는 native ELF system 상에서 사용되는 program header의 전형적인 set을 보여준다.

PHDRS
{
  headers PT_PHDR PHDRS ;
  interp PT_INTERP ;
  text PT_LOAD FILEHDR PHDRS ;
  data PT_LOAD ;
  dynamic PT_DYNAMIC ;
}

SECTIONS
{
  . = SIZEOF_HEADERS;
  .interp : { *(.interp) } :text :interp
  .text : { *(.text) } :text
  .rodata : { *(.rodata) } /* defaults to :text */
  ...
  . = . + 0x1000; /* move to a new page in memory */
  .data : { *(.data) } :data
  .dynamic : { *(.dynamic) } :data :dynamic
  ...
}

VERSION command

(생략)

Linker Scripts내의 Expression

linker script language 내의 expression의 syntax는 C expression의 그것과 완전히 같다. 모든 expressions는 integer로 evaluated된다. 모든 expression은 host와 target이 모두 32 bits면 32 bits로, 아니면 64 bits로, 모두 같은 size로 evaluated된다.

expressions내의 symbol values를 set하고 사용할 수 있다.

linker는 expression내의 사용을 위해 여러 특별 목적의 builtin functions을 define한다.

  • Constants: Constants
  • Symbols: Symbol Names
  • Location Counter: Location Counter
  • Operators: Operators
  • Evaluation: Evaluation
  • Expression Section: Expression의 Section
  • Builtin Functions: Builtin Functions

Constants

모든 constants는 integers다.

C에서처럼 linker는 '0'으로 시작하는 integer는 8진수이고, '0x' 또는 '0X'로 시작하는 integer는 16진수다. linker는 다른 integers는 10진수로 생각할 거다.

추가로 suffixes KM을 각각 constant의 scale로 사용할 수 있다. 예를 들어 다음 모두는 같은 값을 참조한다:

  _fourk_1 = 4K;
  _fourk_2 = 4096;
  _fourk_3 = 0x1000;

Symbol Names

quoted되지 않는 한 symbol name은 letter, '_', 또는 '.'으로 시작하고, letters, digits, '_', '.', '-'을 포함할 수 있다. Unquoted symbol names는 어떤 keywords와도 conflict되지 않아야 한다. odd characters를 포함하거나 keyword와 같은 이름을 가진 symbol을 double quoted로 둘러싸서 지정할 수 있다:

  "SECTION" = 9;
  "with a space" = "also with a space" + 10;

symbol이 많은 non-alphabetic characters를 포함할 수 있기 때문에, spaces로 symbol들을 구분하는게 안전하다. 예를 들어, ‘A – B’는 뻴셈 expression이지만, ‘A-B’는 하나의 symbol이다.

Location Counter

특별한 linker variable dot '.' 은 언제나 현재 output location counter를 담는다. .이 언제나 output section 내의 한 위치를 가리키므로, 이는 SECTION command 안의 expression 에서만 보일 수 있다. . symbol은 expression 내에 보통 symbol이 있을 수 있는 곳 어디에나 있을 수 있다.

. 에 값을 assign하는 것은 location counter를 옮기게 한다. 이는 output section에 holes를 만들 때 사용될 수 있다. location counter는 뒤로 옮겨지진 않을 거다.

SECTIONS
{
  output :
    {
      file1(.text)
      . = . + 1000;
      file2(.text)
      . += 1000;
      file3(.text)
    } = 0x1234;
}

위의 예제에서 'file1'으로부터의 '.text' section은 output section 'output'의 시작에 위치한다. 그 뒤에 1000 byte gap이 붙는다. 그리고 'file2'로부터의 '.text' section이 나오고, 또 1000 byte gap이 'file3'로부터의 '.text' 앞에 붙는다. '= 0x1234' 표기는 gap들에 쓰여질 data를 지정한다 (Output section fill 절을 보라).

Operators

linker는 표준 C의 arithmetic operators를 같은 standard binding과 우선순위 레벨로 인식한다:

우선순위         associativity   Operators                Notes
(highest)
1               left            !  -  ~                  (1)
2               left            *  /  %
3               left            +  -
4               left            >>  <<
5               left            ==  !=  >  <  <=  >=
6               left            &
7               left            |
8               left            &&
9               left            ||
10              right           ? :
11              right           &=  +=  -=  *=  /=       (2)
(lowest)

Notes: (1) Prefix operators (2) Assignments를 보라

Evaluation

linker는 lazy하게 expression을 evaluate한다. 전적으로 필요할 때만 expression의 값을 계산한다.

linker는 어떤 linking이든 전부 하기 위해서 첫번째 section의 start address 값, memory regions의 origin과 길이 같은 information을 필요로 한다. 이들 값은 linker가 linker scripts내에서 읽는대로 바로 계산된다. 그러나 (symbol values 같은) 다른 값들은 storage allocation 후까지 필요하지 않거나 모른다. 이런 값들은 나중에 (output section의 size 같은) 다른 information이 symbol assignment expression 내에서 사용되기 위해 available할 때 evaluated된다.

sections의 size는 allocation 이후까지 알 수 없기 때문에 assignments는 allocation 이후까지 수행되지 않는 것에 의존한다.

location counter '.'에 의존하는 것 같은 어떤 expression은 section allocation 중에 evaluated되어야 한다.

expression의 결과가 필요하지만, 그 값이 available하지 않으면 error가 난다. 예를 들어 다음과 같은 script는

SECTIONS
  {
    .text 9+this_isnt_constant : 
      { *(.text) }
  }

error message `non constant expression for initial address'를 내게 한다.

Expression의 Section

linker가 expression을 evaluate하면 그 결과는 absolute이거나 어떤 section에 relative하거나 둘 중 하나다. relative expression은 section의 base로부터 fixed offset으로 표현된다.

linker script내의 expression의 위치가 그게 absolute인지 relative인지를 결정한다. output section definition 내에 있는 expression은 그 output section의 base에서 relative이다. 다른 곳에 있는 expression은 absolute가 된다.

'-r' 옵션을 사용해서 relocatable output을 요청하면 relative expression으로의 symbol set은 relocatable일 거다. 이는 그 이상의 link operation이 symbol의 값을 바꿀 수 있음을 의미한다. 그 symbol의 section은 relative expression의 그 section이 될 거다.

absolute expression으로의 symbol set은 모든 그 이상의 link operation을 통해 같은 값으로 유지될 거다. 그 symbol은 absolute가 될 거고 다른 특정 연관된 section을 갖지 않을 거다.

builtin function ABSOLUTE를 사용해서 지정하지 않았다면 relative가 될 때, 그 expression이 absolute가 되도록 강제할 수 있다. 예를 들어 output section '.data'의 end address로 absolute symbol set을 만드려면:

SECTIONS
  {
    .data : { *(.data) _edata = ABSOLUTE(.); }
  }

'ABSOLUTE'가 쓰이지 않았다면 '_edata''.data' section의 relative가 됐을 거다.

Builtin Functions

linker script language는 linker script expression 내에 쓸 수 있는 많은 builtin function을 갖고 있다.

ABSOLUTE(exp)

expression exp의 absolute (non-negative와 반대인 non-relocatable) 값을 return. section definition 내에서 symbol 값이 보통 relative가 되는 데서 symbol로 absolute 값을 assign하는데 주로 유용함. Expression 절을 보라.

ADDR(section)

section 이름의 address (VMA)를 return. 사용 전에 그 section의 location을 defined했어야 한다. 다음 예제에서 symbol_1.output1 section의 relative가 되고 다른 둘은 absolute가 되는 것을 제외하면 start_of_output1, symbol_1symbol_2는 동등한 값이 assigned됨:

SECTIONS { …
  .output1 :
    {
    start_of_output_1 = ABSOLUTE(.);
    …
    }
  .output :
    {
    symbol_1 = ADDR(.output1);
    symbol_2 = start_of_output_1;
    }
… }

ALIGN(align)

ALIGN(exp,align)

다음 align boundary로 aligned된 location counter (.) 나 임의의 expression을 return. operand가 하나인 ALIGN은 location counter의 값을 바꾸지 않는다. 그 값에 arithmetic을 할 뿐이다. operand가 둘인 ALIGN은 임의의 expression이 위로 aligned되도록 할 수 있다 (ALIGN(align)ALIGN(ABSOLUTE(.), align)과 동등하다).

여기 앞의 section 이후에 output .data section을 다음 0x2000 byte boundary로 align하고, input section 이후에 다음 0x8000 boundary로의 section 내에 variable을 set하는 예제다:

SECTIONS { ...
  .data ALIGN(0x2000): {
    *(.data)
    variable = ALIGN(0x8000);
  }
... }

이 예제에서의 ALIGN의 첫번째 용도는 section definition의 optional address attribute로 사용되었기 때문에 section의 위치를 지정하는 거다 (Output Section Address절을 보라). ALIGN의 두번째 용도는 symbol의 값을 define하는데 사용하는 거다.

builtin function NEXTALIGN과 매우 관련이 있다.

ALIGNOF(section)

그 section이 allocated되었다면, section이 이름인 것의 bytes로의 alignment를 return. 그 section이 evaluated될 때 allocated되지 않았다면, linker는 error를 report할거다. 다음 예제에서 .output section의 alignment가 그 section내의 첫번째 값으로 저장된다.

 

SECTIONS{ …
  .output {
    LONG (ALIGNOF (.output))
    …
    }
… }

BLOCK(exp)

이는 오래된 linker script의 compatibility를 위한 ALIGN의 다른 이름이다. output section의 address를 setting할 때 자주 보인다.

DATA_SEGMENT_ALIGN(maxpagesizecommonpagesize)

 이는 다음 둘 중 하나와 동등하다.

(ALIGN(maxpagesize) + (. & (maxpagesize - 1)))
또는
(ALIGN(maxpagesize)
 + ((. + commonpagesize - 1) & (maxpagesize - commonpagesize)))

뒤의 것이 (이 expression의 결과와 DATA_SEGMENT_END 사이의 area인) data segment를 위해 더 작은 commonpagesize size의 pages를 사용하는지에 따라 앞의 것인지 아닌지가 달려 있다. 뒤의 form이 사용되면, runtime memory의 commonpagesize bytes가 on-disk file 내의 최대 commonpagesize로 낭비되는 bytes까지 포함해서 save될 것임을 의미한다.

이 expression은 SECTIONS command들 내에 직접 사용될 수 있지만, 모든 output section description에는 있을 수 없고, linker script에 딱 한번만 나올 수 있다. 최대 maxpagesize system page sizes 상에서 실행되는 동안, commonpagesize는 maxpagesize 이하여야 하고, object가 optimized되기를 원하는 system page size여야 한다. 그러나 system page size가 commonpagesize보다 크면 '-z relro' protection이 영향을 미치지 않음을 주의하라.

예제:

  . = DATA_SEGMENT_ALIGN(0x10000, 0x2000);

DATA_SEGMENT_END(exp)

이는 DATA_SEGMENT_ALIGN evaluation 목적의 data segment의 끝을 define한다.

  . = DATA_SEGMENT_END(.);

DATA_SEGMENT_RELRO_END(offsetexp)

이는 '-z relro'옵션을 사용했을 때 PT_GNU_RELRO segment의 끝을 define한다. '-z relro'옵션이 없으면, DATA_SEGMENT_RELRO_END가 아무것도 하지 않지만, 다른 때는 DATA_SEGMENT_ALIGN이 padded되어서 DATA_SEGMENT_ALIGN에 주어진 commonpagesize argument로 exp + offset이 aligned된다. linker script내에 두려면, DATA_SEGMENT_ALIGN과 DATA_SEGMENT_END 사이에 이를 두어야 한다. 두번째 argument로 인한 PT_GNU_RELRO segment의 끝에서 두번째 argument + 필요한 모든 padding을 evaluate하라.

  . = DATA_SEGMENT_RELRO_END(24, .);

DEFINED(symbol)

symbol이 linker global symbol table안에 defined되어 있으면 1을, 아니면 0을 return. symbol의 default values를 제공하는데 이 function을 사용할 수 있다. 예를 들어, 다음 script 조각은 global symbol 'begin''.text' section 내의 첫번째 위치로 어떻게 set하는지를 보여준다–그러나 'begin'이란 symbol이 이미 있다면, 그 값이 유지된다:

SECTIONS { ...
  .text : {
    begin = DEFINED(begin) ? begin : . ;
    ...
  }
  ...
}

LENGTH(memory)

이름이 memory인 memory region의 length를 return

LOADADDR(section)

이름 section의 absolute LMA를 retrun

LOG2CEIL(exp)

무한 쪽으로 올림된 exp의 밑이 2인 log.  LOG2CEIL(0)는 0을 return

MAX(exp1exp2)

exp1exp2 중 더 큰 걸 return

MIN(exp1exp2)

exp1exp2 중 더 작은 걸 return

NEXT(exp)

exp의 multiple인 다음 unallocated address를 return. 이 함수는 ALIGN(exp)와 매우 관련이 깊다; output file의 discontinuous memory를 define하는데 MEMORY command를 사용하기 전까지, 두 functions은 동등하다.

ORIGIN(memory)

이름이 memory인 memory region의 origin을 return

SEGMENT_START(segmentdefault)

이름이 segment인 것의 base address를 return. 이 segment에 명시적인 값이 (command-line '-T' option으로) 주어지지 않았다면 그 값이 return되고, 아니면 그 값은 default가 될거다. 현재, '-T' command-line option은 “text”, “data”, 그리고 “bss” section의 base address를 set하는데만 사용될 수 있지만, 어떤 segment name이든 SEGMENT_START를 사용할 수 있다.

SIZEOF(section)

그 섹션이 allocated되었다면, section이 이름인 것의 bytes로 된 size를 return. 그 section이 evaluate될 때 allocated되지 않았다면, linker는 error를 report할 거다. 다음 예제에서, symbol_1symbol_2는 같은 값이 assigned된다.

SECTIONS{ …
  .output {
    .start = . ;
    …
    .end = . ;
    }
  symbol_1 = .end - .start ;
  symbol_2 = SIZEOF(.output);
… }

SIZEOF_HEADERS

sizeof_headers

output file의 header의 bytes로 된 size를 return. 이는 output file의 시작에서 나오는 information이다. paging이 가능하도록 골랐다면, 첫번째 section의 start address를 setting할 때 이 수를 사용할 수 있다.

ELF output file을 생성할 때 linker script가 SIZEOF_HEADERS builtin function을 사용하면, linker는 모든 section addresses와 sizes를 결정하기 전에 program headers의 수를 계산해야 한다. linker가 additional program headers가 필요함을 발견하면, error ‘not enough room for program headers’를 report할 거다. 이 에러를 피하려면, SIZEOF_HEADERS function의 사용을 피하거나, linker가 additional program headers를 강제로 사용하는 것을 피하도록 linker script를 재작성하거나, PHDRS command를 사용해서 (PHDRS를 보라) program headers 자체를 define해야 한다.

Implicit Linker Scripts

linker script로 linker가 인식할 수 없는 object file이나 archive file을 지정하면, linker script로 그 파일을 읽으려고 시도할거다. 그 file이 linker script로 parsed될 수 없으면, linker는 error를 report할 거다.

Implicit linker script는 default linker script를 대신하지 않는다.

일반적으로 implicit linker script는 symbol assignment, 또는 INPUT, GROUP이나 VERSION commands만 포함할거다.

implicit linker script는 implicit linker script가 read된 command line의 위치에서 read되므로 어떤 input files이든 읽는다. 이는 archive searching에 영향을 준다.