2011-11-14 8 views
10

Seguito da Why is the ELF execution entry point virtual address of the form 0x80xxxxx and not zero 0x0? e Why do virtual memory addresses for linux binaries start at 0x8048000?, perché non posso fare ld utilizzare un punto di ingresso diverso da quello predefinito con ld -e?Perché il punto di ingresso ELF 0x8048000 non è modificabile con l'opzione "ld -e"?

In tal caso, ottengo uno segmentation fault con codice di ritorno 139, anche per gli indirizzi vicini al punto di ingresso predefinito. Perché?

EDIT:

farò la domanda più specifica:

 .text 
     .globl _start  
_start: 
     movl $0x4,%eax  # eax = code for 'write' system call 
     movl $1,%ebx   # ebx = file descriptor to standard output 
     movl $message,%ecx # ecx = pointer to the message 
     movl $13,%edx   # edx = length of the message 
     int $0x80   # make the system call 
     movl $0x0,%ebx  # the status returned by 'exit' 
     movl $0x1,%eax  # eax = code for 'exit' system call 
     int $0x80   # make the system call 
     .data 
     .globl message 
message:   
     .string "Hello world\n" # The message as data 

Se compilo questo con as program.s -o program.o e poi collegarlo staticamente con ld -N program.o -o program, readelf -l program spettacoli 0x0000000000400078 come VirtAddr del testo segmento e come punto di ingresso. Quando viene eseguito, `Ciao mondo" viene stampato.

Tuttavia, quando provo a collegare con ld -N -e0x400082 -Ttext=0x400082 program.o -o program (muovendo segmento di testo e punto di ingresso da 4 byte), il programma sarà killed. Ispezionare con readelf -l ora mostra due intestazioni differenti di tipo LOAD, uno alla 0x0000000000400082 e uno a 0x00000000004000b0.

Quando provo 0x400086, tutto funziona, e c'è solo un LOAD sezione.

  1. che cosa sta succedendo qui?
  2. Quali indirizzi di memoria posso scegliere, quali non posso scegliere e perché?

Grazie.

+0

Sono stato anche in grado di modificare il punto di ingresso con lo script del linker: http://stackoverflow.com/a/30536800/895245 –

risposta

24

perché non posso fare ld utilizzare un diverso punto di ingresso da quello predefinito con ld -e

Sei sicuro possibile. Questo:

int foo(int argc, char *argv[]) { return 0; } 

gcc main.c -Wl,-e,foo 

non funziona, perché l'esecuzione non inizia al main. Inizia da _start, che è collegato da crt0.o (parte di glibc) e consente l'avvio corretto di cose come il collegamento dinamico ecc. Reindirizzando _start a foo, hai ignorato tutto ciò che richiedeva l'inizializzazione di glibc, e quindi le cose non funzionano.

Ma se non avete bisogno di collegamenti dinamici e siete disposti a fare ciò che glibc normalmente fa per voi, allora potete nominare il punto di ingresso come volete. Esempio:

#include <syscall.h> 

int foo() 
{ 
    syscall(SYS_write, 1, "Hello, world\n", 13); 
    syscall(SYS_exit, 0); 
} 

gcc t.c -static -nostartfiles -Wl,-e,foo && ./a.out 
Hello, world 

Oh, e il titolo di questa domanda non corrisponde alla tua domanda reale (cattiva idea (TM)).

Per rispondere alla domanda nel titolo, è sicuro che può modificare l'indirizzo a cui è collegato il file eseguibile. Per impostazione predefinita, viene visualizzato l'indirizzo di caricamento 0x8048000 (solo a 32 bit; l'impostazione predefinita a 64 bit è 0x400000).

Puoi facilmente modificarlo, ad es.0x80000 aggiungendo -Wl,-Ttext-segment=0x80000 alla riga di collegamento.

Aggiornamento:

Tuttavia, quando provo a collegare con ld -N -e0x400082 -Ttext = 0x400082 program.o -o programma (movimento segmento di testo e punto di ingresso da 4 byte), il programma essere ucciso.

Ebbene, è impossibile assegnare Ttext a 0x400082 senza violare .text sezione vincolo di allineamento (che è 4). È necessario mantenere l'indirizzo .text allineato sul limite di almeno 4 byte (o modificare l'allineamento richiesto di .text).

Quando si imposta l'indirizzo iniziale su 0x400078, 0x40007c, 0x400080, 0x400084, ..., 0x400098 e si utilizza GNU-ld 2.20.1, il programma funziona.

Tuttavia, quando uso corrente snapshot CVS di binutils, il programma funziona per 0x400078, 0x40007c, 0x400088, 0x40008c, e viene ucciso per 0x400080, 0x400084, 0x400090, 0x400094, 0x400098. Questo potrebbe essere un bug nel linker, o sto violando qualche altro vincolo (non vedo quale però).

A questo punto, se davvero interessati, vi consiglio di scaricare i sorgenti Binutils, costruendo ld, e cercare di capire che cosa provoca esattamente a creare due PT_LOAD segmenti invece di uno.

Aggiornamento 2:

Forza nuovo segmento per le sezioni con LMA sovrapposte.

Ah! Questo significa solo che devi spostare .data di mezzo. Questo rende un eseguibile di lavoro:

ld -N -o t t.o -e0x400080 -Ttext=0x400080 -Tdata=0x400180 
+0

Ho aggiornato la mia domanda per fare un esempio migliore di ciò che non funziona come previsto. – nh2

+0

Grazie, ottima risposta, non ho considerato l'allineamento. – nh2

+0

Ho usato git bisect per trovare il cambio tra binutils 2.20 e 2.21 che introduce la modifica che descrivi. Si chiama "elf.c (_bfd_elf_map_sections_to_segments): forza il nuovo segmento per le sezioni con LMA sovrapposti." (Http://repo.or.cz/w/binutils.git/commit/278c98e2ff1c95c8ad9579755abda467ea2bc1b4) – nh2

Problemi correlati