요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
Assembler Annotations
=====================
Copyright (c) 2017-2019 Jiri Slaby
This document describes the new macros for annotation of data and code in
assembly. In particular, it contains information about ``SYM_FUNC_START``,
``SYM_FUNC_END``, ``SYM_CODE_START``, and similar.
Rationale
---------
Some code like entries, trampolines, or boot code needs to be written in
assembly. The same as in C, such code is grouped into functions and
accompanied with data. Standard assemblers do not force users into precisely
marking these pieces as code, data, or even specifying their length.
Nevertheless, assemblers provide developers with such annotations to aid
debuggers throughout assembly. On top of that, developers also want to mark
some functions as *global* in order to be visible outside of their translation
units.
Over time, the Linux kernel has adopted macros from various projects (like
``binutils``) to facilitate such annotations. So for historic reasons,
developers have been using ``ENTRY``, ``END``, ``ENDPROC``, and other
annotations in assembly. Due to the lack of their documentation, the macros
are used in rather wrong contexts at some locations. Clearly, ``ENTRY`` was
intended to denote the beginning of global symbols (be it data or code).
``END`` used to mark the end of data or end of special functions with
*non-standard* calling convention. In contrast, ``ENDPROC`` should annotate
only ends of *standard* functions.
When these macros are used correctly, they help assemblers generate a nice
object with both sizes and types set correctly. For example, the result of
``arch/x86/lib/putuser.S``::
Num: Value Size Type Bind Vis Ndx Name
25: 0000000000000000 33 FUNC GLOBAL DEFAULT 1 __put_user_1
29: 0000000000000030 37 FUNC GLOBAL DEFAULT 1 __put_user_2
32: 0000000000000060 36 FUNC GLOBAL DEFAULT 1 __put_user_4
35: 0000000000000090 37 FUNC GLOBAL DEFAULT 1 __put_user_8
This is not only important for debugging purposes. When there are properly
annotated objects like this, tools can be run on them to generate more useful
information. In particular, on properly annotated objects, ``objtool`` can be
run to check and fix the object if needed. Currently, ``objtool`` can report
missing frame pointer setup/destruction in functions. It can also
automatically generate annotations for the ORC unwinder
(Documentation/arch/x86/orc-unwinder.rst)
for most code. Both of these are especially important to support reliable
stack traces which are in turn necessary for kernel live patching
(Documentation/livepatch/livepatch.rst).
Caveat and Discussion
---------------------
As one might realize, there were only three macros previously. That is indeed
insufficient to cover all the combinations of cases:
* standard/non-standard function
* code/data
* global/local symbol
There was a discussion_ and instead of extending the current ``ENTRY/END*``
macros, it was decided that brand new macros should be introduced instead::
So how about using macro names that actually show the purpose, instead
of importing all the crappy, historic, essentially randomly chosen
debug symbol macro names from the binutils and older kernels?
.. _discussion: https://lore.kernel.org/r/[email protected]
Macros Description
------------------
The new macros are prefixed with the ``SYM_`` prefix and can be divided into
three main groups:
1. ``SYM_FUNC_*`` -- to annotate C-like functions. This means functions with
standard C calling conventions. For example, on x86, this means that the
stack contains a return address at the predefined place and a return from
the function can happen in a standard way. When frame pointers are enabled,
save/restore of frame pointer shall happen at the start/end of a function,
respectively, too.
Checking tools like ``objtool`` should ensure such marked functions conform
to these rules. The tools can also easily annotate these functions with
debugging information (like *ORC data*) automatically.
2. ``SYM_CODE_*`` -- special functions called with special stack. Be it
interrupt handlers with special stack content, trampolines, or startup
functions.
Checking tools mostly ignore checking of these functions. But some debug
information still can be generated automatically. For correct debug data,
this code needs hints like ``UNWIND_HINT_REGS`` provided by developers.
3. ``SYM_DATA*`` -- obviously data belonging to ``.data`` sections and not to
``.text``. Data do not contain instructions, so they have to be treated
specially by the tools: they should not treat the bytes as instructions,
nor assign any debug information to them.
Instruction Macros
~~~~~~~~~~~~~~~~~~
This section covers ``SYM_FUNC_*`` and ``SYM_CODE_*`` enumerated above.
``objtool`` requires that all code must be contained in an ELF symbol. Symbol
names that have a ``.L`` prefix do not emit symbol table entries. ``.L``
prefixed symbols can be used within a code region, but should be avoided for
denoting a range of code via ``SYM_*_START/END`` annotations.
* ``SYM_FUNC_START`` and ``SYM_FUNC_START_LOCAL`` are supposed to be **the
most frequent markings**. They are used for functions with standard calling
conventions -- global and local. Like in C, they both align the functions to
architecture specific ``__ALIGN`` bytes. There are also ``_NOALIGN`` variants
for special cases where developers do not want this implicit alignment.
``SYM_FUNC_START_WEAK`` and ``SYM_FUNC_START_WEAK_NOALIGN`` markings are
also offered as an assembler counterpart to the *weak* attribute known from
C.
All of these **shall** be coupled with ``SYM_FUNC_END``. First, it marks
the sequence of instructions as a function and computes its size to the
generated object file. Second, it also eases checking and processing such
object files as the tools can trivially find exact function boundaries.
So in most cases, developers should write something like in the following
example, having some asm instructions in between the macros, of course::
SYM_FUNC_START(memset)
... asm insns ...
SYM_FUNC_END(memset)
In fact, this kind of annotation corresponds to the now deprecated ``ENTRY``
and ``ENDPROC`` macros.
* ``SYM_FUNC_ALIAS``, ``SYM_FUNC_ALIAS_LOCAL``, and ``SYM_FUNC_ALIAS_WEAK`` can
be used to define multiple names for a function. The typical use is::
SYM_FUNC_START(__memset)
... asm insns ...
SYN_FUNC_END(__memset)
SYM_FUNC_ALIAS(memset, __memset)
In this example, one can call ``__memset`` or ``memset`` with the same
result, except the debug information for the instructions is generated to
the object file only once -- for the non-``ALIAS`` case.
* ``SYM_CODE_START`` and ``SYM_CODE_START_LOCAL`` should be used only in
special cases -- if you know what you are doing. This is used exclusively
for interrupt handlers and similar where the calling convention is not the C
one. ``_NOALIGN`` variants exist too. The use is the same as for the ``FUNC``
category above::
SYM_CODE_START_LOCAL(bad_put_user)
... asm insns ...
SYM_CODE_END(bad_put_user)
Again, every ``SYM_CODE_START*`` **shall** be coupled by ``SYM_CODE_END``.
To some extent, this category corresponds to deprecated ``ENTRY`` and
``END``. Except ``END`` had several other meanings too.
* ``SYM_INNER_LABEL*`` is used to denote a label inside some
``SYM_{CODE,FUNC}_START`` and ``SYM_{CODE,FUNC}_END``. They are very similar
to C labels, except they can be made global. An example of use::
SYM_CODE_START(ftrace_caller)
/* save_mcount_regs fills in first two parameters */
...
SYM_INNER_LABEL(ftrace_caller_op_ptr, SYM_L_GLOBAL)
/* Load the ftrace_ops into the 3rd parameter */
...
SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
call ftrace_stub
...
retq
SYM_CODE_END(ftrace_caller)
Data Macros
~~~~~~~~~~~
Similar to instructions, there is a couple of macros to describe data in the
assembly.
* ``SYM_DATA_START`` and ``SYM_DATA_START_LOCAL`` mark the start of some data
and shall be used in conjunction with either ``SYM_DATA_END``, or
``SYM_DATA_END_LABEL``. The latter adds also a label to the end, so that
people can use ``lstack`` and (local) ``lstack_end`` in the following
example::
SYM_DATA_START_LOCAL(lstack)
.skip 4096
SYM_DATA_END_LABEL(lstack, SYM_L_LOCAL, lstack_end)
* ``SYM_DATA`` and ``SYM_DATA_LOCAL`` are variants for simple, mostly one-line
data::
SYM_DATA(HEAP, .long rm_heap)
SYM_DATA(heap_end, .long rm_stack)
In the end, they expand to ``SYM_DATA_START`` with ``SYM_DATA_END``
internally.
Support Macros
~~~~~~~~~~~~~~
All the above reduce themselves to some invocation of ``SYM_START``,
``SYM_END``, or ``SYM_ENTRY`` at last. Normally, developers should avoid using
these.
Further, in the above examples, one could see ``SYM_L_LOCAL``. There are also
``SYM_L_GLOBAL`` and ``SYM_L_WEAK``. All are intended to denote linkage of a
symbol marked by them. They are used either in ``_LABEL`` variants of the
earlier macros, or in ``SYM_START``.
Overriding Macros
~~~~~~~~~~~~~~~~~
Architecture can also override any of the macros in their own
``asm/linkage.h``, including macros specifying the type of a symbol
(``SYM_T_FUNC``, ``SYM_T_OBJECT``, and ``SYM_T_NONE``). As every macro
described in this file is surrounded by ``#ifdef`` + ``#endif``, it is enough
to define the macros differently in the aforementioned architecture-dependent
header.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Assembler annotation macro 개요
1-9`Assembler Annotations` 문서는 Jiri Slaby가 2017-2019년에 저작했으며 assembly의 data와 code를 표시하는 새 macro를 설명합니다.
중심 대상은 `SYM_FUNC_START`, `SYM_FUNC_END`, `SYM_CODE_START`와 그와 비슷한 annotation macro입니다.
정확한 code·data symbol annotation이 필요한 이유
10-50entry, trampoline, boot code 같은 일부 code는 assembly로 작성해야 합니다. C와 마찬가지로 function과 data로 구성되지만 표준 assembler는 각 조각을 code 또는 data로 정확히 표시하거나 길이를 지정하도록 강제하지 않습니다.
assembler annotation은 debugger가 assembly를 이해하도록 돕고, translation unit 밖에서 보여야 하는 function을 global symbol로 표시할 수 있게 합니다.
Linux kernel은 역사적으로 `binutils` 같은 여러 project의 macro를 받아들여 `ENTRY`, `END`, `ENDPROC` 등을 사용했습니다. 문서가 부족해 잘못된 context에 쓰인 경우도 있습니다. `ENTRY`는 data든 code든 global symbol 시작, `END`는 data 또는 비표준 calling convention function의 끝, `ENDPROC`는 표준 function의 끝을 나타내는 의도였습니다.
올바른 annotation을 사용하면 assembler가 size와 type이 정확한 object를 생성합니다. `arch/x86/lib/putuser.S` 결과의 symbol table은 다음과 같습니다.
Num: Value Size Type Bind Vis Ndx Name
25: 0000000000000000 33 FUNC GLOBAL DEFAULT 1 __put_user_1
29: 0000000000000030 37 FUNC GLOBAL DEFAULT 1 __put_user_2
32: 0000000000000060 36 FUNC GLOBAL DEFAULT 1 __put_user_4
35: 0000000000000090 37 FUNC GLOBAL DEFAULT 1 __put_user_8
이는 debugging 외에도 중요합니다. 정확히 annotation된 object에는 `objtool`을 실행해 검사하고 필요하면 수정할 수 있습니다. 현재 `objtool`은 function에서 frame pointer 설정·해제 누락을 보고하고 대부분의 code에 ORC unwinder용 annotation을 자동 생성합니다.
이 정보는 신뢰할 수 있는 stack trace를 지원하며, 이는 다시 kernel live patching에 필요합니다. 관련 문서는 `Documentation/arch/x86/orc-unwinder.rst`와 `Documentation/livepatch/livepatch.rst`입니다.
기존 macro의 한계와 새 이름
51-69기존 macro 세 개만으로는 standard/non-standard function, code/data, global/local symbol의 모든 조합을 표현할 수 없습니다.
- standard function과 non-standard function
- code와 data
- global symbol과 local symbol
논의 결과 기존 `ENTRY/END*`를 확장하는 대신 목적이 이름에 드러나는 완전히 새로운 macro를 도입하기로 했습니다.
So how about using macro names that actually show the purpose, instead
of importing all the crappy, historic, essentially randomly chosen
debug symbol macro names from the binutils and older kernels?
논의 thread는 https://lore.kernel.org/r/[email protected] 에서 확인할 수 있습니다.
SYM_ macro의 세 그룹
70-99새 macro는 `SYM_` prefix를 사용하며 세 그룹으로 나뉩니다.
| 그룹 | 대상 | tool 처리 |
|---|---|---|
| SYM_FUNC_* | 표준 C calling convention function | objtool이 규약을 검사하고 ORC data 같은 debug 정보를 자동 생성 |
| SYM_CODE_* | interrupt handler, trampoline, startup처럼 특수 stack과 calling convention을 쓰는 code | 대부분 검사를 생략하되 `UNWIND_HINT_REGS` 같은 hint로 debug data 생성 |
| SYM_DATA* | `.text`가 아닌 `.data` section의 data | byte를 instruction으로 보거나 debug 정보를 붙이지 않음 |
x86의 `SYM_FUNC_*`는 정해진 위치에 return address가 있고 표준 방식으로 return할 수 있어야 합니다. frame pointer가 활성화되어 있으면 function 시작과 끝에서 각각 저장하고 복원해야 합니다.
`SYM_CODE_*`는 특수 stack content를 가진 interrupt handler, trampoline, startup function에 사용합니다. 정확한 debug data를 위해 개발자가 `UNWIND_HINT_REGS` 같은 hint를 제공해야 할 수 있습니다.
`SYM_DATA*`는 instruction이 없는 data를 명시하므로 tool이 해당 byte를 code로 해석하거나 debug annotation을 부여해서는 안 됩니다.
SYM_FUNC_START, END와 alias
100-145`objtool`은 모든 code가 ELF symbol 안에 있어야 한다고 요구합니다. `.L` prefix symbol은 symbol table entry를 만들지 않으므로 code region 내부 label에는 쓸 수 있지만 `SYM_*_START/END`로 code 범위를 나타내는 데는 피해야 합니다.
`SYM_FUNC_START`와 `SYM_FUNC_START_LOCAL`은 가장 흔한 표시이며 각각 global·local 표준 calling convention function에 사용합니다. C와 마찬가지로 architecture-specific `__ALIGN` byte에 맞추며 implicit alignment가 필요 없는 특수 경우에는 `_NOALIGN` variant를 사용합니다.
`SYM_FUNC_START_WEAK`와 `SYM_FUNC_START_WEAK_NOALIGN`은 C의 weak attribute에 대응합니다.
모든 start macro는 `SYM_FUNC_END`와 짝지어야 합니다. instruction sequence를 function으로 표시하고 object file에 size를 계산하며, tool이 정확한 function boundary를 쉽게 찾아 검사·처리할 수 있게 합니다.
SYM_FUNC_START(memset)
... asm insns ...
SYM_FUNC_END(memset)
이 annotation 조합은 이제 deprecated인 `ENTRY`와 `ENDPROC`에 대응합니다.
`SYM_FUNC_ALIAS`, `SYM_FUNC_ALIAS_LOCAL`, `SYM_FUNC_ALIAS_WEAK`는 function에 여러 이름을 정의할 때 사용합니다.
SYM_FUNC_START(__memset)
... asm insns ...
SYN_FUNC_END(__memset)
SYM_FUNC_ALIAS(memset, __memset)
예에서는 `__memset`과 `memset` 어느 이름으로 호출해도 결과가 같지만 instruction의 debug information은 non-ALIAS인 `__memset`에 대해 한 번만 object file에 생성됩니다.
특수 code와 inner label
146-178`SYM_CODE_START`와 `SYM_CODE_START_LOCAL`은 C calling convention이 아닌 interrupt handler 같은 특수 경우에만 사용해야 합니다. `_NOALIGN` variant도 있으며 FUNC category와 같은 방식으로 씁니다.
SYM_CODE_START_LOCAL(bad_put_user)
... asm insns ...
SYM_CODE_END(bad_put_user)
모든 `SYM_CODE_START*`는 반드시 `SYM_CODE_END`와 짝지어야 합니다. 어느 정도 deprecated `ENTRY`와 `END`에 대응하지만, 과거 `END`에는 다른 의미도 있었습니다.
`SYM_INNER_LABEL*`은 `SYM_{CODE,FUNC}_START`와 `SYM_{CODE,FUNC}_END` 사이의 label을 표시합니다. C label과 비슷하지만 global로 만들 수 있습니다.
SYM_CODE_START(ftrace_caller)
/* save_mcount_regs fills in first two parameters */
...
SYM_INNER_LABEL(ftrace_caller_op_ptr, SYM_L_GLOBAL)
/* Load the ftrace_ops into the 3rd parameter */
...
SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
call ftrace_stub
...
retq
SYM_CODE_END(ftrace_caller)
예에서 `ftrace_caller_op_ptr`와 `ftrace_call`은 `SYM_L_GLOBAL` linkage를 가진 내부 label입니다.
assembly data macro
179-202`SYM_DATA_START`와 `SYM_DATA_START_LOCAL`은 data 시작을 표시하며 `SYM_DATA_END` 또는 끝 label까지 추가하는 `SYM_DATA_END_LABEL`과 함께 사용해야 합니다.
SYM_DATA_START_LOCAL(lstack)
.skip 4096
SYM_DATA_END_LABEL(lstack, SYM_L_LOCAL, lstack_end)
위 예에서는 `lstack`과 local `lstack_end`를 모두 참조할 수 있습니다.
`SYM_DATA`와 `SYM_DATA_LOCAL`은 주로 한 줄짜리 단순 data를 위한 variant입니다.
SYM_DATA(HEAP, .long rm_heap)
SYM_DATA(heap_end, .long rm_stack)
이 macro는 내부적으로 `SYM_DATA_START`와 `SYM_DATA_END` 조합으로 확장됩니다.
support macro, linkage와 architecture override
203-222앞의 모든 macro는 최종적으로 `SYM_START`, `SYM_END`, `SYM_ENTRY` 호출로 축약됩니다. 일반 개발자는 이 저수준 macro를 직접 사용하지 않는 편이 좋습니다.
`SYM_L_LOCAL`, `SYM_L_GLOBAL`, `SYM_L_WEAK`는 symbol linkage를 나타내며 앞선 macro의 `_LABEL` variant 또는 `SYM_START`에서 사용합니다.
architecture는 자체 `asm/linkage.h`에서 어떤 macro든 override할 수 있으며 symbol type을 지정하는 `SYM_T_FUNC`, `SYM_T_OBJECT`, `SYM_T_NONE`도 포함됩니다. 이 문서의 모든 macro는 `#ifdef`와 `#endif`로 둘러싸여 있으므로 architecture-dependent header에서 다른 정의를 제공하면 충분합니다.
요약과 해설
asm-annotations.rst:1-222`SYM_FUNC_*`, `SYM_CODE_*`, `SYM_DATA*`는 표준 function, 특수 calling convention code, data를 구분해 ELF symbol type과 size를 정확히 만듭니다.
정확한 annotation은 debugger뿐 아니라 `objtool`, ORC unwinder, 신뢰 가능한 stack trace와 kernel live patching의 기반이 됩니다.
각 START macro는 대응 END macro와 짝지어야 하고, architecture는 `asm/linkage.h`에서 기본 macro와 symbol type을 override할 수 있습니다.