<arch/x86/kernel/cpu/common.c>
void __init early_cpu_init(void)
const struct cpu_dev *const *cdev;
...
for (cdev = __x86_cpu_dev_start; cdev < __x86_cpu_dev_end; cdev++) {
const struct cpu_dev *cpudev = *cdev;
여기서 cdev가 가리키는게 무엇인가가 문제였는데 cdev는 말씀하신대로 2중 포인터가 맞습니다.
2중포인터라면 중간에 경유하는 주소 테이블이 있어야 하지 않은가? 하는게 의문이었습니다.
cdev는 cpu_dev 구조체를 가리키는 const 포인터를 가리키니 결국 크기는 포인터형입니다.
64비트의 포인터형이니 cdev++를 하면 값은 8이 증가될 것입니다.
__x86_cpu_dev_start는 arch/x86/kernel/vmlinux.lds.S 에 있습니다.
.x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) {
__x86_cpu_dev_start = .;
*(.x86_cpu_dev.init)
__x86_cpu_dev_end = .;
}
.x86_cpu_dev.init 섹션을 모아주는 매크로는 arch/x86/kernel/cpu/cpu.h 에 있습니다.
#define cpu_dev_register(cpu_devX)
static const struct cpu_dev *const __cpu_dev_##cpu_devX __used
__attribute__((__section__(".x86_cpu_dev.init"))) =
&cpu_devX;
&로 넣어주니 .x86_cpu_dev.init에는 포인터가 들어갑니다.
저는 이 부분에서 구조체 주소가 아니라 구조체 내용이 그대로 들어간다고 착각했었습니다.
__x86_cpu_dev.init은 각 cpu 의 cpu_dev 구조체 주소가 모인 섹션입니다.
<arch/x86/kernel/cpu/Makefile>
obj-$(CONFIG_CPU_SUP_INTEL) += intel.o
obj-$(CONFIG_CPU_SUP_AMD) += amd.o
obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o
obj-$(CONFIG_CPU_SUP_CENTAUR) += centaur.o
obj-$(CONFIG_CPU_SUP_TRANSMETA_32) += transmeta.o
obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o
<.config>
CONFIG_CPU_SUP_INTEL=y
CONFIG_CPU_SUP_AMD=y
CONFIG_CPU_SUP_CENTAUR=y
<intel.c> 마지막 부분에는 위 매크로로 등록하는 부분이 있습니다.
cpu_dev_register(intel_cpu_dev);
<amd.c>
cpu_dev_register(amd_cpu_dev);
<centaur.c>
cpu_dev_register(centaur_cpu_dev);
ld 스크립트에 의해 포인터가 모였습니다. 확인을 해보겠습니다.
/usr/src/linux/arch/x86/boot/compressed# objdump -h vmlinux.bin
Sections:
Idx Name Size VMA LMA File off Algn
....
3 .rodata 000f7052 ffffffff81400000 0000000001400000 00600000 2**6
CONTENTS, ALLOC, LOAD, READONLY, DATA
...
11 __init_rodata 00003e70 ffffffff81530490 0000000001530490 00730490 2**4
CONTENTS, ALLOC, LOAD, READONLY, DATA
...
20 .x86_cpu_dev.init 00000018 ffffffff81702370 0000000001702370 00b02370 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
사이즈는 0x18 = 24바이트네요.
/usr/src/linux/arch/x86/boot/compressed# objdump vmlinux.bin -s -j .x86_cpu_dev.init
vmlinux.bin: file format elf64-x86-64
Contents of section .x86_cpu_dev.init:
ffffffff81702370 30395381 ffffffff 903b5381 ffffffff 09S......;S.....
ffffffff81702380 f03d5381 ffffffff .=S.....
리틀엔디안이니 뒤집어서 ff ff ff ff 81 53 39 30, ff ff ff ff 81 53 3b 90, ff ff ff ff 81 53 3d f0 를 찾아가겠습니다..
위쪽에 81530490부터 81534300까지는 __init_rodata 영역에 속하네요.
/usr/src/linux/arch/x86/boot/compressed# objdump -s -j __init_rodata vmlinux.bin|grep 81533930
ffffffff81533930 cb644b81 ffffffff c4644b81 ffffffff .dK......dK.....
구조체를 찾아보겠습니다.
<arch/x86/kernel/cpu/cpu.h>
struct cpu_dev {
const char *c_vendor;
/* some have two possibilities for cpuid string */
const char *c_ident[2];
....
}
<intel.c>
static const struct cpu_dev __cpuinitconst intel_cpu_dev = {
.c_vendor = "Intel",
.c_ident = { "GenuineIntel" },
....
}
ff ff ff ff 81 4b 64 cb는 .rodata 섹션에 있네요.
/usr/src/linux/arch/x86/boot/compressed# objdump -s -j .rodata vmlinux.bin|grep 814b64c0
ffffffff814b64c0 38290a00 47656e75 696e6549 6e74656c 8)..GenuineIntel
*c_vendor와 *c_indent 문자열이 동일하니 같은 주소를 재활용하네요. gcc는 똑똑한것 같습니다.
c_ventor는 814b64cb번지로 "Intel", c_ident는 814b64c4번지로 "GenuineIntel"이 됩니다.
간단히 그림으로 그려봤습니다.
void __init early_cpu_init(void)
const struct cpu_dev *const *cdev;
...
for (cdev = __x86_cpu_dev_start; cdev < __x86_cpu_dev_end; cdev++) {
const struct cpu_dev *cpudev = *cdev;
여기서 cdev가 가리키는게 무엇인가가 문제였는데 cdev는 말씀하신대로 2중 포인터가 맞습니다.
2중포인터라면 중간에 경유하는 주소 테이블이 있어야 하지 않은가? 하는게 의문이었습니다.
cdev는 cpu_dev 구조체를 가리키는 const 포인터를 가리키니 결국 크기는 포인터형입니다.
64비트의 포인터형이니 cdev++를 하면 값은 8이 증가될 것입니다.
__x86_cpu_dev_start는 arch/x86/kernel/vmlinux.lds.S 에 있습니다.
.x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) {
__x86_cpu_dev_start = .;
*(.x86_cpu_dev.init)
__x86_cpu_dev_end = .;
}
.x86_cpu_dev.init 섹션을 모아주는 매크로는 arch/x86/kernel/cpu/cpu.h 에 있습니다.
#define cpu_dev_register(cpu_devX)
static const struct cpu_dev *const __cpu_dev_##cpu_devX __used
__attribute__((__section__(".x86_cpu_dev.init"))) =
&cpu_devX;
&로 넣어주니 .x86_cpu_dev.init에는 포인터가 들어갑니다.
저는 이 부분에서 구조체 주소가 아니라 구조체 내용이 그대로 들어간다고 착각했었습니다.
__x86_cpu_dev.init은 각 cpu 의 cpu_dev 구조체 주소가 모인 섹션입니다.
<arch/x86/kernel/cpu/Makefile>
obj-$(CONFIG_CPU_SUP_INTEL) += intel.o
obj-$(CONFIG_CPU_SUP_AMD) += amd.o
obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o
obj-$(CONFIG_CPU_SUP_CENTAUR) += centaur.o
obj-$(CONFIG_CPU_SUP_TRANSMETA_32) += transmeta.o
obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o
<.config>
CONFIG_CPU_SUP_INTEL=y
CONFIG_CPU_SUP_AMD=y
CONFIG_CPU_SUP_CENTAUR=y
<intel.c> 마지막 부분에는 위 매크로로 등록하는 부분이 있습니다.
cpu_dev_register(intel_cpu_dev);
<amd.c>
cpu_dev_register(amd_cpu_dev);
<centaur.c>
cpu_dev_register(centaur_cpu_dev);
ld 스크립트에 의해 포인터가 모였습니다. 확인을 해보겠습니다.
/usr/src/linux/arch/x86/boot/compressed# objdump -h vmlinux.bin
Sections:
Idx Name Size VMA LMA File off Algn
....
3 .rodata 000f7052 ffffffff81400000 0000000001400000 00600000 2**6
CONTENTS, ALLOC, LOAD, READONLY, DATA
...
11 __init_rodata 00003e70 ffffffff81530490 0000000001530490 00730490 2**4
CONTENTS, ALLOC, LOAD, READONLY, DATA
...
20 .x86_cpu_dev.init 00000018 ffffffff81702370 0000000001702370 00b02370 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
사이즈는 0x18 = 24바이트네요.
/usr/src/linux/arch/x86/boot/compressed# objdump vmlinux.bin -s -j .x86_cpu_dev.init
vmlinux.bin: file format elf64-x86-64
Contents of section .x86_cpu_dev.init:
ffffffff81702370 30395381 ffffffff 903b5381 ffffffff 09S......;S.....
ffffffff81702380 f03d5381 ffffffff .=S.....
리틀엔디안이니 뒤집어서 ff ff ff ff 81 53 39 30, ff ff ff ff 81 53 3b 90, ff ff ff ff 81 53 3d f0 를 찾아가겠습니다..
위쪽에 81530490부터 81534300까지는 __init_rodata 영역에 속하네요.
/usr/src/linux/arch/x86/boot/compressed# objdump -s -j __init_rodata vmlinux.bin|grep 81533930
ffffffff81533930 cb644b81 ffffffff c4644b81 ffffffff .dK......dK.....
구조체를 찾아보겠습니다.
<arch/x86/kernel/cpu/cpu.h>
struct cpu_dev {
const char *c_vendor;
/* some have two possibilities for cpuid string */
const char *c_ident[2];
....
}
<intel.c>
static const struct cpu_dev __cpuinitconst intel_cpu_dev = {
.c_vendor = "Intel",
.c_ident = { "GenuineIntel" },
....
}
ff ff ff ff 81 4b 64 cb는 .rodata 섹션에 있네요.
/usr/src/linux/arch/x86/boot/compressed# objdump -s -j .rodata vmlinux.bin|grep 814b64c0
ffffffff814b64c0 38290a00 47656e75 696e6549 6e74656c 8)..GenuineIntel
*c_vendor와 *c_indent 문자열이 동일하니 같은 주소를 재활용하네요. gcc는 똑똑한것 같습니다.
c_ventor는 814b64cb번지로 "Intel", c_ident는 814b64c4번지로 "GenuineIntel"이 됩니다.
간단히 그림으로 그려봤습니다.
.
인생은 무엇이고 나는 누구인가.
자문자답의 달인을 뵙겠습니다 ㅋㅋㅋ
좋은정리입니다+_+