This is a minimal linker script that can be used to build images without any of the Altera initialisation or library code. I think it works - but is a cut down copy of one that does work so might have typos.
I put the code in 0..64k (but avoiding 0), data in 64k..128k, and io just above 128k. %gp is set to cover data and io (so data starts a bit higher up).
The low 256 bytes of data memory are used as the stack, the very bottom will be used to save the registers on any exception. I don't actually use any stack - except that gcc insists on saving registers on entry to a function marked 'noreturn'.
OUTPUT_FORMAT("elf32-littlenios2")
OUTPUT_ARCH(nios2)
MEMORY
{
code (x) : ORIGIN = 0x4000, LENGTH = 8k
data (rw) : ORIGIN = 0x14000, LENGTH = 8k
}
/* Memory (and some io) from 0x14000 to 0x23fff is accessible from %gp */
_gp = 128k - 16k;
SECTIONS
{
/* code */
code : {
/* Set %gp then %sp and %et from %gp */
LONG(26 << 22 | ((_gp + 32768) >> 16) << 6 | 0x34)
LONG(26 << 27 | 26 << 22 | (_gp & 0xffff) << 6 | 0x4)
LONG(26 << 27 | 27 << 22 | ((stack_top - _gp) & 0xffff) << 6 | 0x4)
LONG(26 << 27 | 24 << 22 | ((reg_save - _gp) & 0xffff) << 6 | 0x4)
LONG(c_code << 4 | 1) /* jmpi c_code */
. = 0x20;
/* Save all registers relative to %et */
LONG(24 << 27 | ((1 << 22 | 4 << 6) * 0) | 0x15)
/* Repeat changing reg number - total 32 lines until */
LONG(24 << 27 | ((1 << 22 | 4 << 6) * 31) | 0x15)
/* Save control registers 7 (exception cause) and 12 (badaddr) via r2
* as registers 32 and 33. */
LONG(2 << 17 | 0x26 << 11 | 7 << 6 | 0x3a)
LONG(24 << 27 | 2 << 22 | (4 * 32) << 6 | 0x15)
LONG(2 << 17 | 0x26 << 11 | 12 << 6 | 0x3a)
LONG(24 << 27 | 2 << 22 | (4 * 33) << 6 | 0x15)
LONG(. << 4 | 1) /* loopstop */
*(.code)
} >code
data : {
reg_save = .;
. = . + 256;
stack_top = .;
*(.data)
*(.rodata)
} >data
/* Overlap unexpected sections with code to generate an error */
unwanted 0x4000 : { *(*) }
}