在 ld 默认链接描述文件中定义一个部分并打印其值
Define a section in the ld default linker script and print its value
我想在链接描述文件中定义一个部分,并在运行时从源代码中获取它的值。
到目前为止,我已经采用了默认的 gcc linker
脚本文件,并且添加了如下部分:
...
.my_section : { BYTE(0xAA); }
...
编译后,可以看到部分:
> gcc -T ls.ld main.c -o main
> objdump -h main
...
...
27 .my_section 00000001 0000000000a01040 0000000000a01040 00001040 2**0
CONTENTS, ALLOC, LOAD, DATA
28 .comment 00000034 0000000000000000 0000000000000000 00001041 2**0
CONTENTS, READONLY
现在,我想将该值打印到 stdout
(我希望获得 0xAA
):
#include <stdio.h>
static volatile unsigned char SECTION __attribute__((section(".my_section")));
int main(){
printf("hello %d\n", SECTION);
return 0;
}
我获取的值一直是0,我哪里做错了?
What I'm doing wrong?
你强迫 linker 在程序中输出 两个 部分,每个部分称为
.my_section
.
其中一个是由于:
static volatile unsigned char SECTION __attribute__((section(".my_section")));
在 main.c
中。在这个 .my_section
中,一个名为 SECTION
的符号被静态定义为
地址 a char
默认情况下静态初始化 = 0。当你
printf("hello %d\n", SECTION)
您当然是在该 0 初始化符号处打印整数。
另一个.my_section
是由于:
.my_section : { BYTE(0xAA); }
在 ls.ld
。第二个 .my_section
以字节 = 0xAA
开头,但永远不会
被程序访问。
这是一个例子。我有:
main.c
#include <stdio.h>
static volatile unsigned char MY_SECTION __attribute__((section(".my_section"))) = '!';
int main(){
printf("hello %c\n", MY_SECTION);
return 0;
}
我有一个 linker-script ls.ld
这是我的 gcc
默认 linker 脚本:
.my_section : { BYTE(0xAA); }
最后添加到 SECTIONS
。
编译,link和运行:
$ gcc -Wall -Wextra -T ls.ld -o prog main.c
$ ./prog
hello !
查看prog
的部分详情:
$ readelf -t prog
There are 31 section headers, starting at offset 0x3990:
Section Headers:
[Nr] Name
Type Address Offset Link
Size EntSize Info Align
Flags
...
[24] .my_section
PROGBITS PROGBITS 0000000000004010 0000000000003010 0
0000000000000001 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
[25] .bss
NOBITS NOBITS 0000000000004011 0000000000003011 0
0000000000000007 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
[26] .comment
PROGBITS PROGBITS 0000000000000000 0000000000003019 0
0000000000000023 0000000000000001 0 1
[0000000000000030]: MERGE, STRINGS
[27] .my_section
PROGBITS PROGBITS 0000000000006018 0000000000003018 0
0000000000000001 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
...
第 24 节称为 .my_section
,第 27 节也是如此。局部符号 MY_SECTION
:
$ readelf -s prog | grep 'MY_SECTION'
37: 0000000000004010 1 OBJECT LOCAL DEFAULT 24 MY_SECTION
在 24
.
节中定义
然后看反汇编:
$ objdump --disassemble-all prog
prog: file format elf64-x86-64
...
...
Disassembly of section .my_section:
0000000000004010 <__TMC_END__>:
4010: 21 .byte 0x21
...
...
Disassembly of section .my_section:
0000000000006018 <.my_section>:
6018: aa stos %al,%es:(%rdi)
...
...
第一个,以0x21
= !
开头,是在main.c
中创建的
并被程序访问。第二个,以 0xaa
开头,是
由 linker 脚本创建但程序未访问的那个。
选择一种输出方式 .my_section
或另一种:-
您可以在源代码中使用:
static volatile unsigned char MY_SECTION __attribute__((section(".my_section"))) = 0xAA;
或者您可以在 linker 脚本中执行此操作,正如@MichaelPetch 评论的那样,例如:
.my_section : { my_section_addr = .; BYTE(0xAA); }
并在如下程序中访问该部分:
$ cat main1.c
#include <stdio.h>
extern unsigned char my_section_addr[];
int main(){
printf("section `.my_section` starts at %p and the 1st byte is %x\n",
my_section_addr, (unsigned int)my_section_addr[0]);
return 0;
}
$ gcc -Wall -Wextra -T ls.ld -o prog main1.c
$ ./prog
section `.my_section` starts at 0x560a32964018 and the 1st byte is aa
但实际上并不需要自定义linker脚本来获得
程序中自定义部分的地址。参见:
$ cat main2.c
#include <stdio.h>
static unsigned char pling __attribute__((section("my_section"))) = '!';
extern unsigned char __start_my_section;
extern unsigned char __stop_my_section;
static char * p_my_section_start = &__start_my_section;
static char * p_my_section_end = &__stop_my_section;
int main(){
printf("section `my_section` starts at %p, ends at %p, and the 1st byte is %c\n",
p_my_section_start, p_my_section_end, p_my_section_start[0]);
return 0;
}
$ gcc -o prog main2.c
$ ./prog
section `my_section` starts at 0x55db7b0fb020, ends at 0x55db7b0fb021, and the 1st byte is !
看到 extern
形式的声明 __start_<section_name
或 __stop_<section_name>
,
linker 会自动将这些符号分别放在
部分 <section_name>
.
您是否应该编译和 link 访问相同自定义的多个源文件
section my_section
在程序中,您可以简单地定义归于 section 的符号
my_section
在几个源文件和 linker 中,使用默认的 linker 脚本,
将输入目标文件中所有名为 my_section
的部分合并为一个
在程序中输出my_section
。 (就像它合并一样,例如所有 .text
部分
输入目标文件的一部分到程序的单个 .text
部分)。参见:
$ cat foo.c
#include <stdio.h>
unsigned int foo __attribute__((section("my_section"))) = 0xf00;
$ cat boo.c
#include <stdio.h>
unsigned int boo __attribute__((section("my_section"))) = 0xb00;
$ cat main3.c
#include <stdio.h>
extern unsigned int foo;
extern unsigned int boo;
int main(){
printf("foo=%x, boo=%x\n",foo,boo);
return 0;
}
$ gcc -Wall -o prog main3.c foo.c boo.c
$ ./prog
foo=f00, boo=b00
和:
$ readelf -t prog | grep my_section
[24] my_section
程序中只有一节24,叫做my_section
,即:
$ readelf -s prog | egrep '(foo|boo)'
36: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
37: 0000000000000000 0 FILE LOCAL DEFAULT ABS boo.c
59: 0000000000004010 4 OBJECT GLOBAL DEFAULT 24 foo
66: 0000000000004014 4 OBJECT GLOBAL DEFAULT 24 boo
包含foo
和boo
的定义。
我想在链接描述文件中定义一个部分,并在运行时从源代码中获取它的值。
到目前为止,我已经采用了默认的 gcc linker
脚本文件,并且添加了如下部分:
...
.my_section : { BYTE(0xAA); }
...
编译后,可以看到部分:
> gcc -T ls.ld main.c -o main
> objdump -h main
...
...
27 .my_section 00000001 0000000000a01040 0000000000a01040 00001040 2**0
CONTENTS, ALLOC, LOAD, DATA
28 .comment 00000034 0000000000000000 0000000000000000 00001041 2**0
CONTENTS, READONLY
现在,我想将该值打印到 stdout
(我希望获得 0xAA
):
#include <stdio.h>
static volatile unsigned char SECTION __attribute__((section(".my_section")));
int main(){
printf("hello %d\n", SECTION);
return 0;
}
我获取的值一直是0,我哪里做错了?
What I'm doing wrong?
你强迫 linker 在程序中输出 两个 部分,每个部分称为
.my_section
.
其中一个是由于:
static volatile unsigned char SECTION __attribute__((section(".my_section")));
在 main.c
中。在这个 .my_section
中,一个名为 SECTION
的符号被静态定义为
地址 a char
默认情况下静态初始化 = 0。当你
printf("hello %d\n", SECTION)
您当然是在该 0 初始化符号处打印整数。
另一个.my_section
是由于:
.my_section : { BYTE(0xAA); }
在 ls.ld
。第二个 .my_section
以字节 = 0xAA
开头,但永远不会
被程序访问。
这是一个例子。我有:
main.c
#include <stdio.h>
static volatile unsigned char MY_SECTION __attribute__((section(".my_section"))) = '!';
int main(){
printf("hello %c\n", MY_SECTION);
return 0;
}
我有一个 linker-script ls.ld
这是我的 gcc
默认 linker 脚本:
.my_section : { BYTE(0xAA); }
最后添加到 SECTIONS
。
编译,link和运行:
$ gcc -Wall -Wextra -T ls.ld -o prog main.c
$ ./prog
hello !
查看prog
的部分详情:
$ readelf -t prog
There are 31 section headers, starting at offset 0x3990:
Section Headers:
[Nr] Name
Type Address Offset Link
Size EntSize Info Align
Flags
...
[24] .my_section
PROGBITS PROGBITS 0000000000004010 0000000000003010 0
0000000000000001 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
[25] .bss
NOBITS NOBITS 0000000000004011 0000000000003011 0
0000000000000007 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
[26] .comment
PROGBITS PROGBITS 0000000000000000 0000000000003019 0
0000000000000023 0000000000000001 0 1
[0000000000000030]: MERGE, STRINGS
[27] .my_section
PROGBITS PROGBITS 0000000000006018 0000000000003018 0
0000000000000001 0000000000000000 0 1
[0000000000000003]: WRITE, ALLOC
...
第 24 节称为 .my_section
,第 27 节也是如此。局部符号 MY_SECTION
:
$ readelf -s prog | grep 'MY_SECTION'
37: 0000000000004010 1 OBJECT LOCAL DEFAULT 24 MY_SECTION
在 24
.
然后看反汇编:
$ objdump --disassemble-all prog
prog: file format elf64-x86-64
...
...
Disassembly of section .my_section:
0000000000004010 <__TMC_END__>:
4010: 21 .byte 0x21
...
...
Disassembly of section .my_section:
0000000000006018 <.my_section>:
6018: aa stos %al,%es:(%rdi)
...
...
第一个,以0x21
= !
开头,是在main.c
中创建的
并被程序访问。第二个,以 0xaa
开头,是
由 linker 脚本创建但程序未访问的那个。
选择一种输出方式 .my_section
或另一种:-
您可以在源代码中使用:
static volatile unsigned char MY_SECTION __attribute__((section(".my_section"))) = 0xAA;
或者您可以在 linker 脚本中执行此操作,正如@MichaelPetch 评论的那样,例如:
.my_section : { my_section_addr = .; BYTE(0xAA); }
并在如下程序中访问该部分:
$ cat main1.c
#include <stdio.h>
extern unsigned char my_section_addr[];
int main(){
printf("section `.my_section` starts at %p and the 1st byte is %x\n",
my_section_addr, (unsigned int)my_section_addr[0]);
return 0;
}
$ gcc -Wall -Wextra -T ls.ld -o prog main1.c
$ ./prog
section `.my_section` starts at 0x560a32964018 and the 1st byte is aa
但实际上并不需要自定义linker脚本来获得 程序中自定义部分的地址。参见:
$ cat main2.c
#include <stdio.h>
static unsigned char pling __attribute__((section("my_section"))) = '!';
extern unsigned char __start_my_section;
extern unsigned char __stop_my_section;
static char * p_my_section_start = &__start_my_section;
static char * p_my_section_end = &__stop_my_section;
int main(){
printf("section `my_section` starts at %p, ends at %p, and the 1st byte is %c\n",
p_my_section_start, p_my_section_end, p_my_section_start[0]);
return 0;
}
$ gcc -o prog main2.c
$ ./prog
section `my_section` starts at 0x55db7b0fb020, ends at 0x55db7b0fb021, and the 1st byte is !
看到 extern
形式的声明 __start_<section_name
或 __stop_<section_name>
,
linker 会自动将这些符号分别放在
部分 <section_name>
.
您是否应该编译和 link 访问相同自定义的多个源文件
section my_section
在程序中,您可以简单地定义归于 section 的符号
my_section
在几个源文件和 linker 中,使用默认的 linker 脚本,
将输入目标文件中所有名为 my_section
的部分合并为一个
在程序中输出my_section
。 (就像它合并一样,例如所有 .text
部分
输入目标文件的一部分到程序的单个 .text
部分)。参见:
$ cat foo.c
#include <stdio.h>
unsigned int foo __attribute__((section("my_section"))) = 0xf00;
$ cat boo.c
#include <stdio.h>
unsigned int boo __attribute__((section("my_section"))) = 0xb00;
$ cat main3.c
#include <stdio.h>
extern unsigned int foo;
extern unsigned int boo;
int main(){
printf("foo=%x, boo=%x\n",foo,boo);
return 0;
}
$ gcc -Wall -o prog main3.c foo.c boo.c
$ ./prog
foo=f00, boo=b00
和:
$ readelf -t prog | grep my_section
[24] my_section
程序中只有一节24,叫做my_section
,即:
$ readelf -s prog | egrep '(foo|boo)'
36: 0000000000000000 0 FILE LOCAL DEFAULT ABS foo.c
37: 0000000000000000 0 FILE LOCAL DEFAULT ABS boo.c
59: 0000000000004010 4 OBJECT GLOBAL DEFAULT 24 foo
66: 0000000000004014 4 OBJECT GLOBAL DEFAULT 24 boo
包含foo
和boo
的定义。