Can not reallocate memory in a very peculiar circumstance - c

Almost minimal reproducible example:
prog.c
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *buffer;
int c;
size_t bufsiz = 1024, i = 0;
if (!(buffer = malloc(bufsiz))) {
fputs("malloc() failed!\n", stderr);
return 1;
}
while (EOF != (c = fgetc(stdin))) {
buffer[i] = c;
if (++i == bufsiz && !(buffer = realloc(buffer, bufsiz *= 2))) {
fputs("realloc() failed! (loop)\n", stderr);
return 1;
}
}
buffer[i] = '\0';
if (!(buffer = realloc(buffer, i))) {
fputs("realloc() failed! ", stderr);
fprintf(stderr, "%d\n", i);
return 1;
}
fputs(buffer, stdout);
return 0;
}
I use this command to compile and run:
gcc prog.c -o prog
This command copies the content of prog.c to exp as expected:
cat prog.c | ./prog > exp
This command prints the error message “realloc() failed! 0”:
cat prog.c | ./prog > prog.c
I have yet to find out the reason behind this peculiar behavior...
P.S.: I am using GNU cat and bash

Congratulations, you've (re-)discovered a bug in your system's implementation of realloc, whereby "success" resizing to 0 is indistinguishable from an error. As specified, if realloc returns a null pointer, it has failed and the old object still exists. But some historical implementations treat realloc(p,0) as free(p). Future versions of the C standard allow for this behavior, and deprecate the use of realloc with a zero size, so if you want to use realloc like this you should make sure you are not passing a zero size.
As noted by Eric Postpischil in a comment:
Where does your program put a null character at the end of the string in the buffer?
the fact that 0 is a possible size for your buffer is indicative of a problem - you forgot to reserve space for terminating the string - and if you fix this, even a zero-length string takes a nonzero number of bytes.

In cat prog.c | ./prog > prog.c, the shell parses the command, sees there is a redirection into prog.c, and opens prog.c for writing, which erases any previous contents of the file. Then cat prog.c sees an empty file and copies it to standard output. ./prog faithfully reproduces this empty stream.

Related

how to run my own C program that prints to std out?

I only used C 2-3 times. Following hello world tutorial did not help. the function should just print to std out console.
#include <stdio.h>
void my_putstr(char* param_1) {
char *t ;
for (t = param_1; *t != '\0'; t++) {
printf("%s", t);
}
}
int main(){
my_putstr("abc");
return 0;
}
How to run this program? I do have main to call & test my putstr function.
I do this:
gcc file.c -o file
gcc file
But it still gives me the error of "main":
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
I do have the main function. What's wrong?
gcc file.c -o file
gcc file
That second line will try to compile the executable file that you created with the first line and, since it's not C source(a), that won't end too well :-)
You need to run the file with something like:
./file
And, just as an aside, you should strive to make your programs more readable, such as with:
#include <stdio.h>
// my_putstr:
// Output the given string multiple times, each time starting
// at the next character. So, for "1234", it would output
// "1234 234 34 4" (without the spaces).
void my_putstr(char *str) {
// Start at position 0, 1, m2, etc until no more string left.
for (char *ptr = str; *ptr != '\0'; ptr++) {
printf("%s", ptr);
}
}
int main(void) {
my_putstr("abc");
return 0;
}
Changes made:
Comments are quite handy if you ever come back to the code after some time;
You should try to avoid simple variable names, use names that make the intent clear (about the only exception are simple i, j, k loop variables;
The two canonical forms of main are int main(int argc, char **argv) (though the "or equivalent" phrase in the standard also allows for int main(int argc, char *argv[])) or int main(void), you should try to stick with them.
By the way, the description in the comments above is an accurate representation of the way the code works. If, instead, you just want to output a string (i.e., not the 1234 234 34 4 behaviour), you're probably better off with something like:
void my_putstr(char *str) {
// Output each character, one at a time.
for (char *ptr = str; *ptr != '\0'; ptr++)
putchar(*ptr);
// Output newline (if desired).
putchar('\n');
}
(a) The gcc program is quite capable of taking other input file types (like object files, assembler files, and so on) but I'm not sure finished executables are one of those types.

Not sure why optimizer is optimizing my method incorrectly

I'm new to C (so there's every good chance I'm doing something fundamentally wrong or bad), and working on a kattis problem, https://open.kattis.com/problems/abc.
I'm running in to fun with what appears to be the GCC optimiser and I'm trying to understand why or what I'm doing wrong. Here's the simplest replication case I can come up with:
#include <stdio.h>
#include <string.h>
int lookup(char input){
// This will return 2 on invalid input. Not ideal, but we don't have to worry about invalid inputs like that
fprintf(stderr, "Asked to compare: %s\n", &input);
if (strcmp("A", &input) == 0){
//fprintf(stderr, "It's an A\n");
return 0;
} else if (strcmp("B", &input) == 0){
//fprintf(stderr, "It's a B\n");
return 1;
} else {
return 2;
};
}
int main(void){
char input[3];
if (scanf("%3s", input) != 1){
fprintf(stderr, "Something went wrong reading input\n");
return -1;
};
printf("Desired Order: %s\n", input);
printf("%d %d %d\n", lookup(input[0]),
lookup(input[1]),
lookup(input[2])
);
}
An example input file for triggering this bug has a single line:
CAB
If I compile with without optimisations:
$ cc -std=gnu11 -static -lm simple.c -o simple && cat input | ./simple
Desired Order: CAB
Asked to compare: B
Asked to compare: A
Asked to compare: C
2 0 1
With optimisations enabled, note it's falling through to the else section:
$ cc -O2 -std=gnu11 -static -lm simple.c -o simple && cat input | ./simple
Desired Order: CAB
Asked to compare: B
Asked to compare: A
Asked to compare: C
2 2 2
Things I've noticed so far:
If I uncomment the fprintf after the strcmp("A", &input), the code works fine even with optimisations enabled (that's what got me wondering about optimisations breaking my code in the first place).
If I uncomment the fprintf after the strcmp("B", &input) the code fails when optimised.
If I comment out the "Desired Order:" printf, the code works fine even with optimisations.
char input[3];
but you wrote three characters to it. Need 1 more for null terminator.
char input[4];
And here we have
fprintf(stderr, "Asked to compare: %s\n", &input);
No. That's a character not a string so we want
fprintf(stderr, "Asked to compare: %c\n", input);
and we also have
strcmp("A", &input) == 0
but input is a char so we want
'A' == input
and the same for 'B'

Not matches for working regex in c

I want to match the regex (?<=SEARCH_THIS=").+(?<!"\n) in C with PCRE.
However, the following code doesn't work as expected.
#include <pcreposix.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(void){
regex_t re;
regmatch_t matches[2];
char *regex = "(?<=SEARCH_THIS=\").+(?<!\"\n)";
char *file = "NO_MATCH=\"0\"\nSOMETHING_ELSE=\"1\"\nSOME_STUFF=\"1\"\nSEARCH_THIS=\"gimme that\"\nNOT_THIS=\"foobar\"\nTHIS_NEITHER=\"test\"\n";
puts("compiling regex");
int compErr = regcomp(&re, regex, REG_NOSUB | REG_EXTENDED);
if(compErr != 0){
char buffer[128];
regerror(compErr, &re, buffer, 100);
printf("regcomp failed: %s\n", buffer);
return 0;
}
puts("executing regex");
int err = regexec(&re, file, 2, matches, 0);
if(err == 0){
puts("no error");
printf("heres the match: [.%*s]",matches[0].rm_eo-matches[0].rm_so,file+matches[0].rm_so);
} else {
puts("some error here!");
char buffer[128];
regerror(err, &re, buffer, 100);
printf("regexec failed: %s\n", buffer);
}
return 0;
}
The console output is:
compiling regex
executing regex
some error here!
regexec failed: No match
I verified the functionality of this regex here
Any idea what is going wrong here?
EDIT #1
Compiler Version
$ arm-merlin-linux-uclibc-gcc --version
arm-merlin-linux-uclibc-gcc (GCC) 4.2.1
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Compile Command
$ arm-merlin-linux-uclibc-gcc -lpcre ./re_test.c -o re_test.o
There are actually a few issues with your code.
First, you use %*s in an attempt to restrict the length of the printed string. However, the integer width before the s formatter is the minimum length of what gets printed; if the corresponding string's length is less than what's given, it'll be padded with spaces. If the length is greater than what's given, it'll just output the whole string. You'll need some other method of restricting the length of the outputted string (just avoid modifying *file, because file points to a constant string).
Second, you specify the REG_NOSUB option in your regcomp call, but according to the man page, this means that no substring positions are stored in the pmatch argument - thus, even if your regexec did work, the following printf would be using uninitialized values (which is undefined behavior).
Finally, I suspect the problem is that the \" and \n characters need to be doubly-escaped; i.e. you need to use \\\" and \\n in your regex string. While the code you gave worked for me (Ubuntu 14.04 x64), the doubly-escaped version also works.
Taking all of this into account, this is the output I get:
compiling regex
executing regex
no error
heres the match: [.gimme that"]

C - cs50.h GetString error

Hello I am completely new to the world of programming an I am attempting to take Harvard's CS50 course online.
While making my "Hello World" program, I downloaded 'cs50.h' to define GetString and string (at least I think). So this is the code I wrote:
file.c:
#include "cs50.h"
#include <stdio.h>
int main(int argc, string argv[])
{
string name;
printf("Enter your name: ");
name = GetString();
printf("Hello, %s\n", name);
}
However, whenever I try to make file, this happens:
cc file.c -o file
Undefined symbols for architecture x86_64:
"_GetString", referenced from:
_main in file-JvqYUC.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [file] Error 1
Here is a link to the cs50.h file if it can help: http://dkui3cmikz357.cloudfront.net/library50/c/cs50-library-c-3.0/cs50.h
I would like to know why I get this error and how I can fix it. Please help.
It seems that you forgot to download and link to project cs50.c file from http://dkui3cmikz357.cloudfront.net/library50/c/cs50-library-c-3.0/cs50.c
*.h usually contain only declarations. *.c (for C) and *.cpp (for C++) contains implementations.
There is GetSting function implementation from this class:
string GetString(void)
{
// growable buffer for chars
string buffer = NULL;
// capacity of buffer
unsigned int capacity = 0;
// number of chars actually in buffer
unsigned int n = 0;
// character read or EOF
int c;
// iteratively get chars from standard input
while ((c = fgetc(stdin)) != '\n' && c != EOF)
{
// grow buffer if necessary
if (n + 1 > capacity)
{
// determine new capacity: start at 32 then double
if (capacity == 0)
capacity = 32;
else if (capacity <= (UINT_MAX / 2))
capacity *= 2;
else
{
free(buffer);
return NULL;
}
// extend buffer's capacity
string temp = realloc(buffer, capacity * sizeof(char));
if (temp == NULL)
{
free(buffer);
return NULL;
}
buffer = temp;
}
// append current character to buffer
buffer[n++] = c;
}
// return NULL if user provided no input
if (n == 0 && c == EOF)
return NULL;
// minimize buffer
string minimal = malloc((n + 1) * sizeof(char));
strncpy(minimal, buffer, n);
free(buffer);
// terminate string
minimal[n] = '\0';
// return string
return minimal;
}
Look at your first include statement. You are using " " instead of < >.
In the videos with the CS50 course, the instructor uses carets (< >) rather than quotation marks (" ").
For anyone taking the CS50 class, and don't want to paste the .c code every time, you can also link the CS50 code when compiling.
Place cs50.h and cs50.c in the same directory as file.c, and then type the following in the command line:
clang file.c -lcs50 -o <file name>
The "-l" links the cs50.c and cs50.h files to your c file (after compiling to object file), and "-o" specifies where to put the compiled output.
More information on this here
In your #include"cs50.h" header you should be typing it like this: #include<cs50.h>. Also, try doing:
#include<cs50.h>
#include<stdio.h>
int main(void)
{
string name = get_string("Enter your name: ");
printf("%s\n", name);
}
Instead of this:
#include "cs50.h"
#include <stdio.h>
int main(int argc, string argv[])
{
string name;
printf("Enter your name: ");
name = GetString();
printf("Hello, %s\n", name);
}
That should get rid of the error messages.
P.S
In week 2 they tell you about help50, but if you want you can use it now.
I myself have found it very useful. Here's how it works: in your terminal window(the one where you execute ./hello and clang) you should type : "help50 make hello" (without the quotation marks) and then it will type: asking for help... in yellow. Then it will decipher the error message and type it in a more simple language. For example:
#include <stdio.h>
#include <cs50.h>
int main(void)
{
string name = get_string("Enter your name: ");
printf("%s\n", name)
}
I do make hello, and this appears:
clang -ggdb3 -O0 -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow hello.c -lcrypt -lcs50 -lm -o hello
hello.c:13:21: error: expected ';' after expression
printf("%s\n", name)
^
;
1 error generated.
<builtin>: recipe for target 'hello' failed
make: *** [hello] Error 1
But when I do it with help50 make hello, this appears:
clang -ggdb3 -O0 -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow hello.c -lcrypt -lcs50 -lm -o hello
hello.c:13:21: error: expected ';' after expression
printf("%s\n", name)
^
;
1 error generated.
<builtin>: recipe for target 'hello' failed
make: *** [hello] Error 1
Asking for help...
hello.c:13:21: error: expected ';' after expression
printf("%s\n", name)
^
;
Are you missing a semicolon at the end of line 13 of hello.c?
As you can see, now I know my problem and can fix it. Help50 deciphers the error messages into a language you can understand.

Modifying Linker Script to make the .text section writable, errors

I am trying to make the .text section writable for a C program. I looked through the options provided in this SO question and zeroed on modifying the linker script to achieve this.
For this I created a writable memory region using
MEMORY { rwx (wx) : ORIGIN = 0x400000, LENGTH = 256K}
and at the section .text added:
.text :
{
*(.text.unlikely .text.*_unlikely)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em. */
*(.gnu.warning)
} >rwx
On compiling the code with gcc flag -T and giving my linker file as an argument I am getting an error:
error: no memory region specified for loadable section '.interp'
I am only trying to change the memory permissions for the .text region. Working on Ubuntu x86_64 architecture.
Is there a better way to do this?
Any help is highly appreciated.
Thanks
The Linker Script
Linker Script on pastie.org
In Linux, you can use mprotect() to enable/disable text section write protection from the runtime code; see the Notes section in man 2 mprotect.
Here is a real-world example. First, however, a caveat:
I consider this just a proof of concept implementation, and not something I'd ever use in a real world application. It may look enticing for use in a high-performance library of some sort, but in my experience, changing the API (or the paradigm/approach) of the library usually yields much better results -- and fewer hard-to-debug bugs.
Consider the following six files:
foo1.c:
int foo1(const int a, const int b) { return a*a - 2*a*b + b*b; }
foo2.c:
int foo2(const int a, const int b) { return a*a + b*b; }
foo.h.header:
#ifndef FOO_H
#define FOO_H
extern int foo1(const int a, const int b);
extern int foo2(const int a, const int b);
foo.h.footer:
#endif /* FOO_H */
main.c:
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include "foo.h"
int text_copy(const void *const target,
const void *const source,
const size_t length)
{
const long page = sysconf(_SC_PAGESIZE);
void *start = (char *)target - ((long)target % page);
size_t bytes = length + (size_t)((long)target % page);
/* Verify sane page size. */
if (page < 1L)
return errno = ENOTSUP;
/* Although length should not need to be a multiple of page size,
* adjust it up if need be. */
if (bytes % (size_t)page)
bytes = bytes + (size_t)page - (bytes % (size_t)page);
/* Disable write protect on target pages. */
if (mprotect(start, bytes, PROT_READ | PROT_WRITE | PROT_EXEC))
return errno;
/* Copy code.
* Note: if the target code is being executed, we're in trouble;
* this offers no atomicity guarantees, so other threads may
* end up executing some combination of old/new code.
*/
memcpy((void *)target, (const void *)source, length);
/* Re-enable write protect on target pages. */
if (mprotect(start, bytes, PROT_READ | PROT_EXEC))
return errno;
/* Success. */
return 0;
}
int main(void)
{
printf("foo1(): %d bytes at %p\n", foo1_SIZE, foo1_ADDR);
printf("foo2(): %d bytes at %p\n", foo2_SIZE, foo2_ADDR);
printf("foo1(3, 5): %d\n", foo1(3, 5));
printf("foo2(3, 5): %d\n", foo2(3, 5));
if (foo2_SIZE < foo1_SIZE) {
printf("Replacing foo1() with foo2(): ");
if (text_copy(foo1_ADDR, foo2_ADDR, foo2_SIZE)) {
printf("%s.\n", strerror(errno));
return 1;
}
printf("Done.\n");
} else {
printf("Replacing foo2() with foo1(): ");
if (text_copy(foo2_ADDR, foo1_ADDR, foo1_SIZE)) {
printf("%s.\n", strerror(errno));
return 1;
}
printf("Done.\n");
}
printf("foo1(3, 5): %d\n", foo1(3, 5));
printf("foo2(3, 5): %d\n", foo2(3, 5));
return 0;
}
function-info.bash:
#!/bin/bash
addr_prefix=""
addr_suffix="_ADDR"
size_prefix=""
size_suffix="_SIZE"
export LANG=C
export LC_ALL=C
nm -S "$#" | while read addr size kind name dummy ; do
[ -n "$addr" ] || continue
[ -n "$size" ] || continue
[ -z "$dummy" ] || continue
[ "$kind" = "T" ] || continue
[ "$name" != "${name#[A-Za-z]}" ] || continue
printf '#define %s ((void *)0x%sL)\n' "$addr_prefix$name$addr_suffix" "$addr"
printf '#define %s %d\n' "$size_prefix$name$size_suffix" "0x$size"
done || exit $?
Remember to make it executable using chmod u+x ./function-info.bash
First, compile the sources using valid sizes but invalid addresses:
gcc -W -Wall -O3 -c foo1.c
gcc -W -Wall -O3 -c foo2.c
( cat foo.h.header ; ./function-info.bash foo1.o foo2.o ; cat foo.h.footer) > foo.h
gcc -W -Wall -O3 -c main.c
The sizes are correct but the addresses are not, because the code is yet to be linked. Relative to the final binary, the object file contents are usually relocated at link time. So, link the sources to get example executable, example:
gcc -W -Wall -O3 main.o foo1.o foo2.o -o example
Extract the correct (sizes and) addresses:
( cat foo.h.header ; ./function-info.bash example ; cat foo.h.footer) > foo.h
Recompile and link,
gcc -W -Wall -O3 -c main.c
gcc -W -Wall -O3 foo1.o foo2.o main.o -o example
and verify that the constants now do match:
mv -f foo.h foo.h.used
( cat foo.h.header ; ./function-info.bash example ; cat foo.h.footer) > foo.h
cmp -s foo.h foo.h.used && echo "Done." || echo "Recompile and relink."
Due to high optimization (-O3) the code that utilizes the constants may change size, requiring a yet another recompile-relink. If the last line outputs "Recompile and relink", just repeat the last two steps, i.e. five lines.
(Note that since foo1.c and foo2.c do not use the constants in foo.h, they obviously do not need to be recompiled.)
On x86_64 (GCC-4.6.3-1ubuntu5), running ./example outputs
foo1(): 21 bytes at 0x400820
foo2(): 10 bytes at 0x400840
foo1(3, 5): 4
foo2(3, 5): 34
Replacing foo1() with foo2(): Done.
foo1(3, 5): 34
foo2(3, 5): 34
which shows that the foo1() function indeed was replaced. Note that the longer function is always replaced with the shorter one, because we must not overwrite any code outside the two functions.
You can modify the two functions to verify this; just remember to repeat the entire procedure (so that you use the correct _SIZE and _ADDR constants in main()).
Just for giggles, here is the generated foo.h for the above:
#ifndef FOO_H
#define FOO_H
extern int foo1(const int a, const int b);
extern int foo2(const int a, const int b);
#define foo1_ADDR ((void *)0x0000000000400820L)
#define foo1_SIZE 21
#define foo2_ADDR ((void *)0x0000000000400840L)
#define foo2_SIZE 10
#define main_ADDR ((void *)0x0000000000400610L)
#define main_SIZE 291
#define text_copy_ADDR ((void *)0x0000000000400850L)
#define text_copy_SIZE 226
#endif /* FOO_H */
You might wish to use a smarter scriptlet, say an awk one that uses nm -S to obtain all function names, addresses, and sizes, and in the header file replaces only the values of existing definitions, to generate your header file. I'd use a Makefile and some helper scripts.
Further notes:
The function code is copied as-is, no relocation etc. is done. (This means that if the machine code of the replacement function contains absolute jumps, the execution continues in the original code. These example functions were chosen, because they're unlikely to have absolute jumps in them. Run objdump -d foo1.o foo2.o to verify from the assembly.)
That is irrelevant if you use the example just to investigate how to modify executable code within the running process. However, if you build runtime-function-replacing schemes on top of this example, you may need to use position independent code for the replaced code (see the GCC manual for relevant options for your architecture) or do your own relocation.
If another thread or signal handler executes the code being modified, you're in serious trouble. You get undefined results. Unfortunately, some libraries start extra threads, which may not block all possible signals, so be extra careful when modifying code that might be run by a signal handler.
Do not assume the compiler compiles the code in a specific way or uses a specific organization. My example uses separate compilation units, to avoid the cases where the compiler might share code between similar functions.
Also, it examines the final executable binary directly, to obtain the sizes and addresses to be modified to modify an entire function implementation. All verifications should be done on the object files or final executable, and disassembly, instead of just looking at the C code.
Putting any code that relies on the address and size constants into a separate compilation unit makes it easier and faster to recompile and relink the binary. (You only need to recompile the code that uses the constants directly, and you can even use less optimization for that code, to eliminate extra recompile-relink cycles, without impacting the overall code quality.)
In my main.c, both the address and length supplied to mprotect() are page-aligned (based on the user parameters). The documents say only the address has to be. Since protections are page-granular, making sure the length is a multiple of the page size does not hurt.
You can read and parse /proc/self/maps (which is a kernel-generated pseudofile; see man 5 proc, /proc/[pid]/maps section, for further info) to obtain the existing mappings and their protections for the current process.
In any case, if you have any questions, I'd be happy to try and clarify the above.
Addendum:
It turns out that using the GNU extension dl_iterate_phdr() you can enable/disable write protection on all text sections trivially:
#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <link.h>
static int do_write_protect_text(struct dl_phdr_info *info, size_t size, void *data)
{
const int protect = (data) ? PROT_READ | PROT_EXEC : PROT_READ | PROT_WRITE | PROT_EXEC;
size_t page;
size_t i;
page = sysconf(_SC_PAGESIZE);
if (size < sizeof (struct dl_phdr_info))
return ENOTSUP;
/* Ignore libraries. */
if (info->dlpi_name && info->dlpi_name[0] != '\0')
return 0;
/* Loop over each header. */
for (i = 0; i < (size_t)info->dlpi_phnum; i++)
if ((info->dlpi_phdr[i].p_flags & PF_X)) {
size_t ptr = (size_t)info->dlpi_phdr[i].p_vaddr;
size_t len = (size_t)info->dlpi_phdr[i].p_memsz;
/* Start at the beginning of the relevant page, */
if (ptr % page) {
len += ptr % page;
ptr -= ptr % page;
}
/* and use full pages. */
if (len % page)
len += page - (len % page);
/* Change protections. Ignore unmapped sections. */
if (mprotect((void *)ptr, len, protect))
if (errno != ENOMEM)
return errno;
}
return 0;
}
int write_protect_text(int protect)
{
int result;
result = dl_iterate_phdr(do_write_protect_text, (void *)(long)protect);
if (result)
errno = result;
return result;
}
Here is an example program you can use to test the above write_protect_text() function:
#define _POSIX_C_SOURCE 200809L
int dump_smaps(void)
{
FILE *in;
char *line = NULL;
size_t size = 0;
in = fopen("/proc/self/smaps", "r");
if (!in)
return errno;
while (getline(&line, &size, in) > (ssize_t)0)
if ((line[0] >= '0' && line[0] <= '9') ||
(line[0] >= 'a' && line[0] <= 'f'))
fputs(line, stdout);
free(line);
if (!feof(in) || ferror(in)) {
fclose(in);
return errno = EIO;
}
if (fclose(in))
return errno = EIO;
return 0;
}
int main(void)
{
printf("Initial mappings:\n");
dump_smaps();
if (write_protect_text(0)) {
fprintf(stderr, "Cannot disable write protection on text sections: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("\nMappings with write protect disabled:\n");
dump_smaps();
if (write_protect_text(1)) {
fprintf(stderr, "Cannot enable write protection on text sections: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("\nMappings with write protect enabled:\n");
dump_smaps();
return EXIT_SUCCESS;
}
The example program dumps /proc/self/smaps before and after changing the text section write protection, showing that it indeed does enable/disable write protectio on all text sections (program code). It does not try to alter write protect on dynamically-loaded libraries. This was tested to work on x86-64 using Ubuntu 3.8.0-35-generic kernel.
If you just want to have one executable with a writable .text, you can just link with -N
At least for me, binutils 2.22 , ld -N objectfile.o
will produce a binary that i can happily write around in.
Reading gcc pages, you can pass the linker option from gcc by : gcc -XN source

Resources