当前位置: 首页 > news >正文

GNU链接器简介-2

GNU链接器简介-2

  • 1 简单链接器脚本命令
    • 1.1 Setting the Entry Point
    • 1.2 Commands Dealing with Files
    • 2.3 Commands Dealing with Object File Formats
    • 2.4 Assign alias names to memory regions
    • 2.5 Other Linker Script Commands
  • 3 5Assigning Values to Symbols
    • 3.1 Simple Assignments
    • 3.2 HIDDEN
    • 3.3 PROVIDE
    • 3.4 PROVIDE_HIDDEN
    • 3.5 Source Code Reference

该篇文章是基于对 The GNU linker的翻译,并添加了部分自己的理解。

1 简单链接器脚本命令

In this section we describe the simple linker script commands.
在本节中,我们描述了简单的链接器脚本命令。

  • Setting the Entry Point
  • 设置入口点
  • Commands Dealing with Files
  • 文件处理命令
  • Commands Dealing with Object File Formats
  • 处理对象文件格式的命令
  • Assign alias names to memory regions
  • 为内存区域分配别名
  • Other Linker Script Commands
  • 其他链接脚本命令

1.1 Setting the Entry Point

The first instruction to execute in a program is called the entry point. You can use the ENTRY linker script command to set the entry point. The argument is a symbol name:
程序中要执行的第一个指令被称为入口点。你可以使用ENTRY链接脚本命令来设置入口点。参数是一个符号名称:

ENTRY(symbol)

There are several ways to set the entry point. The linker will set the entry point by trying each of the following methods in order, and stopping when one of them succeeds:
有几种方法可以设置入口点。链接器将按顺序尝试以下每种方法来设置入口点,并在其中一种方法成功时停止:

  • the ‘-e’ entry command-line option;
  • “-e”命令行选项;
  • the ENTRY(symbol) command in a linker script;
  • 链接脚本中的ENTRY(symbol)命令;
  • the value of a target-specific symbol, if it is defined; For many targets this is start, but PE- and BeOS-based systems for example check a list of possible entry symbols, matching the first one found.
  • 目标特定符号的值,如果已定义;对于许多目标来说这是start符号,但是基于PE-和BeOS的系统会检查一系列可能的入口符号列表,匹配到的第一个符号。
  • the address of the first byte of the code section, if present and an executable is being created - the code section is usually ‘.text’, but can be something else;
  • 代码段的第一个字节的地址,如果存在并且可执行属性被创建,这个代码段一般是‘.text’,但是也有可能是其他的部分。
  • The address 0.
  • 地址0

1.2 Commands Dealing with Files

Several linker script commands deal with files.
有几条链接器脚本命令与文件有关。

  • INCLUDE filename
    Include the linker script filename at this point. The file will be searched for in the current directory, and in any directory specified with the -L option. You can nest calls to INCLUDE up to 10 levels deep.
    在此处包含链接器脚本文件名。该文件将在当前目录以及使用‘-L’选项指定的任何目录中搜索。你可以嵌套调用INCLUDE,最多可达10层深度。

You can place INCLUDE directives at the top level, in MEMORY or SECTIONS commands, or in output section descriptions.
你可以把INCLUDE放在MEMORY或SECTIONS命令中的顶层、,或在输出部分描述中放置INCLUDE指令。

  • INPUT(file, file, …)
  • INPUT(file file …)

The INPUT command directs the linker to include the named files in the link, as though they were named on the command line.
INPUT命令指示链接器将指定的文件包含在链接中,就好像它们在命令行上被命名了一样。
For example, if you always want to include subr.o any time you do a link, but you can’t be bothered to put it on every link command line, then you can put ‘INPUT (subr.o)’ in your linker script.
例如,如果你总是希望在进行链接时包含‘subr.o’,但你又不想每次都在链接命令行中添加它,那么你可以在链接器脚本中加入‘INPUT (subr.o)’
In fact, if you like, you can list all of your input files in the linker script, and then invoke the linker with nothing but a ‘-T’ option.
实际上,如果你愿意,你可以在链接器脚本中列出你所有的输入文件,然后仅使用一个‘-T’选项来调用链接器。
In case a sysroot prefix is configured, and the filename starts with the ‘/’ character, and the script being processed was located inside the sysroot prefix, the filename will be looked for in the sysroot prefix. The sysroot prefix can also be forced by specifying = as the first character in the filename path, or prefixing the filename path with $SYSROOT.
如果配置了sysroot前缀,并且文件名以‘/’字符开头,且正在处理的脚本位于sysroot前缀内,那么链接器将在sysroot前缀中查找该文件名。否则,链接器将在当前目录中尝试打开文件。如果未找到,链接器将通过归档库搜索路径进行搜索。也可以通过在文件名路径的开头指定=字符,或者在文件名路径前加上$SYSROOT来强制使用sysroot前缀。
If a sysroot prefix is not used then the linker will try to open the file in the directory containing the linker script. If it is not found the linker will then search the current directory. If it is still not found the linker will search through the archive library search path.
如果sysroot前缀没有使用,那么链接器将会尝试去打开链接器脚本所在的目录。如果没有发现,那么链接器将会搜索当前的目录。如果仍未找到,链接器将通过归档文件库搜索路径进行搜索。

If you use ‘INPUT (-lfile)’, ld will transform the name to libfile.a, as with the command-line argument ‘-l’.
如果你使用 'INPUT (-lfile)',ld 将会像处理命令行参数 '-l' 一样,将名称转换为 libfile.a
When you use the INPUT command in an implicit linker script, the files will be included in the link at the point at which the linker script file is included. This can affect archive searching.
当你在隐式链接脚本中使用INPUT命令时,文件将在链接器脚本文件被包含的那一点被包含在链接过程中。这可能会影响归档搜索。

  • GROUP(file, file, …)
  • GROUP(file file …)

The GROUP command is like INPUT, except that the named files should all be archives, and they are searched repeatedly until no new undefined references are created.
GROUP命令类似于INPUT命令,不同之处在于指定的文件应该是归档文件,并且会反复搜索这些文件,直到不再产生新的未定义引用为止。

  • AS_NEEDED(file, file, …)
  • AS_NEEDED(file file …)

This construct can appear only inside of the INPUT or GROUP commands, among other filenames. The files listed will be handled as if they appear directly in the INPUT or GROUP commands, with the exception of ELF shared libraries, that will be added only when they are actually needed. This construct essentially enables --as-needed option for all the files listed inside of it and restores previous --as-needed resp. --no-as-needed setting afterwards.
这个构造只能出现在INPUTGROUP命令中,以及其他文件名中。列出的文件将被处理,就像它们直接出现在INPUT或GROUP命令中一样,除了ELF共享库,只有在实际需要时才会添加。这个构造基本上为其中列出的所有文件启用了‘--as-needed’选项,并在之后恢复之前的‘--as-needed’‘--no-as-needed’设置。

  • OUTPUT(filename)

The OUTPUT command names the output file. Using OUTPUT(filename) in the linker script is exactly like using ‘-o filename’ on the command line (see Command Line Options). If both are used, the command-line option takes precedence.
OUTPUT 命令为输出文件命名。在链接器脚本中使用 OUTPUT(filename) 与在命令行中使用“-o filename ”完全相同(请参阅命令行选项)。如果两者同时使用,命令行选项优先。
You can use the OUTPUT command to define a default name for the output file other than the usual default of a.out.
您可以使用OUTPUT命令来定义一个默认的输出文件名,而不是通常的默认文件名‘a.out’。

  • SEARCH_DIR(path)

The SEARCH_DIR command adds path to the list of paths where ld looks for archive libraries. Using SEARCH_DIR(path) is exactly like using ‘-L path’ on the command line. If both are used, then the linker will search both paths. Paths specified using the command-line option are searched first.
SEARCH_DIR命令将路径添加到ld查找存档库的路径列表中。使用SEARCH_DIR(path)与在命令行上使用‘-L path’完全相同。如果两者都使用,链接器将搜索这两个路径。使用命令行选项指定的路径首先被搜索。

  • STARTUP(filename)

The STARTUP command is just like the INPUT command, except that filename will become the first input file to be linked, as though it were specified first on the command line. This may be useful when using a system in which the entry point is always the start of the first file.
STARTUP命令与INPUT命令类似,不同之处在于filename将成为第一个链接的输入文件,就好像它在命令行中首先被指定一样。当系统的入口点总是第一个文件的起点时,这条命令可能很有用。

2.3 Commands Dealing with Object File Formats

A couple of linker script commands deal with object file formats.
几个链接器脚本命令处理对象文件格式。

  • OUTPUT_FORMAT(bfdname)
  • OUTPUT_FORMAT(default, big, little)

The OUTPUT_FORMAT command names the BFD format to use for the output file. Using OUTPUT_FORMAT(bfdname) is exactly like using ‘–oformat bfdname’ on the command line. If both are used, the command line option takes precedence.
OUTPUT_FORMAT 命令命名输出文件要使用的 BFD 格式。使用 OUTPUT_FORMAT(bfdname)与在命令行中使用“--oformat bfdname ”完全相同。如果同时使用这两个选项,则以命令行选项为准。

You can use OUTPUT_FORMAT with three arguments to use different formats based on the ‘-EB’ and ‘-EL’ command-line options. This permits the linker script to set the output format based on the desired endianness.
您可以使用带有三个参数的 OUTPUT_FORMAT,根据“-EB ”“-EL ”命令行选项使用不同的格式。这允许链接器脚本根据所需的字节序设置输出格式。
If neither ‘-EB’ nor ‘-EL’ are used, then the output format will be the first argument, default. If ‘-EB’ is used, the output format will be the second argument, big. If ‘-EL’ is used, the output format will be the third argument, little.
如果既未使用“-EB ”也未使用“-EL”,则输出格式将是第一个参数,即默认值。如果使用“-EB”,输出格式将是第二个参数 big。如果使用“-EL”,输出格式将是第三个参数,即 little
For example, the default linker script for the MIPS ELF target uses this command:
例如,MIPS ELF 目标机的默认链接器脚本就使用了该命令:

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

This says that the default format for the output file is ‘elf32-bigmips’, but if the user uses the ‘-EL’ command-line option, the output file will be created in the ‘elf32-littlemips’ format.
这表示输出文件的默认格式是 “elf32-bigmips”,但如果用户使用“-EL ”命令行选项,输出文件将以 “elf32-littlemips ”格式创建。

  • TARGET(bfdname)

The TARGET command names the BFD format to use when reading input files. It affects subsequent INPUT and GROUP commands. This command is like using ‘-b bfdname’ on the command line. If the TARGET command is used but OUTPUT_FORMAT is not, then the last TARGET command is also used to set the format for the output file.
TARGET 命令用于命名读取输入文件时使用的 BFD 格式。它会影响后续的 INPUTGROUP 命令。该命令类似于在命令行中使用“-b bfdname”。如果使用了 TARGET 命令,但没有使用 OUTPUT_FORMAT,那么最后一条 TARGET 命令也将用于设置输出文件的格式。

2.4 Assign alias names to memory regions

Alias names can be added to existing memory regions created with the MEMORY Command command. Each name corresponds to at most one memory region.
别名可以添加到使用 MEMORY 命令创建的现有内存区域中。每个名称最多对应一个内存区域。

REGION_ALIAS(alias, region)

The REGION_ALIAS function creates an alias name alias for the memory region region. This allows a flexible mapping of output sections to memory regions. An example follows.
REGION_ALIAS函数为内存区域region创建一个别名alias。这允许灵活地将输出段映射到内存区域。下面是一个例子。
Suppose we have an application for embedded systems which come with various memory storage devices. All have a general purpose, volatile memory RAM that allows code execution or data storage. Some may have a read-only, non-volatile memory ROM that allows code execution and read-only data access. The last variant is a read-only, non-volatile memory ROM2 with read-only data access and no code execution capability. We have four output sections:
假设我们有一个嵌入式系统的应用程序,它配备了各种存储设备。所有设备都有一般用途的易失性内存RAM,允许代码执行或数据存储。一些设备可能具有只读的非易失性内存ROM,允许代码执行和只读数据访问。最后一种变体是一种只读的非易失性内存ROM2,它具有只读数据访问功能,但不允许代码执行。我们有四个输出部分:

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

The goal is to provide a linker command file that contains a system independent part defining the output sections and a system dependent part mapping the output sections to the memory regions available on the system. Our embedded systems come with three different memory setups A, B and C:
目标是提供一个链接器命令文件,其中包含一个系统独立部分定义输出段,以及一个系统依赖部分将输出段映射到系统上可用的内存区域。我们的嵌入式系统有三种不同的内存配置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

The notation RAM/ROM or RAM/ROM2 means that this section is loaded into region ROM or ROM2 respectively. Please note that the load address of the .data section starts in all three variants at the end of the .rodata section.
标记RAM/ROM或RAM/ROM2意味着这一部分分别被加载到ROM或ROM2区域。请注意,.data段的加载地址在所有三种变体中都始于.rodata段的末端。
The base linker script that deals with the output sections follows. It includes the system dependent linkcmds.memory file that describes the memory layout:
处理输出段的基链接脚本如下。它包括描述内存布局的系统依赖的linkcmds.memory文件:

INCLUDE linkcmds.memorySECTIONS{.text :{*(.text)} > REGION_TEXT.rodata :{*(.rodata)rodata_end = .;} > REGION_RODATA.data : AT (rodata_end){data_start = .;*(.data)} > REGION_DATAdata_size = SIZEOF(.data);data_load_start = LOADADDR(.data);.bss :{*(.bss)} > REGION_BSS}

Now we need three different linkcmds.memory files to define memory regions and alias names. The content of linkcmds.memory for the three variants A, B and C:
现在我们需要三个不同的linkcmds.memory文件来定义内存区域和别名名称。三个变体A、B和C的linkcmds.memory内容如下:

  • A

Here everything goes into the 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 and read-only data go into the ROM. Read-write data goes into the RAM. An image of the initialized data is loaded into the ROM and will be copied during system start into the RAM.
程序代码和只读数据存放在ROM中。可读写数据则存放在RAM中。初始化数据的镜像会被加载到ROM中,并在系统启动时复制到RAM中。

MEMORY{ROM : ORIGIN = 0, LENGTH = 3MRAM : 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 goes into the ROM. Read-only data goes into the ROM2. Read-write data goes into the RAM. An image of the initialized data is loaded into the ROM2 and will be copied during system start into the RAM.
程序代码存入ROM。只读数据存入ROM2。读写数据存入RAM。初始化数据的镜像被加载到ROM2中,并将在系统启动时复制到RAM中。

MEMORY{ROM : ORIGIN = 0, LENGTH = 2MROM2 : ORIGIN = 0x10000000, LENGTH = 1MRAM : ORIGIN = 0x20000000, LENGTH = 1M}REGION_ALIAS("REGION_TEXT", ROM);
REGION_ALIAS("REGION_RODATA", ROM2);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);

It is possible to write a common system initialization routine to copy the .data section from ROM or ROM2 into the RAM if necessary:
有可能编写一个通用的系统初始化例程,在必要时将.data段从ROM或ROM2复制到RAM中:

#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);}
}

2.5 Other Linker Script Commands

There are a few other linker scripts commands.

  • ASSERT(exp, message)

Ensure that exp is non-zero. If it is zero, then exit the linker with an error code, and print message.

Note that assertions are checked before the final stages of linking take place. This means that expressions involving symbols PROVIDEd inside section definitions will fail if the user has not set values for those symbols. The only exception to this rule is PROVIDEd symbols that just reference dot. Thus an assertion like this:

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

will fail if __stack_size is not defined elsewhere. Symbols PROVIDEd outside of section definitions are evaluated earlier, so they can be used inside ASSERTions. Thus:

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

will work.

  • EXTERN(symbol symbol …)

Force symbol to be entered in the output file as an undefined symbol. Doing this may, for example, trigger linking of additional modules from standard libraries. You may list several symbols for each EXTERN, and you may use EXTERN multiple times. This command has the same effect as the ‘-u’ command-line option.

  • FORCE_COMMON_ALLOCATION

This command has the same effect as the ‘-d’ command-line option: to make ld assign space to common symbols even if a relocatable output file is specified (‘-r’).

  • INHIBIT_COMMON_ALLOCATION

This command has the same effect as the ‘–no-define-common’ command-line option: to make ld omit the assignment of addresses to common symbols even for a non-relocatable output file.

  • FORCE_GROUP_ALLOCATION

This command has the same effect as the ‘–force-group-allocation’ command-line option: to make ld place section group members like normal input sections, and to delete the section groups even if a relocatable output file is specified (‘-r’).

  • INSERT [ AFTER | BEFORE ] output_section

This command is typically used in a script specified by ‘-T’ to augment the default SECTIONS with, for example, overlays. It inserts all prior linker script statements after (or before) output_section, and also causes ‘-T’ to not override the default linker script. The exact insertion point is as for orphan sections. See The Location Counter. The insertion happens after the linker has mapped input sections to output sections. Prior to the insertion, since ‘-T’ scripts are parsed before the default linker script, statements in the ‘-T’ script occur before the default linker script statements in the internal linker representation of the script. In particular, input section assignments will be made to ‘-T’ output sections before those in the default script. Here is an example of how a ‘-T’ script using INSERT might look:

SECTIONS
{OVERLAY :{.ov1 { ov1*(.text) }.ov2 { ov2*(.text) }}
}
INSERT AFTER .text;
  • NOCROSSREFS(section section …)

This command may be used to tell ld to issue an error about any references among certain output sections.

In certain types of programs, particularly on embedded systems when using overlays, when one section is loaded into memory, another section will not be. Any direct references between the two sections would be errors. For example, it would be an error if code in one section called a function defined in the other section.

The NOCROSSREFS command takes a list of output section names. If ld detects any cross references between the sections, it reports an error and returns a non-zero exit status. Note that the NOCROSSREFS command uses output section names, not input section names.

  • NOCROSSREFS_TO(tosection fromsection …)

This command may be used to tell ld to issue an error about any references to one section from a list of other sections.

The NOCROSSREFS command is useful when ensuring that two or more output sections are entirely independent but there are situations where a one-way dependency is needed. For example, in a multi-core application there may be shared code that can be called from each core but for safety must never call back.

The NOCROSSREFS_TO command takes a list of output section names. The first section can not be referenced from any of the other sections. If ld detects any references to the first section from any of the other sections, it reports an error and returns a non-zero exit status. Note that the NOCROSSREFS_TO command uses output section names, not input section names.

  • OUTPUT_ARCH(bfdarch)

Specify a particular output machine architecture. The argument is one of the names used by the BFD library (see BFD). You can see the architecture of an object file by using the objdump program with the ‘-f’ option.

  • LD_FEATURE(string)

This command may be used to modify ld behavior. If string is “SANE_EXPR” then absolute symbols and numbers in a script are simply treated as numbers everywhere. See The Section of an Expression.

3 5Assigning Values to Symbols

You may assign a value to a symbol in a linker script. This will define the symbol and place it into the symbol table with a global scope.

  • Simple Assignments
  • HIDDEN
  • PROVIDE
  • PROVIDE_HIDDEN
  • Source Code Reference

3.1 Simple Assignments

You may assign to a symbol using any of the C assignment operators:

symbol = expression ;
symbol += expression ;
symbol -= expression ;
symbol *= expression ;
symbol /= expression ;
symbol <<= expression ;
symbol >>= expression ;
symbol &= expression ;
symbol |= expression ;

The first case will define symbol to the value of expression. In the other cases, symbol must already be defined, and the value will be adjusted accordingly.

The special symbol name ‘.’ indicates the location counter. You may only use this within a SECTIONS command. See The Location Counter.

The semicolon after expression is required.

Expressions are defined below; see Expressions in Linker Scripts.

You may write symbol assignments as commands in their own right, or as statements within a SECTIONS command, or as part of an output section description in a SECTIONS command.

The section of the symbol will be set from the section of the expression; for more information, see The Section of an Expression.

Here is an example showing the three different places that symbol assignments may be used:

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

In this example, the symbol ‘floating_point’ will be defined as zero. The symbol ‘_etext’ will be defined as the address following the last ‘.text’ input section. The symbol ‘_bdata’ will be defined as the address following the ‘.text’ output section aligned upward to a 4 byte boundary.

3.2 HIDDEN

For ELF targeted ports, define a symbol that will be hidden and won’t be exported. The syntax is HIDDEN(symbol = expression).

Here is the example from Simple Assignments, rewritten to use HIDDEN:

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

In this case none of the three symbols will be visible outside this module.

3.3 PROVIDE

In some cases, it is desirable for a linker script to define a symbol only if it is referenced and is not defined by any object included in the link. For example, traditional linkers defined the symbol ‘etext’. However, ANSI C requires that the user be able to use ‘etext’ as a function name without encountering an error. The PROVIDE keyword may be used to define a symbol, such as ‘etext’, only if it is referenced but not defined. The syntax is PROVIDE(symbol = expression).

Here is an example of using PROVIDE to define ‘etext’:

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

In this example, if the program defines ‘_etext’ (with a leading underscore), the linker will give a multiple definition diagnostic. If, on the other hand, the program defines ‘etext’ (with no leading underscore), the linker will silently use the definition in the program. If the program references ‘etext’ but does not define it, the linker will use the definition in the linker script.

Note - the PROVIDE directive considers a common symbol to be defined, even though such a symbol could be combined with the symbol that the PROVIDE would create. This is particularly important when considering constructor and destructor list symbols such as ‘CTOR_LIST’ as these are often defined as common symbols.

3.4 PROVIDE_HIDDEN

Similar to PROVIDE. For ELF targeted ports, the symbol will be hidden and won’t be exported.

3.5 Source Code Reference

Accessing a linker script defined variable from source code is not intuitive. In particular a linker script symbol is not equivalent to a variable declaration in a high level language, it is instead a symbol that does not have a value.

Before going further, it is important to note that compilers often transform names in the source code into different names when they are stored in the symbol table. For example, Fortran compilers commonly prepend or append an underscore, and C++ performs extensive ‘name mangling’. Therefore there might be a discrepancy between the name of a variable as it is used in source code and the name of the same variable as it is defined in a linker script. For example in C a linker script variable might be referred to as:

  extern int foo;

But in the linker script it might be defined as:

  _foo = 1000;

In the remaining examples however it is assumed that no name transformation has taken place.

When a symbol is declared in a high level language such as C, two things happen. The first is that the compiler reserves enough space in the program’s memory to hold the value of the symbol. The second is that the compiler creates an entry in the program’s symbol table which holds the symbol’s address. ie the symbol table contains the address of the block of memory holding the symbol’s value. So for example the following C declaration, at file scope:

  int foo = 1000;

creates an entry called ‘foo’ in the symbol table. This entry holds the address of an ‘int’ sized block of memory where the number 1000 is initially stored.

When a program references a symbol the compiler generates code that first accesses the symbol table to find the address of the symbol’s memory block and then code to read the value from that memory block. So:

  foo = 1;

looks up the symbol ‘foo’ in the symbol table, gets the address associated with this symbol and then writes the value 1 into that address. Whereas:

  int * a = & foo;

looks up the symbol ‘foo’ in the symbol table, gets its address and then copies this address into the block of memory associated with the variable ‘a’.

Linker scripts symbol declarations, by contrast, create an entry in the symbol table but do not assign any memory to them. Thus they are an address without a value. So for example the linker script definition:

  foo = 1000;

creates an entry in the symbol table called ‘foo’ which holds the address of memory location 1000, but nothing special is stored at address 1000. This means that you cannot access the value of a linker script defined symbol - it has no value - all you can do is access the address of a linker script defined symbol.

Hence when you are using a linker script defined symbol in source code you should always take the address of the symbol, and never attempt to use its value. For example suppose you want to copy the contents of a section of memory called .ROM into a section called .FLASH and the linker script contains these declarations:

  start_of_ROM   = .ROM;end_of_ROM     = .ROM + sizeof (.ROM);start_of_FLASH = .FLASH;

Then the C source code to perform the copy would be:

  extern char start_of_ROM, end_of_ROM, start_of_FLASH;memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);

Note the use of the ‘&’ operators. These are correct. Alternatively the symbols can be treated as the names of vectors or arrays and then the code will again work as expected:

  extern char start_of_ROM[], end_of_ROM[], start_of_FLASH[];memcpy (start_of_FLASH, start_of_ROM, end_of_ROM - start_of_ROM);

Note how using this method does not require the use of ‘&’ operators.


http://www.mrgr.cn/news/82649.html

相关文章:

  • 小程序租赁系统开发的优势与应用前景分析
  • 设计模式 行为型 策略模式(Strategy Pattern)与 常见技术框架应用 解析
  • 人工智能的可解释性:从黑箱到透明
  • 【Android项目学习】3. MVVMHabit
  • 力扣--35.搜索插入位置
  • 小程序学习08—— 系统参数获取和navBar组件样式动态设置
  • Ubuntu 下载安装 Consul1.17.1
  • Branch-Solve-Merge Improves Large Language Model Evaluation and Generation
  • 高中数学部分基础知识
  • 【51单片机零基础-chapter4:LED数码管】
  • 【51单片机零基础-chapter2:灯独立点亮,自定义点亮,跑马灯点亮,函数】
  • 1.1.3 插入排序
  • GNU链接器简介
  • MAC环境安装(卸载)软件
  • 数据结构C语言描述8(图文结合)--哈希、哈希冲突、开放地址法、链地址法等实现
  • Oracle Dataguard(主库为 Oracle 11g 单节点)配置详解(2):配置主数据库
  • Kraft模式安装Kafka(含常规、容器两种安装方式)
  • 【操作系统不挂科】操作系统期末考试题库<1>(单选题&简答题&计算与分析题&应用题)
  • ARM CCA机密计算安全模型之固件更新
  • 代码实战:基于InvSR对视频进行超分辨率重建
  • Unity-Mirror网络框架-从入门到精通之Benchmark示例
  • 1.1.2.1 选择 + 冒泡排序
  • Oracle 11g rac + Dataguard 环境调整 redo log 大小
  • 与 Oracle Dataguard 相关的进程及作用分析
  • 1.1.7 master公式的使用
  • 1.2.1 归并排序