i want to put some functions in specific section
i add
include/asm-generic/vmlinux.lds.h:
#define MY_TEXT \
ALIGN_FUNCTION(); \
VMLINUX_SYMBOL(__my_text_start) = .; \
*(.my.text) \
VMLINUX_SYMBOL(__my_text_end) = .;
arch/arm/kernel/vmlinux.lds.S:
...
TEXT_TEXT
MY_TEXT
SCHED_TEXT
...
my code:
#define __my __attribute__((section(".my.text")))
int __my my_test(int i)
{
...
...
}
but the compiler show:
/tmp/ccp1mtiA.s: Assembler messages:
/tmp/ccp1mtiA.s:13: Warning: setting incorrect section type for .my.text
/tmp/ccp1mtiA.s:13: Warning: setting incorrect section attributes for .my.text
how to fix the warning?
i change to
int __sche my_test(int i)
the warning is missed, i guess my section is not define well.
thanks
You can control sections for your code with the section attribute (taken nearly verbatim from the gcc docs):
#include <stdio.h>
extern void foobar (void) __attribute__ ((section (.bar.text)));
void foobar()
{
printf("asdf");
}
int main()
{
foobar();
}
Build with gcc -o sec.exe sec.cpp. Examining the output from objdump -h sec.exe, we see a section named .bar.text:
sec.exe: file format pei-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00001d10 0000000000401000 0000000000401000 00000400 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .bar.text 0000001c 0000000000403000 0000000000403000 00002200 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .data 000000a0 0000000000404000 0000000000404000 00002400 2**4
CONTENTS, ALLOC, LOAD, DATA
3 .rdata 00000978 0000000000405000 0000000000405000 00002600 2**4
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .pdata 00000240 0000000000406000 0000000000406000 00003000 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .xdata 0000020c 0000000000407000 0000000000407000 00003400 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .bss 000009b0 0000000000408000 0000000000408000 00000000 2**5
ALLOC
7 .idata 000007c8 0000000000409000 0000000000409000 00003800 2**2
CONTENTS, ALLOC, LOAD, DATA
8 .CRT 00000068 000000000040a000 000000000040a000 00004000 2**3
CONTENTS, ALLOC, LOAD, DATA
9 .tls 00000068 000000000040b000 000000000040b000 00004200 2**5
CONTENTS, ALLOC, LOAD, DATA
10 .debug_aranges 00000050 000000000040c000 000000000040c000 00004400 2**4
CONTENTS, READONLY, DEBUGGING
11 .debug_info 00000df5 000000000040d000 000000000040d000 00004600 2**0
CONTENTS, READONLY, DEBUGGING
12 .debug_abbrev 00000087 000000000040e000 000000000040e000 00005400 2**0
CONTENTS, READONLY, DEBUGGING
13 .debug_line 00000119 000000000040f000 000000000040f000 00005600 2**0
CONTENTS, READONLY, DEBUGGING
14 .debug_frame 00000048 0000000000410000 0000000000410000 00005800 2**3
CONTENTS, READONLY, DEBUGGING
Be aware that some compile options can end up re-combining sections.
Related
I'm building an embeeded software for STM32 microcontroller with the toolchain GNU Tools for STM32 and I need the binary output without gaps.
The linker produces a gap between sections .text and .rodata. The problem is the alignment of the section .rodata. The issue appears by using of the GNU Tools for STM32 version 9-2020-q2-update. The previous version I had used (7-2018-q2-update) did not produced that issue.
Excerpt from the linker script (it's the same for both GNU Tools versions):
SECTIONS
{
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4); /* PaulV: change that to ALIGN(8) eliminates the gap */
} >FLASH
/* Constant data into "FLASH" Rom type memory */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
}
More details:
The version 7-2018-q2-update produces the output without gap.
The .lst file (note that section .rodata is aligned to bound 4):
K4_G1.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
....
3 .text 0001a20c 08100800 08100800 00010800 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
4 .rodata 00009b54 0811aa0c 0811aa0c 0002aa0c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
and .map file (no gap between the non-empty sections .fini and .rodata):
.fini 0x000000000811aa04 0x8 c:/st/stm32cubeide_1.4.0/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.5.0.202011040924/tools/bin/../lib/gcc/arm-none-eabi/7.3.1/thumb/v7e-m/fpv5/hard/crtn.o
0x000000000811aa0c . = ALIGN (0x4)
0x000000000811aa0c _etext = .
.vfp11_veneer 0x000000000811aa0c 0x0
.vfp11_veneer 0x000000000811aa0c 0x0 linker stubs
.v4_bx 0x000000000811aa0c 0x0
.v4_bx 0x000000000811aa0c 0x0 linker stubs
.iplt 0x000000000811aa0c 0x0
.iplt 0x000000000811aa0c 0x0 c:/st/stm32cubeide_1.4.0/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7-2018-q2-update.win32_1.5.0.202011040924/tools/bin/../lib/gcc/arm-none-eabi/7.3.1/thumb/v7e-m/fpv5/hard/crtbegin.o
.rodata 0x000000000811aa0c 0x9b54
0x000000000811aa0c . = ALIGN (0x4)
*(.rodata)
.rodata 0x000000000811aa0c 0x8c Src/app_composer/init.o
The version 9-2020-q2-update produces the output with gap.
The .lst file (note that section .rodata is aligned to bound 8, but why?):
K4_G1.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
...
3 .text 0001923c 08100800 08100800 00010800 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
4 .rodata 000061f0 08119a40 08119a40 00029a40 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
and .map file (there is a gap between the non-empty sections .fini and .rodata):
.fini 0x0000000008119a34 0x8 c:/st/stm32cubeide_1.4.0/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.9-2020-q2-update.win32_1.5.0.202011040924/tools/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v7e-m+dp/hard/crtn.o
.vfp11_veneer 0x0000000008119a3c 0x0
.vfp11_veneer 0x0000000008119a3c 0x0 linker stubs
.v4_bx 0x0000000008119a3c 0x0
.v4_bx 0x0000000008119a3c 0x0 linker stubs
.iplt 0x0000000008119a3c 0x0
.iplt 0x0000000008119a3c 0x0 c:/st/stm32cubeide_1.4.0/stm32cubeide/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.9-2020-q2-update.win32_1.5.0.202011040924/tools/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v7e-m+dp/hard/crtbegin.o
.rodata 0x0000000008119a40 0x61f0
0x0000000008119a40 . = ALIGN (0x4)
*(.rodata)
.rodata 0x0000000008119a40 0x96 Src/app_composer/init.o
Edit 03/16/2021
There are no sections *(.rodata) in the input object files having
alignment on the boundary 8 or greater.
Changing the section name .rodata to the name .text eliminates the
gap (The same result if I join the sections .text and .rodata content to a
single .text section):
SECTIONS
{
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
/* ... */
. = ALIGN(4);
} >FLASH
/* Constant data into "FLASH" Rom type memory */
.text : /* <-- the same name as the previous section instead of .rodata */
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
}
The source code and build settings are also the same for both variants.
What could be the reason for the problem and how could it be solved? Do I missing something?
P.S. Of cause I can change the alignment at the end of .text section to 8. That would be treating the symptoms, but I want to understand the cause.
Thanks in advance for your help!
Run objdump -h on the input object files. I suspect that you will find that the compiler is putting a minimum alignment of 8 on one of the input .rodata sections. The linker then sets the output alignment to the maximum of the input sections.
I am trying to write a simple "Hello, World!" firmware for Cortex-M0 CPU. The goal is to correctly initialize and shutdown C++ runtime so that global constructors are called before main() and global destructors are called after main().
So far I managed to get exactly half of it working - global ctors run correctly, but global destructors don't. I use gcc on Windows with the Newlib, which is supposed to initialize the runtime.
struct Test {
Test() // This is invoked correctly.
{}
~Test() // This is not invoked at all.
{}
};
Test test;
int main()
{
return 0;
}
extern void __libc_init_array();
extern void __libc_fini_array();
void reset_handler() // This is the entry point.
{
__libc_init_array(); // Test::Test().
asm volatile( "bl main" ); // Invoke 'main'.
__libc_fini_array(); // Expect Test::~Test() but nothing happens.
}
I did quite a bit of research already, and it seems that for ARM compiler should generate a section called .init_array, for global constructors, and .fini_array, for global destructors, where it put pointer to the function to be invoked. Then, the linker will merge the sections from all units together, and __libc_init_array will "walk" through that section and call corresponding functions.
This is the relevant part of the linker script:
/* Initialization functions which run before main(),
such as global constructors. */
.init_array : ALIGN( 4 ) {
/* preinit data */
PROVIDE_HIDDEN ( __preinit_array_start = . );
KEEP( *( .preinit_array ) )
PROVIDE_HIDDEN( __preinit_array_end = . );
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN ( __init_array_start = . );
KEEP( *( SORT( .init_array.* ) ) )
KEEP(*(.init_array))
PROVIDE_HIDDEN ( __init_array_end = . );
} > flash
/* Finalization functions which run after main(),
such as global destructors. */
.fini_array : ALIGN( 4 ) {
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP ( *( .fini_array.* ) )
KEEP ( *( .fini_array ) )
PROVIDE_HIDDEN (__fini_array_end = .);
} > flash
What worries me, however, is that when I dump the object file, main.o, not even an executable, I can only see the .init_array section, but no .fini_array:
Sections:
Idx Name Size VMA LMA File off Algn
0 .group 00000008 00000000 00000000 00000034 2**2
CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
1 .group 00000008 00000000 00000000 0000003c 2**2
CONTENTS, READONLY, GROUP, LINK_ONCE_DISCARD
2 .text 0000022c 00000000 00000000 00000044 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
3 .data 00000000 00000000 00000000 00000270 2**0
CONTENTS, ALLOC, LOAD, DATA
4 .bss 00000001 00000000 00000000 00000270 2**2
ALLOC
5 .text._ZN4TestC2Ev 00000012 00000000 00000000 00000270 2**1 << Test::Test()
CONTENTS, ALLOC, LOAD, READONLY, CODE
6 .text._ZN4TestD2Ev 00000012 00000000 00000000 00000282 2**1 << Test::~Test()
CONTENTS, ALLOC, LOAD, READONLY, CODE
7 .init_array 00000004 00000000 00000000 00000294 2**2 << WHERE IS .fini_array???
CONTENTS, ALLOC, LOAD, RELOC, DATA
8 .debug_info 000012d9 00000000 00000000 00000298 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
9 .debug_abbrev 000003a1 00000000 00000000 00001571 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
10 .debug_aranges 00000030 00000000 00000000 00001912 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
11 .debug_ranges 00000020 00000000 00000000 00001942 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
12 .debug_line 000002d2 00000000 00000000 00001962 2**0
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
13 .debug_str 000009e4 00000000 00000000 00001c34 2**0
CONTENTS, READONLY, DEBUGGING, OCTETS
14 .comment 0000004d 00000000 00000000 00002618 2**0
CONTENTS, READONLY
15 .debug_frame 0000054c 00000000 00000000 00002668 2**2
CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
16 .ARM.attributes 0000002c 00000000 00000000 00002bb4 2**0
CONTENTS, READONLY
This is how I invoke the compiler:
arm-none-eabi-c++.exe -o main.elf --verbose -mcpu=cortex-m0 -mthumb --specs=nano.specs --entry reset_handler -T./../src/firmware.ld -ggdb -O0 -Wall -Wextra -Wpedantic -Werror ../src/main.cpp
And this is the log:
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 7a1ab17ae8404f635d46188cccacd8be
COLLECT_GCC_OPTIONS='-o' 'main.elf' '-v' '-mcpu=cortex-m0' '-mthumb' '-specs=nano.specs' '-e' 'reset_handler' '-T' './../src/firmware.ld' '-ggdb' '-O0' '-Wall' '-Wextra' '-Wpedantic' '-Werror' '-mfloat-abi=soft' '-march=armv6s-m'
c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/as.exe -v -march=armv6s-m -mfloat-abi=soft -meabi=5
GNU assembler version 2.34.0 (arm-none-eabi) using BFD version (GNU Arm Embedded Toolchain 9-2020-q2-update) 2.34.0.20200428
COMPILER_PATH=c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/;c:/id/gcc/bin/../lib/gcc/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/
LIBRARY_PATH=c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v6-m/nofp/;c:/id/gcc/bin/../arm-none-eabi/lib/thumb/v6-m/nofp/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/;c:/id/gcc/bin/../lib/gcc/;c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/;c:/id/gcc/bin/../arm-none-eabi/lib/
COLLECT_GCC_OPTIONS='-o' 'main.elf' '-v' '-mcpu=cortex-m0' '-mthumb' '-specs=nano.specs' '-e' 'reset_handler' '-T' './../src/firmware.ld' '-ggdb' '-O0' '-Wall' '-Wextra' '-Wpedantic' '-Werror' '-mfloat-abi=soft' '-march=armv6s-m'
c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/collect2.exe -plugin c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/liblto_plugin-0.dll -plugin-opt=c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/lto-wrapper.exe -plugin-opt=-fresolution=C:\Users\MAXID~1\AppData\Local\Temp\ccpOTkFN.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lg_nano -plugin-opt=-pass-through=-lc_nano -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc_nano --sysroot=c:\id\gcc\bin\../arm-none-eabi -X -o main.elf -e reset_handler c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crti.o c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crtbegin.o c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v6-m/nofp/crt0.o -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v6-m/nofp -Lc:/id/gcc/bin/../arm-none-eabi/lib/thumb/v6-m/nofp -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1 -Lc:/id/gcc/bin/../lib/gcc -Lc:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib -Lc:/id/gcc/bin/../arm-none-eabi/lib C:\Users\MAXID~1\AppData\Local\Temp\cc1lU5Gg.o -lstdc++_nano -lm --start-group -lgcc -lg_nano -lc_nano --end-group --start-group -lgcc -lc_nano --end-group c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crtend.o c:/id/gcc/bin/../lib/gcc/arm-none-eabi/9.3.1/thumb/v6-m/nofp/crtn.o -T ./../src/firmware.ld
COLLECT_GCC_OPTIONS='-o' 'main.elf' '-v' '-mcpu=cortex-m0' '-mthumb' '-specs=nano.specs' '-e' 'reset_handler' '-T' './../src/firmware.ld' '-ggdb' '-O0' '-Wall' '-Wextra' '-Wpedantic' '-Werror' '-mfloat-abi=soft' '-march=armv6s-m'
To be fair, I have zero idea of what could be wrong - I tried the default linker script, .fini_array did not appear.
The only idea which comes to mind is that my compiler was built with some kind of a flag which disabled the .fini_array section???
Any ideas will be much appreciated!
UPDATE: after further investigation, it seems that the destructors have more to do with __cxa_atexit... Will investigate further.
UPDATE2: Thanks to this wonderful discussion here https://forum.osdev.org/viewtopic.php?f=13&t=36728 I added -fno-use-cxa-atexit, and the .fini_array section appeared!
Ok, so for those who are interested, there are two ways for gcc to generate calls to global destructors. One way is via __cxa_atexit. The compiler will inject a piece of code into the global constructor, which uses __cxa_atexit to register the destructor of that object. This way, when exit is called, the runtime "knows" which destructors to invoke. This complexity is needed to deal with loading/unloading of shared libraries.
However, it is also possible to pass a -fno-cxa-atexit flag, and the compiler will put the calls to global destructors into .fini_array. The Newlib then uses this section to invoke the destructors.
More can be found here: https://forum.osdev.org/viewtopic.php?f=13&t=36728
I am trying to use a custom RAM section to be able to pass information across reboot. This section will not be erased at boot and so the variables placed in this section will be kept across reboots (if there is no alimentation loss of course).
I use GNU toolchain and a Cortex-M0 (STM32) MCU
So I added in the linker script a new memory area before RAM :
RAM_PERSIST (xrw) : ORIGIN = 0x20000000, LENGTH = 0x0040
RAM (xrw) : ORIGIN = 0x20000040, LENGTH = 0x0FD0
Then a section to go in there :
.pds :
{
KEEP(*(.pds))
} >RAM_PERSIST
Finally in the C code, I declare some data in this section :
data_t __attribute((section(".pds")) data;
I does compile but I could not upload the generated binary on my target. Using objdump I discovered that my firmware got a new section ".sec2" beginning at 0x20000000 :
> (...)/arm-none-eabi-objdump -s ./obj/firmware.hex | tail
8006d20 f8bc08bc 9e467047 f8b5c046 f8bc08bc .....FpG...F....
8006d30 9e467047 e9000008 c1000008 00127a00 .FpG..........z.
8006d40 19000000 e0930400 409c0000 400d0300 ........#...#...
8006d50 c0c62d00 30750000 ffffffff 01000000 ..-.0u..........
8006d60 04000000 ....
Contents of section .sec2:
20000000 00000000 00000000 00000000 00000000 ................
20000010 00000000 00000000 00000000 00000000 ................
20000020 00000000 00000000 00000000 00000000 ................
20000030 00000000 00000000 00000000 00000000 ................
So I think I have to tell the linker this section is not in the flash so must not be part of the firmware.
Am I right ? If so, how to do that ?
Thanks by advance.
MEMORY
{
rom : ORIGIN = 0x00000000, LENGTH = 0x40000
ram : ORIGIN = 0x20000000, LENGTH = 0x4000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
I had more control/success when I stopped using xrw, etc in the memory definition and instead went with control over .text, .bss, .data, etc. and if you then further want a specific object somewhere you add that. etc...
I did achieve what I wanted by adding NOLOAD attibute to my custom section :
.pds (NOLOAD): { KEEP(*(.pds)) } >RAM
Here is the NOLOAD description (gcc documentation) :
(NOLOAD)
The (NOLOAD) directive will mark a section to not be loaded at run time. The linker will process the section normally, but will mark
it so that a program loader will not load it into memory. For example,
in the script sample below, the ROM section is addressed at memory
location 0 and does not need to be loaded when the program is run.
The contents of the ROM section will appear in the linker output file
as usual.
SECTIONS {
ROM 0 (NOLOAD) : { ... }
...
}
I found a similar post which helped me, I add a link here for reference : GCC (NOLOAD) directive loads memory into section anyway
I'm compiling a very simple hello-world one-liner statically on Debian 7 system on x86_64 machine with gcc version 4.8.2 (Debian 4.8.2-21):
gcc test.c -static -o test
and I get an executable ELF file that includes the following sections:
[17] .tdata PROGBITS 00000000006b4000 000b4000
0000000000000020 0000000000000000 WAT 0 0 8
[18] .tbss NOBITS 00000000006b4020 000b4020
0000000000000030 0000000000000000 WAT 0 0 8
[19] .init_array INIT_ARRAY 00000000006b4020 000b4020
0000000000000010 0000000000000000 WA 0 0 8
[20] .fini_array FINI_ARRAY 00000000006b4030 000b4030
0000000000000010 0000000000000000 WA 0 0 8
[21] .jcr PROGBITS 00000000006b4040 000b4040
0000000000000008 0000000000000000 WA 0 0 8
[22] .data.rel.ro PROGBITS 00000000006b4060 000b4060
00000000000000e4 0000000000000000 WA 0 0 32
Note that .tbss section is allocated at addresses 0x6b4020..0x6b4050 (0x30 bytes) and it intersects with allocation of .init_array section at 0x6b4020..0x6b4030 (0x10 bytes), .fini_array section at 0x6b4030..0x6b4040 (0x10 bytes) and with .jcr section at 0x6b4040..0x6b4048 (8 bytes).
Note it does not intersect with the following sections, for example, .data.rel.ro, but that's probably because .data.rel.ro alignment is 32 and thus it can't be placed any earlier than 0x6b4060.
The resulting file runs ok, but I still don't exactly get how it works. From what I read in glibc documentation, .tbss is a just .bss section for thread local storage (i.e. allocated memory scratch space, not really mapped in physical file). Is it that .tbss section is so special that it can overlap other sections? Are .init_array, .fini_array and .jcr are so useless (for example, they are not needed anymore then TLS-related code runs), so they can be overwritten by bss? Or is it some sort of a bug?
Basically, what do I get to read and write if I'll try to read address 0x6b4020 in my application? .tbss contents or .init_array pointers? Why?
The virtual address of .tbss is meaningless as that section only serves as a template for the TLS storage as allocated by the threading implementation in GLIBC.
The way this virtual address comes into place is that .tbss follows .tbdata in the default linker script:
...
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
}
...
therefore its virtual address is simply the virtual address of the preceding section (.tbdata) plus the size of the preceding section (eventually with some padding in order to reach the desired alignment). .init_array (or .preinit_array if present) comes next and its location should be determined the same way, but .tbss is known to be so very special, that it is given a deeply hard-coded treatment inside GNU LD:
/* .tbss sections effectively have zero size. */
if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
|| (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0
|| link_info.relocatable)
dotdelta = TO_ADDR (os->bfd_section->size);
else
dotdelta = 0; // <----------------
dot += dotdelta;
.tbss is not relocatable, it has the SEC_THREAD_LOCAL flag set, and it does not have contents (NOBITS), therefore the else branch is taken. In other words, no matter how large the .tbss is, the linker does not advance the location of the section that follows it (also know as "the dot").
Note also that .tbss sits in a non-loadable ELF segment:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000b1f24 0x00000000000b1f24 R E 200000
LOAD 0x00000000000b2000 0x00000000006b2000 0x00000000006b2000
0x0000000000002288 0x00000000000174d8 RW 200000
NOTE 0x0000000000000158 0x0000000000400158 0x0000000000400158
0x0000000000000044 0x0000000000000044 R 4
TLS 0x00000000000b2000 0x00000000006b2000 0x00000000006b2000 <---+
0x0000000000000020 0x0000000000000060 R 8 |
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 |
0x0000000000000000 0x0000000000000000 RW 8 |
|
Section to Segment mapping: |
Segment Sections... |
00 .note.ABI-tag ... |
01 .tdata .ctors ... |
02 .note.ABI-tag ... |
03 .tdata .tbss <---------------------------------------------------+
04
This is rather simple if you have an understanding about two things:
1) What is SHT_NOBITS
2) What is tbss section
SHT_NOBITS means that this section occupies no space inside file.
Normally, NOBITS sections, like bss are placed after all PROGBITS sections at the end of the loaded segments.
tbss is special section to hold uninitialized thread-local data that contribute to the program's memory image. Take an attention here: this section must hold unique data for each program thread.
Now lets talk about overlapping. We have two possible overlappings -- inside binary file and inside memory.
1) Binary files offset:
There is no data to write under this section in binary. Inside file it holds no space, so linker start next section init_array immediately after tbss declared. You may think about its size not as about size, but as about special service information for code like:
if (isTLSSegment) tlsStartAddr += section->memSize();
So it doesn't overlap anything inside file.
2) Memory offset
The tdata and tbss sections may be possibly modified at startup time by the dynamic linker
performing relocations, but after that the section data is kept around as the initialization image and not modified anymore. For each thread, including the initial one, new memory is allocated into which then the content of the initialization image is copied. This ensures that all threads get the same starting conditions.
This what makes tbss (and tdata) so special.
Do not think about their memory offsets as about statically known -- they are more like "generation patterns" for per-thread work. So they also can not overlap with "normal" memory offsets -- they are being processed in other way.
You may consult with this paper to know more.
I have a problem with Virtual Treeview Destructor, which stops the WorkerThread while it is still running.
Code:
destructor TBaseVirtualTree.Destroy;
begin
Exclude(FOptions.FMiscOptions, toReadOnly);
ReleaseThreadReference(Self);
and the code for ReleaseThreadReference:
procedure ReleaseThreadReference(Tree: TBaseVirtualTree);
begin
if Assigned(WorkerThread) then
begin
Dec(WorkerThread.FRefCount);
// Make sure there is no reference remaining to the releasing tree.
Tree.InterruptValidation;
if WorkerThread.FRefCount = 0 then
begin
with WorkerThread do
begin
Terminate;
SetEvent(WorkEvent);
end;
FreeAndNil(WorkerThread);
CloseHandle(WorkEvent);
end;
end;
end;
And output from WinDbg:
FAULTING_IP:
vcl120!Forms.TGlassFrame.FrameExtended+3
501f0b57 807b0800 cmp byte ptr [ebx+8],0
EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 501f0b57 (vcl120!Forms.TGlassFrame.FrameExtended+0x00000003)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00000008
Attempt to read from address 00000008
PROCESS_NAME: MyProcess.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
EXCEPTION_PARAMETER1: 00000000
EXCEPTION_PARAMETER2: 00000008
READ_ADDRESS: 00000008
FOLLOWUP_IP:
vcl120!Forms.TGlassFrame.FrameExtended+0
501f0b54 53 push ebx
FAULTING_THREAD: 00000894
ADDITIONAL_DEBUG_TEXT: Followup set via attribute from Frame 0 on thread 894
BUGCHECK_STR: APPLICATION_FAULT_NULL_CLASS_PTR_DEREFERENCE_WINDOW_HOOK
PRIMARY_PROBLEM_CLASS: NULL_CLASS_PTR_DEREFERENCE
DEFAULT_BUCKET_ID: NULL_CLASS_PTR_DEREFERENCE
LAST_CONTROL_TRANSFER: from 501c565d to 501f0b57
00d7eca4 501c565d 00d7efb0 028408b9 00d7ee74 vcl120!Forms.TGlassFrame.FrameExtended+0x3
00d7edd0 501c9cec 00d7ee74 01a58e10 77c2c3ce vcl120!Controls.TControl.WndProc+0x2d5
00d7ee10 501e5a59 00d7efb0 028408b9 02190e80 vcl120!Controls.TWinControl.WndProc+0x518
00d7ee3c 501c9403 00d7ee50 50007cb0 00d7ee6c vcl120!Forms.TCustomForm.WndProc+0x599
00d7ee6c 500591de 00000046 00000000 00d7efb0 vcl120!Controls.TWinControl.MainWndProc+0x2f
00d7ee84 77d48709 002b00ee 00000046 00000000 rtl120!Classes.StdWndProc+0x16
00d7eeb0 77d4d297 028408b9 002b00ee 00000046 user32!InternalCallWinProc+0x28
00d7ef18 77d4b368 00000000 028408b9 002b00ee user32!UserCallWinProcCheckWow+0xea
00d7ef6c 77d4d1da 01c9c880 00000046 00000000 user32!DispatchClientMessage+0xa3
00d7ef94 7c90eae3 00d7efa4 00000030 01c9c880 user32!__fnINOUTLPWINDOWPOS+0x27
00d7efac 002b00ee 00000000 00000000 00000000 ntdll!KiUserCallbackDispatcher+0x13
00d7efe8 501e6a2b 002b00ee 00000000 00d7f1cc MainControls!TUJournalIntf.ImportFunFile+0x33a
00d7f020 501e88cc 02190e80 00d7f158 501c565d vcl120!Forms.TCustomForm.SetMenu+0x197
00d7f02c 501c565d 00d7f35c 028408b9 00d7f1fc vcl120!Forms.TCustomForm.WMNCCreate+0x20
00d7f158 501c9cec 00d7f1fc 00d7f1a4 501c9cec vcl120!Controls.TControl.WndProc+0x2d5
00d7f198 501e5a59 00d7f35c 028408b9 02190e80 vcl120!Controls.TWinControl.WndProc+0x518
00d7f1c4 501c9403 00d7f1d8 501c941b 00d7f1f4 vcl120!Forms.TCustomForm.WndProc+0x599
00d7f1f4 500591de 00000081 00000000 00d7f35c vcl120!Controls.TWinControl.MainWndProc+0x2f
00d7f20c 77d48709 002b00ee 00000081 00000000 rtl120!Classes.StdWndProc+0x16
00d7f238 77d4d297 028408b9 002b00ee 00000081 user32!InternalCallWinProc+0x28
00d7f2a0 77d4b368 00000000 028408b9 002b00ee user32!UserCallWinProcCheckWow+0xea
00d7f2f4 77d4e840 01c9c880 00000081 00000000 user32!DispatchClientMessage+0xa3
00d7f324 7c90eae3 00d7f334 00000060 00000060 user32!__fnINLPCREATESTRUCT+0x8b
00d7f390 77d517eb 77d517b1 00050000 00d7f8b8 ntdll!KiUserCallbackDispatcher+0x13
00d7f834 77d518a4 00050000 00d7f8b8 00d7f8cc user32!NtUserCreateWindowEx+0xc
00d7f8e0 77d51b08 00050000 00d7fa04 00d7f8cc user32!_CreateWindowEx+0x1ed
00d7f91c 50122dd0 00050000 00d7fa04 0b3413bc user32!CreateWindowExW+0x33
00d7f964 501c8b29 00000000 50120000 00000000 vcl120!Windows.CreateWindowEx+0x44
00d7faa8 501c8a47 00d7fc80 501c8ae7 00d7fbc4 vcl120!Controls.TWinControl.CreateWindowHandle+0x35
00d7fbc4 501e3682 02190e80 501e7787 00d7f9cc vcl120!Controls.TWinControl.CreateWnd+0x13f
00d7fc18 77d4d86f 000100a8 00d7fc68 00d7fcf8 vcl120!Forms.TScrollingWinControl.CreateWnd+0xa
00d7fc38 77d4d94b 00000000 00000000 501ed470 user32!InternalEnumWindows+0x5a
00d7fc58 501ed556 501ed470 00d7fc68 00450242 user32!EnumWindows+0x16
00d7fce0 500591de 0000001c 00000000 00000454 vcl120!Forms.TApplication.DoNormalizeTopMosts+0x32
00d7fcf8 77d48709 00450242 0000001c 00000000 rtl120!Classes.StdWndProc+0x16
00d7fd24 77d487eb 02840fe2 00450242 0000001c user32!InternalCallWinProc+0x28
00d7fd8c 77d4b368 00000000 02840fe2 00450242 user32!UserCallWinProcCheckWow+0x150
00d7fde0 77d4b3b4 01c8f058 0000001c 00000000 user32!DispatchClientMessage+0xa3
00d7fe08 7c90eae3 00d7fe18 00000018 01c8f058 user32!__fnDWORD+0x24
00d7fe2c 77d493c6 77d49385 00d7feac 00000000 ntdll!KiUserCallbackDispatcher+0x13
00d7fe58 77d493df 00d7feac 00000000 00000000 user32!NtUserPeekMessage+0xc
00d7fe84 500576a4 00d7feac 00000000 00000000 user32!PeekMessageW+0xbc
00d7fee8 50006c37 5002b6a9 01783c94 00d7ff08 rtl120!Classes.TThread.WaitFor+0x5c
00d7fef8 0178fc99 00000003 021d5800 021d5830 rtl120!System.TObject.Free+0xb
00d7ff08 501c7240 02190e80 021d5830 021d5830 VirtualTreesR!VirtualTrees.TBaseVirtualTree.Destroy+0x21
00d7ff1c 501cef46 00000002 00000000 501c7240 vcl120!Controls.TWinControl.Destroy+0x90
00d7ff28 501c7240 00d7ff64 02190e80 02190e80 vcl120!Controls.TCustomControl.Destroy+0x22
00000000 00000000 00000000 00000000 00000000 vcl120!Controls.TWinControl.Destroy+0x90
SYMBOL_NAME: vcl120!Forms.TGlassFrame.FrameExtended+0
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: vcl120
IMAGE_NAME: vcl120.bpl
DEBUG_FLR_IMAGE_TIMESTAMP: 4a0b8b7f
STACK_COMMAND: .ecxr ; ~~[894] ; .frame 0 ; ~0s; .ecxr ; kb
FAILURE_BUCKET_ID: NULL_CLASS_PTR_DEREFERENCE_c0000005_vcl120.bpl!Forms.TGlassFrame.FrameExtended
BUCKET_ID: APPLICATION_FAULT_NULL_CLASS_PTR_DEREFERENCE_WINDOW_HOOK_vcl120!Forms.TGlassFrame.FrameExtended+0
If you look at this line in callstack "0000001c 00000000 rtl120!Classes.StdWndProc+0x16", you can see that application processed WM_ACTIVATEAPP(0x1C) message and called "DoNormalizeTopMosts" (I think this leads to create main form again). I found in our project DevExpress6 with "dxBarWndProcHook", maybe this is a problem, but I'm not sure. I have no idea why this is happen - function TThread.WaitFor processed message which leads to recreate form.
Please, could anyone help me to solve this? Thank you in advance!