Somewhere between my headers and my Makefile I'm not doing the dependencies correctly, and it's not compiling. This really only has anything to do with the first few lines from each code, but I posted all the code for reference
I'm trying to split up a who clone into 3 parts. Here is the original for reference. The exercise is to make it with utmp, so you also need utmplib
So I've split it up into 3 files, the first one being show.h
#include <stdio.h>
#include <sys/types.h>
#include <utmp.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#define SHOWHOST
void show_info(struct utmp *);
void showtime(time_t);
then I have show.c
/*
* * show info()
* * displays the contents of the utmp struct
* * in human readable form
* * * displays nothing if record has no user name
* */
void show_info( struct utmp *utbufp )
{
if ( utbufp->ut_type != USER_PROCESS )
return;
printf("%-8.8s", utbufp->ut_name); /* the logname */
printf(" "); /* a space */
printf("%-8.8s", utbufp->ut_line); /* the tty */
printf(" "); /* a space */
showtime( utbufp->ut_time ); /* display time */
#ifdef SHOWHOST
if ( utbufp->ut_host[0] != '\0' )
printf(" (%s)", utbufp->ut_host); /* the host */
#endif
printf("\n"); /* newline */
}
void showtime( time_t timeval )
/*
* * displays time in a format fit for human consumption
* * uses ctime to build a string then picks parts out of it
* * Note: %12.12s prints a string 12 chars wide and LIMITS
* * it to 12chars.
* */
{
char *ctime(); /* convert long to ascii */
char *cp; /* to hold address of time */
cp = ctime( &timeval ); /* convert time to string */
/* string looks like */
/* Mon Feb 4 00:46:40 EST 1991 */
/* 0123456789012345. */
printf("%12.12s", cp+4 ); /* pick 12 chars from pos 4 */
}
and finally, `who3.c'
/* who3.c - who with buffered reads
* - surpresses empty records
* - formats time nicely
* - buffers input (using utmplib)
*/
#include "show.h"
int main()
{
struct utmp *utbufp, /* holds pointer to next rec */
*utmp_next(); /* returns pointer to next */
if ( utmp_open( UTMP_FILE ) == -1 ){
perror(UTMP_FILE);
exit(1);
}
while ( ( utbufp = utmp_next() ) != ((struct utmp *) NULL) )
show_info( utbufp );
utmp_close( );
return 0;
}
So I created my Makefile:
who3:who3.o utmplib.o
gcc -o who who3.o utmplib.o
who3.o:who3.c show.c
gcc -c who3.c show.o
show.o:show.c
gcc -c show.c show.h
utmplib.o:utmplib.c
gcc -c utmplib.c
clean:
rm -f *.o
Unfortunately there's an error when I do make:
gcc -o who who3.o utmplib.o
who3.o: In function `main':
who3.c:(.text+0x38): undefined reference to `show_info'
collect2: error: ld returned 1 exit status
make: *** [who3] Error 1
As I said earlier, I haven't done my dependencies correctly, and I'm not sure what I did wrong. How do I do my dependencies correctly?
It looks like you are missing show.o from the dependencies and from the list of object files of the command for building who3 in your makefile.
Also, the command for who3.o looks wrong. You are compiling only -c, but you are passing an object file as input (show.o). You should remove show.o from the rule and show.c doesn't belong on the list of dependencies of who3.o either.
Also, the command for show.o looks wrong. You shouldn't be passing header files (show.h) to the compiler; they only need to be referenced as #include in the source files.
Also, you are inconsistent about what your default is actually called. You say it is who3 in the rule (who3: ...) but the command will actually build a task called who (gcc -o who ...).
Related
I am learning to write apps on ESP32 platform. When I was trying to compile my code, I got this error from linker:
undefined reference to `Serial_Init'
Where Serial_Init is a function declared in a file serial_cli.h which is in the same directory and workspace. What's more, I declared some macros there, and can use them no problem in my code, so I really don't understand where the error comes from.
Here's serial_cli.h:
#ifndef _SERIAL__H_
#define _SERIAL__H_
#include "driver/uart.h"
#define SERIAL_PORT UART_NUM_1
#define RTS_SIG_PINOUT UART_PIN_NO_CHANGE
#define CTS_SIG_PINOUT UART_PIN_NO_CHANGE
/**
* #brief This function initialises serial communication with PC
* #param uart_config_t type pointer to structure containing needed parameters
* #param int size of RX and TX buffer (in bytes)
* #param QueueHandle_t type pointer to structure containing UART queue
*/
void Serial_Init(uart_config_t*, const int, QueueHandle_t*);
#endif /* _SERIAL_H_ */
And here's serial_cli.c:
#include "serial_cli.h"
void Serial_Init(uart_config_t* uart_config, const int buffer_size, QueueHandle_t* queue)
{
ESP_ERROR_CHECK(uart_param_config(SERIAL_PORT, uart_config));
ESP_ERROR_CHECK(uart_set_pin(SERIAL_PORT, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, RTS_SIG_PINOUT, CTS_SIG_PINOUT));
ESP_ERROR_CHECK(uart_driver_install(SERIAL_PORT, buffer_size, buffer_size, 10, queue, 0));
}
And finally, main body of the app:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "serial_cli.h"
#include "string.h"
void app_main(void)
{
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
.rx_flow_ctrl_thresh = 122
};
QueueHandle_t queue;
Serial_Init(&uart_config, 1024, &queue);
char* test_str = "This is a test string.\n";
while(1) {
uart_write_bytes(SERIAL_PORT, (const char*)test_str, strlen(test_str));
vTaskDelay(500);
}
}
Below I also include complete output that I get from console:
D:\Tools\ESP_IDF\examples\get-started\blink>idf.py build
Executing action: all (aliases: build)
Running ninja in directory d:\tools\esp_idf\examples\get-started\blink\build
Executing "ninja all"...
[1/8] Performing build step for 'bootloader'
ninja: no work to do.
[5/6] Linking CXX executable blink.elf
FAILED: blink.elf
cmd.exe /C "cd . && D:\Tools\ESP_IDF_container\.espressif\tools\xtensa-esp32-elf\esp-2020r3-8.4.0\xtensa-esp32-elf\bin\xtensa-esp32-elf-g++.exe -mlongcalls -Wno-frame-address #CMakeFiles\blink.elf.rsp -o blink.elf
&& cd ."
d:/tools/esp_idf_container/.espressif/tools/xtensa-esp32-elf/esp-2020r3-8.4.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/main/libmain.a(blink.c.obj):(.literal.app_main+0x8): undefined reference to `Serial_Init'
d:/tools/esp_idf_container/.espressif/tools/xtensa-esp32-elf/esp-2020r3-8.4.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: esp-idf/main/libmain.a(blink.c.obj): in function `app_main':
d:\tools\esp_idf\examples\get-started\blink\build/../main/blink.c:38: undefined reference to `Serial_Init'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
ninja failed with exit code 1
Thanks in advance for any feedback.
You need to add "serial_cli.c" to your main/CMakeLists.txt. Something like this:
idf_component_register(
SRCS
"blink.c"
"serial_cli.c"
...
See details in ESP IDF documentation
I tried running this sample code from fm4dd.com. But I don't know how to actually include the header files into my program.
Orignally it was like:
#include <openssl/bio.h>
But i changes it to their actual path, but an error still shows up.
#include <C:\openssl\include\openssl\bio.h>
#include <C:\openssl\include\openssl\err.h>
#include <C:\openssl\include\openssl\pem.h>
#include <C:\openssl\include\openssl\x509.h>
#include <C:\openssl\include\openssl\e_os2.h>
int main() {
const char cert_filestr[] = "./cert-file.pem";
EVP_PKEY *pkey = NULL;
BIO *certbio = NULL;
BIO *outbio = NULL;
X509 *cert = NULL;
int ret;
/* ---------------------------------------------------------- *
* These function calls initialize openssl for correct work. *
* ---------------------------------------------------------- */
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
/* ---------------------------------------------------------- *
* Create the Input/Output BIO's. *
* ---------------------------------------------------------- */
certbio = BIO_new(BIO_s_file());
outbio = BIO_new_fp(stdout, BIO_NOCLOSE);
/* ---------------------------------------------------------- *
* Load the certificate from file (PEM). *
* ---------------------------------------------------------- */
ret = BIO_read_filename(certbio, cert_filestr);
if (! (cert = PEM_read_bio_X509(certbio, NULL, 0, NULL))) {
BIO_printf(outbio, "Error loading cert into memory\n");
exit(-1);
}
/* ---------------------------------------------------------- *
* Extract the certificate's public key data. *
* ---------------------------------------------------------- */
if ((pkey = X509_get_pubkey(cert)) == NULL)
BIO_printf(outbio, "Error getting public key from certificate");
/* ---------------------------------------------------------- *
* Print the public key information and the key in PEM format *
* ---------------------------------------------------------- */
/* display the key type and size here */
if (pkey) {
switch (pkey->type) {
case EVP_PKEY_RSA:
BIO_printf(outbio, "%d bit RSA Key\n\n", EVP_PKEY_bits(pkey));
break;
case EVP_PKEY_DSA:
BIO_printf(outbio, "%d bit DSA Key\n\n", EVP_PKEY_bits(pkey));
break;
default:
BIO_printf(outbio, "%d bit non-RSA/DSA Key\n\n", EVP_PKEY_bits(pkey));
break;
}
}
if(!PEM_write_bio_PUBKEY(outbio, pkey))
BIO_printf(outbio, "Error writing public key data in PEM format");
EVP_PKEY_free(pkey);
X509_free(cert);
BIO_free_all(certbio);
BIO_free_all(outbio);
exit(0);
}
but the following error shows up every time I try to compile it on the command prompt. Since, I'm a noob, I have no clue how to proceed from here and what to do to fix this error.
c:\openssl>gcc -lssl -lcrypto -o test test.c
In file included from test.c:1:0:
C:\openssl\include\openssl\bio.h:62:27: fatal error: openssl/e_os2.h: No such file or directory
#include <openssl/e_os2.h>
^
compilation terminated.
Edit:
I included the solution to the problem, but now a new error showed up:
c:\openssl>gcc -lssl -lcrypto -o test test.c -IC:\openssl\include\
c:/mingw/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: cannot find -lssl
c:/mingw/bin/../lib/gcc/mingw32/4.8.1/../../../../mingw32/bin/ld.exe: cannot find -lcrypto
collect2.exe: error: ld returned 1 exit status
In many cases, include-files in turn include other files. The paths of these files are specified relative, not absolute. So you have to tell your compiler, where to search for include files in general.
The -I-option is for this purpose and tells the compiler, which paths (additionally to some standard paths) are to be searched for specified include files, in your case you would use:
gcc -I C:\openssl\include
If you really need to specify an absolute include path you would use quotes, not <>, i.e.
#include "C:\foo\bar\baz.h"
but if this file includes other files, the compiler will not look specifically into C:\foo\bar for these.
Get rid of the full path names in your #include directives. That is, don't use #include <C:\openssl\include\openssl\bio.h>; rather, use:
#include <openssl\bio.h>
#include <openssl\err.h>
#include <openssl\pem.h>
#include <openssl\x509.h>
#include <openssl\e_os2.h>
And pass the include directory to gcc with -I:
gcc -I c:\openssl\include -o myfile myfile.c -lcrypto
I am trying to use two .c files together. I am lost at how to do this, I have a simple setup for each file but I get a undefined reference to format_lines error when I try to compile. Any help would be muchly appreciated;
formatter.h
#ifndef _FORMATTER_H_
#define _FORMATTER_H_
#include <stdio.h>
char **format_file(FILE *);
char **format_lines(char **, int);
void test();
#endif
formatter.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "formatter.h"
char **format_file(FILE *infile) {
return NULL;
}
char **format_lines(char **lines, int num_lines) {
char **result = NULL;
#ifdef DEBUG
result = (char **)malloc(sizeof(char *) * 2);
if (result == NULL) {
return NULL;
}
result[0] = (char *)malloc(sizeof(char) * 80);
if (result[0] == NULL) {
return NULL;
}
strncpy(result[0], "(machine-like voice) EXTERMINATE THEM!", 79);
result[1] = (char *)malloc(sizeof(char) * 2);
if (result[1] == NULL) {
return NULL;
}
result[1][0] = '\0';
#endif
}
void test(){
print("here");
}
and sengfmt.c
#include <stdio.h>
#include <stdlib.h>
#include "formatter.h"
int main(int argc, char *argv[]) {
test();
#ifdef DEBUG
printf("%s does nothing right now.\n", argv[0]);
#endif
exit(0);
}
When I try to compile, I just type this.
$ gcc sengfmt3.c
/tmp/cc7Ttgne.o: In function `main':
sengfmt3.c:(.text+0x15): undefined reference to `test'
collect2: ld returned 1 exit status
I suspect that your main used to try to call format_lines
You need to do this
gcc formatter.c sendgfmt.c -o myprog
You must list all the c files that you want compiled together
If you have code in multiple source files, then you need to build with all the source files.
There are two ways of doing this:
Compile and link all source files using using one command:
$ gcc sengfmt3.c someOtherSourceFile.c someThirdSourceFile.c
First make object files of all source files, and then link the object files together. This is more work, but if you have a makefile or other build-system it will be better since only the modified source files will be recompiled, and might save you some build-time:
$ gcc -c sengfmt3.c
$ gcc -c someOtherSourceFile.c
$ gcc -c someThirdSourceFile.c
$ gcc sengfmt.o someOtherSourceFile.o someThirdSorceFile.o
Note the command-line option -c for the compilation, this tells GCC to generate object files. Also note that for the linking command (the last one) the file extensions have changed from .c to .o.
The command in point 1 does this internally, using temporary files which are removed when done.
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
I'm learning how to embed Lua into C, and start with a simple example:
demo.c
#include <stdio.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int main (void) {
char buff[256];
int error;
lua_State *L = luaL_newstate(); /* opens Lua */
luaopen_base(L); /* opens the basic library */
luaopen_table(L); /* opens the table library */
luaopen_io(L); /* opens the I/O library */
luaopen_string(L); /* opens the string lib. */
luaopen_math(L); /* opens the math lib. */
while (fgets(buff, sizeof(buff), stdin) != NULL) {
error = luaL_loadbuffer(L, buff, strlen(buff), "line") ||
lua_pcall(L, 0, 0, 0);
if (error) {
fprintf(stderr, "%s", lua_tostring(L, -1));
lua_pop(L, 1); /* pop error message from the stack */
}
}
lua_close(L);
return 0;
}
======
My Local environment:
evans#master:~/codebase/demo/lua$ sudo dpkg -L liblua5.2-dev
/.
/usr
/usr/include
/usr/include/lua5.2
/usr/include/lua5.2/lua.h
/usr/include/lua5.2/luaconf.h
/usr/include/lua5.2/lualib.h
/usr/include/lua5.2/lauxlib.h
/usr/include/lua5.2/lua.hpp
/usr/lib
/usr/lib/i386-linux-gnu
/usr/lib/i386-linux-gnu/liblua5.2.a
/usr/lib/i386-linux-gnu/pkgconfig
/usr/lib/i386-linux-gnu/pkgconfig/lua5.2.pc
/usr/share
/usr/share/doc
/usr/share/doc/liblua5.2-dev
/usr/share/doc/liblua5.2-dev/copyright
/usr/lib/i386-linux-gnu/liblua5.2.so
Then:
gcc -o demo demo.c -llua5.2
demo.c:3:17: fatal error: lua.h: No such file or directory
compilation terminated.
I also tried -llua5, -llua and all failed.
======
Finally I found a solution:
gcc -o demo demo.c -I/usr/include/lua5.2 /usr/lib/i386-linux-gnu/liblua5.2.a -lm
But I couldn't figure out why I cannot do that as I usual.
You will need to either specify the actual path to the header file:
#include <lua5.2/lua.h>
or use -I/usr/include/lua5.2, like you already figured out. When you attempt to include <lua.h>, the compiler only looks for it at /usr/include/lua.h (and a few other places that don't matter here).
Copy all your files from /usr/include/lua*.* to /usr/include/