This question already has answers here:
How can I make GCC compile the .text section as writable in an ELF binary?
(4 answers)
Closed 8 years ago.
I need to make .text segment of an executable ELF writable.
The program i need to modify is written in C and i can compile it. Any ideas?
Thanks A lot.
For the answer below, I'm going to use this test program:
#include <stdio.h>
#include <stdlib.h>
int
main (int argc, char **argv)
{
printf ("Hello world\n");
void *m = main;
*((char *) m) = 0;
exit (0);
}
Compile with:
$ gcc -g -o test test.c
As expected:
$ gdb test
...
(gdb) run
Starting program: /home/amb/so/test
Hello world
Program received signal SIGSEGV, Segmentation fault.
0x00000000004005a2 in main (argc=1, argv=0x7fffffffe628) at test.c:9
9 *((char *)m) = 0;
(gdb)
The obvious route here is to use the -Wl flag to gcc to pass -N or (aka --omagic) to the linker, i.e. gcc ... -Wl,--omagic ..., though this may have other undesirable results (e.g. disabling shared libraries). From the man page:
-N
--omagic
Set the text and data sections to be readable and writable. Also, do not page-align the
data segment, and disable linking against shared libraries. If the output format
supports Unix style magic numbers, mark the output as "OMAGIC". Note: Although a
writable text section is allowed for PE-COFF targets, it does not conform to the format
specification published by Microsoft.
Let's give that a go:
$ gcc --static -g -Wl,--omagic -o test test.c
$ ./test
Hello world
$
That works fine, but you've lost dynamic library support.
To keep dynamic library support, and retain a writable text segment, you should be able to use:
objcopy --writable-text ...
From the man page:
--writable-text
Mark the output text as writable. This option isn't meaningful for all object file
formats.
This ought to work, but doesn't, as objdump will verify. So here's a solution that gets a bit further than --writable-text which as OP has stated in the comments does not appear to do what it says on the tin^Wmanpage.
Let's see how the sections are marked:
$ gcc -g -o test test.
$ objdump -h test | fgrep -A1 .text
12 .text 00000192 0000000000400490 0000000000400490 00000490 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
Now let's get rid of that READONLY flag:
$ objcopy --set-section-flags .text=contents,alloc,load,code test test1
$ objdump -h test1 | fgrep -A1 .text
12 .text 00000192 0000000000400490 0000000000400490 00000490 2**4
CONTENTS, ALLOC, LOAD, CODE
and now READONLY has gone, as requested.
But:
$ gdb test1
...
(gdb) run
Starting program: /home/amb/so/test1
Hello world
Program received signal SIGSEGV, Segmentation fault.
0x00000000004005a2 in main (argc=1, argv=0x7fffffffe628) at test.c:9
9 *((char *)m) = 0;
(gdb)
I suspect the issue here is that something else other than the ELF section name is making the section read-only when actually loaded. Which is probably why people are suggesting you use mprotect. Sorry not to have been more help.
Related
I'm currently trying to get into the basics regarding C-compilation without the use of an IDE.
As I only learned C- and embedded-programming with an IDE I thought it would be a good idea to learn and give me a better understanding of how the whole build process is working behind the scenes.
I mainly want to learn how to implement a complete IDEless toolchain for an STM32 controller.
So my idea was to start simple and try to understand the C-only build toolchain and its possible configurations. For this purpose I searched for tutorials and found this and this one.
I tried to follow along the first tutorial on my windows system but encountered some problems quite early that I have trouble understanding.
I created the following hello.c testfile:
#include <stdio.h>
#include <stdint.h>
int main ( void )
{
printf("Hello World!\n");
return 0;
}
First I tried the simple full compilation using gcc -o hello.exe hello.c (1.6 from the tutorial)
Everything works fine, so I decided to test the compilation steps one after the other (1.7 from the tutorial)
I called all commands in the following order:
cpp hello.c > hello.i (preprocessing) -> gcc -S hello.i (Compilation) -> as -o hello.o hello.s (Assembly) -> ld -o hello.exe hello.o (Linking)
Every step until the linking seems to work but the linker gives me the following errors:
ld: hello.o:hello.c:(.text+0xa): undefined reference to `__main' ld:
hello.o:hello.c:(.text+0x47): undefined reference to `puts' ld:
hello.o:hello.c:(.text+0x5c): undefined reference to `printf'
Did I do something wrong here? And is there a reason the ">" operator is used for preprocessing and assembling but not if I just compile using gcc -o hello.exe hello.c
Do one even use these steps seperately that often?
I read that instead of cpp hello.c > hello.i I could also use gcc -E main.c > main.i so why use the cpp command, are there any advantages?
Next I set this problem aside and tried to add includes.
For this purpose I created the following 2 files:
myFunc.c:
uint8_t myFunc( uint8_t param )
{
uint8_t retVal = 0;
retVal = param + 1;
return retVal;
}
myFunc.h
#include <stdint.h>
uint8_t myFunc( uint8_t param );
And changed the hello.c to:
#include <stdio.h>
#include <stdint.h>
#include "myFunc.h"
int main ( void )
{
uint8_t testVal = 0;
testVal = myFunc(testVal);
printf("Hello World!\n");
printf("Test Value is %d \n", testVal);
return 0;
}
I first tried the gcc -o hello.exe hello.c but get the error:
undefined reference to `myFunc' collect2.exe: error: ld returned 1 exit status
So I figured I should add the include path (even if it is the same directory).
After a short search and the help of the second site I tried gcc -Wall -v -IC:\Users\User\Desktop\C-Only_Toolchain hello.c -o hello.exe
But get the same error...
Is there something wrong with the way my include paths are added? (obviously yes)
Lastly I tried to test the GNU make command from the tutorial.
I opened the editor and inserted all contents shown in the tutorial.
As the editor saves the file as a .txt editor I tried to just delete the file extension.
The makefile looks like this:
all: hello.exe
hello.exe: hello.o
gcc -o hello.exe hello.o
hello.o: hello.c
gcc -c hello.c
clean:
rm hello.o hello.exe
But if I enter make in my console I get the error that the command "make" is written incorrectly or could not be found.
I used tab for the indentation just as the tutorial suggests but it will not even recognize that there is a makefile.
Is this because it was originally a .txt file before I deleted the extension?
I would be happy if someone could help me with my confusing regarding this rather simple issues...
Furthermore I would be very thankful if you have some good suggestions on how to get into this topic more efficiently or have some good sources to share.
Thank you in advance and stay healthy :)
Best Regards
Evox402
So, these are a lot of questions.
(In the following I use linux, so some outputs are just similar, not identical, like paths and the assembly output, but because of your usage of gcc, it's quite transferable to windows).
I called all commands in the following order: cpp hello.c > hello.i (preprocessing) -> gcc -S hello.i (Compilation) -> as -o hello.o hello.s (Assembly) -> ld -o hello.exe hello.o (Linking)
As a repetition: What are you doing here?
cpp hello.c > hello.i
You run the preprocessor over the C file. It just does a text-replace of macros/ #defines and includes files.
This looks like this. (A bit shortened as it has around 800 lines)
...Snip....
struct _IO_FILE;
typedef struct _IO_FILE FILE;
struct _IO_FILE
{
int _flags;
char *_IO_read_ptr;
char *_IO_read_end;
char *_IO_read_base;
char *_IO_write_base;
char *_IO_write_ptr;
char *_IO_write_end;
char *_IO_buf_base;
char *_IO_buf_end;
char *_IO_save_base;
char *_IO_backup_base;
char *_IO_save_end;
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
int _flags2;
__off_t _old_offset;
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
_IO_lock_t *_lock;
__off64_t _offset;
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
size_t __pad5;
int _mode;
char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;
...Snip...
extern int printf (const char *__restrict __format, ...);
...Snip...
int main ( void )
{
printf("Hello World!\n");
return 0;
}
Now all important definitions are included, so the C compiler can run.
gcc -S hello.i.
It just converts your C code to assembly. (It will look a bit different on windows)
.file "hello.c"
.text
.section .rodata
.LC0:
.string "Hello World!"
.text
.globl main
.type main, #function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
call puts#PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Debian 10.2.0-17) 10.2.0"
.section .note.GNU-stack,"",#progbits
Now you have to convert the assembly code to machine code:
as -o hello.o hello.s
This command just generates an so called object file with your code and important metadata, the linker will need.
ld -o hello.exe hello.o
Now you invoke the linker with your object file as argument and hello.exe as output file. It will look for the entry point (_start on linux-like, WinMain for example on windows, or sometimes _main).
But also the functions from the C-standard-library are missing.
But why? You don't say the linker, that you want to include it. If you invoke the linker ld as explicit as you did, you have to pass all libraries you want to include.
You have to add for example -lc to include the stdlib, and so on.
Did I do something wrong here?
You just forgot to add the C library to the libraries the linker should link with your object-file.
And is there a reason the ">" operator is used for preprocessing
> is not from cpp. It is from the shell. Try running without > hello.i. The preprocessor will just output it on the console. The > redirects to the specified file (Here hello.i).
I could also use gcc -E main.c > main.i so why use the cpp command, are there any advantages?
There is no difference. gcc calls the preprocessor internally.
Do one even use these steps seperately that often?
These steps are sometimes used in makefiles, but not as separated as you did, but often only in compiling+linking as two separate steps to reduce compile-time.
first tried the gcc -o hello.exe hello.c but get the error:
It compiles, the C compiler knows, there is at least a definition for myFunc and because of this, it emits valid assembly code.
But the linker, as soon as it resolves the references to functions, it doesn't find it and emits the error.
You have to add the myFunc.c to your commandline:
gcc -o hello.exe hello.c myFunc.c
But if I enter make in my console I get the error that the command "make" is written incorrectly or could not be found. I used tab for the indentation just as the tutorial suggests but it will not even recognize that there is a makefile. Is this because it was originally a .txt file before I deleted the extension?
You have to add the directory of make.exe to the path.
Suppose it has the path:
C:\Foo\bar\baz\make.exe
Then you add it to the path (Execute it in the commandline):
set PATH=%PATH%;C:\Foo\bar\baz
This will only work until you close the commandline, or you can set it permanently as outlined here for example.
I know that on ELF platforms, __attribute__((constructor)) uses the .ctors ELF section. Now I realized that the function attribute works with GCC on MinGW as well and I'm wondering how it is implemented.
For MinGW targets (and other COFF targets, like Cygwin) compiler just emits each constructor function address in .ctors COFF section:
$ cat c1.c
void c1() {
}
$ x86_64-w64-mingw32-gcc -c c1.c
$ objdump -x c1.o | grep ctors
# nothing
$ cat c1.c
__attribute__((constructor)) void c1() {
}
$ x86_64-w64-mingw32-gcc -c c1.c
$ objdump -x c1.o | grep ctors
5 .ctors 00000008 0000000000000000 0000000000000000 00000150 2**3
GNU ld linker (for MinGW targets) is then configured (via its default linker script) to combine these sections into regular .text section with __CTOR_LIST__ symbol pointing to the first item, and having the last item terminated with zero. (Probably .rdata section would be clearer since these are just addresses of functions, not CPU instructions, but for some reason .text is used. In fact LLVM LLD linker targeting MinGW places them in .rdata.)
LD linker:
$ x86_64-w64-mingw32-ld --verbose
...
.text ... {
...
__CTOR_LIST__ = .;
LONG (-1); LONG (-1);
KEEP (*(.ctors));
KEEP (*(.ctor));
KEEP (*(SORT_BY_NAME(.ctors.*)));
LONG (0); LONG (0);
...
...
}
Then it is up to C runtime library to run these constructors during initialization, by using this __CTOR_LIST__ symbol.
From mingw-w64 C runtime:
extern func_ptr __CTOR_LIST__[];
void __do_global_ctors (void)
{
// finds the last (zero terminated) item
...
// then runs from last to first:
for (i = nptrs; i >= 1; i--)
{
__CTOR_LIST__[i] ();
}
...
}
(also, it is very similar in Cygwin runtime)
This can be also seen in the debugger:
$ echo $MSYSTEM
MINGW64
$ cat c11.c
#include <stdio.h>
__attribute__((constructor))
void i1() {
puts("i 1");
}
int main() {
puts("main");
return 0;
}
$ gcc c11.c -o c11
$ gdb ./c11.exe
(gdb) b i1
(gdb) r
(gdb) bt
#0 0x00007ff603591548 in i1 ()
#1 0x00007ff6035915f2 in __do_global_ctors () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:44
#2 0x00007ff60359164f in __main () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/gccmain.c:58
#3 0x00007ff60359139b in __tmainCRTStartup () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:313
#4 0x00007ff6035914f6 in mainCRTStartup () at C:/_/M/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:202
(gdb)
Note that in some environments (not MinGW and not Linux) it is instead the responsibility of GCC (its compiler runtime libgcc, more specifically its static part called crtbegin.o and crtend.o) and not C runtime to run these constructors.
Also, for comparison, on ELF targets (like Linux) GCC compiler used similar mechanism like the one described above for MinGW (it used ELF .ctors sections, although the rest was a bit different), but since GCC 4.7 (released in 2012) it uses slightly different mechanism (ELF .init_array section).
I have a function in my C code that is being called implicitly, and getting dumped by the linker. how can I prevent this phenomena?
I'm compiling using gcc and the linker flag -gc-sections, and I don't want to exclude the whole file from the flag. I tried using attributes: "used" and "externally_visible" and neither has worked.
void __attribute__((section(".mySec"), nomicromips, used)) func(){
...
}
on map file I can see that the function has compiled but didn't linked. am I using it wrong? is there any other way to do it?
You are misunderstanding the used attribute
used
This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced...
i.e the compiler must emit the function definition even the function appears
to be unreferenced. The compiler will never conclude that a function is unreferenced
if it has external linkage. So in this program:
main1.c
static void foo(void){}
int main(void)
{
return 0;
}
compiled with:
$ gcc -c -O1 main1.c
No definition of foo is emitted at all:
$ nm main1.o
0000000000000000 T main
because foo is not referenced in the translation unit, is not external,
and so may be optimised out.
But in this program:
main2.c
static void __attribute__((used)) foo(void){}
int main(void)
{
return 0;
}
__attribute__((used)) compels the compiler to emit the local definition:
$ gcc -c -O1 main2.c
$ nm main2.o
0000000000000000 t foo
0000000000000001 T main
But this does nothing to inhibit the linker from discarding a section
in which foo is defined, in the presence of -gc-sections, even if foo is external, if that section is unused:
main3.c
void foo(void){}
int main(void)
{
return 0;
}
Compile with function-sections:
$ gcc -c -ffunction-sections -O1 main3.c
The global definition of foo is in the object file:
$ nm main3.o
0000000000000000 T foo
0000000000000000 T main
But after linking:
$ gcc -Wl,-gc-sections,-Map=mapfile main3.o
foo is not defined in the program:
$ nm a.out | grep foo; echo Done
Done
And the function-section defining foo was discarded:
mapfile
...
...
Discarded input sections
...
...
.text.foo 0x0000000000000000 0x1 main3.o
...
...
As per Eric Postpischil's comment, to force the linker to retain
an apparently unused function-section you must tell it to assume that the program
references the unused function, with linker option {-u|--undefined} foo:
main4.c
void __attribute__((section(".mySec"))) foo(void){}
int main(void)
{
return 0;
}
If you don't tell it that:
$ gcc -c main4.c
$ gcc -Wl,-gc-sections main4.o
$ nm a.out | grep foo; echo Done
Done
foo is not defined in the program. If you do tell it that:
$ gcc -c main4.c
$ gcc -Wl,-gc-sections,--undefined=foo main4.o
$ nm a.out | grep foo; echo Done
0000000000001191 T foo
Done
it is defined. There's no use for attribute used.
Apart from -u already mentioned here are two other ways to keep the symbol using GCC.
Create a reference to it without calling it
This approach does not require messing with linker scripts, which means it will work for hosted programs and libraries using the operating system's default linker script.
However it varies with compiler optimization settings and may not be very portable.
For example, in GCC 7.3.1 with LD 2.31.1, you can keep a function without actually calling it, by calling another function on its address, or branching on a pointer to its address.
bool function_exists(void *address) {
return (address != NULL);
}
// Somewhere reachable from main
assert(function_exists(foo));
assert(foo != NULL); // Won't work, GCC optimises out the constant expression
assert(&foo != NULL); // works on GCC 7.3.1 but not GCC 10.2.1
Another way is to create a struct containing function pointers, then you can group them all together and just check the address of the struct. I use this a lot for interrupt handlers.
Modify the linker script to keep the section
If you are developing a hosted program or a library, then it's pretty tricky to change the linker script.
Even if you do, its not very portable, for example gcc on OSX does not actually use the GNU linker since OSX uses the Mach-O format instead of ELF.
Your code already shows a custom section though, so it's possible you are working on an embedded system and can easily modify the linker script.
SECTIONS {
// ...
.mySec {
KEEP(*(.mySec));
}
}
const int hello= 0x1111;
int main(){
}
I build a really simple code, and compile it with
gcc t.c -g -o t.out
Can I use objdump or nm or any tools to make sure the const variable value?
I always find address of variable "hello", but cannot find value
Anyone can help me, thanks a lot
The example code
const int hello = 0xdeadbeef;
int main()
{
return 0;
}
compile with
gcc-4.9 -W -Wall -Wextra -pedantic -std=c11 fortests.c -o fortests
dump the content with
objdump -xDSs fortests | less
(dumped a bit too much, but costs nothing, so ... meh ...) and search for hello
0000000000400594 g O .rodata 0000000000000004 hello
That means it is in the section .rodata. We explicitly asked objdump to list the contents of all sections, so here we have it, the value 0xdeadbeef.
Contents of section .rodata:
400590 01000200 efbeadde ........
^^^^^^^^
here ||||||||
And it should be clear by now, why you had trouble to find it.
This question already has answers here:
GDB macro symbols are not present even when using -g3 or -ggdb3 or -gdwarf-4
(4 answers)
Closed 6 years ago.
Why GDB doesn't print a macro's value in the following example?
❯ cat sample.c
#include <stdio.h>
#define M 42
int main(int argc, const char **argv)
{
printf("M: %d\n", M);
return 0;
}
❯ rm -f sample
❯ gcc -Wall -g3 -ggdb -gdwarf-2 sample.c -o sample
❯ gdb sample
gdb> break main
gdb> run
gdb> info macro M
The symbol `M' has no definition as a C/C++ preprocessor macro
at <user-defined>:-1
gdb> continue
Continuing.
M: 42
Thanks!
❯ gcc --version
Apple LLVM version 7.3.0 (clang-703.0.29)
❯ gdb --version
GNU gdb (GDB) 7.10.1
I get different results with GCC 4.4.7 and GDB 7.2 than what you report. Having used your source and your compilation command, my GDB session looks like this:
> gdb sample
[ ... startup banner ... ]
(gdb) break main
Breakpoint 1 at 0x4004d3: file sample.c, line 7.
(gdb) run
Starting program: /home/jbolling/tmp/sample
Breakpoint 1, main (argc=1, argv=0x7fffffffcba8) at sample.c:7
7 printf("M: %d\n", M);
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.166.el6_7.7.x86_64
(gdb) info macro M
Defined at /home/jbolling/tmp/sample.c:3
#define M 42
(gdb) continue
Continuing.
M: 42
Program exited normally.
(gdb)
I suspect that the key difference here, and the reason that you aren't seeing a definition of M, is in GDB's sense of the source location associated with a breakpoint at function main. The GDB output you reported provides a clue about this:
gdb> info macro M
The symbol `M' has no definition as a C/C++ preprocessor macro
at <user-defined>:-1
Note in particular the location GDB reports: "<user-defined>" file, line number -1. In my GDB run, the breakpoint was associated with the first source line in the body of main(). I am inclined to believe that if you break there then GDB will report correctly on the macro's definition at that location.