assemblyx86-64nasmposition-independent-code

How to build .rodata string table when PIE is enabled?


I was using coding exercise site; I write and submit code that can solve provided problem, and then the system runs against predetermined test cases and judge whether the code is valid.

I'm currently practicing x86-64 NASM.

I wrote some code that includes read-only string table. But, the system emitted compile error related to PIE scheme.

When I place my string table to .data instead of .rodata, the system accepts my code.

Although my code is accepted, I want to know how to write proper read-only string table that can be placed to .rodata section.

This is part of string table which is placed on .rodata section:

section .rodata
align 8, db 0
table:
    dq .Leucine         ; UUA
    dq .Phenylalanine   ; UUC
    dq .Phenylalanine   ; UUU
    dq .Leucine         ; UUG
    dq 0                ; UGA
    dq .Cysteine        ; UGC
    dq .Cysteine        ; UGU
    dq .Tryptophan      ; UGG

.Leucine:
    db "Leucine", 0
.Phenylalanine:
    db "Phenylalanine", 0
.Cysteine:
    db "Cysteine", 0
.Tryptophan:
    db "Tryptophan", 0

I got following error from system when I submitted this code:

/usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: protein_translation.o: warning: relocation in read-only section `.rodata'
/usr/lib/gcc/x86_64-alpine-linux-musl/12.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
make: *** [Makefile:32: tests] Error 1

When I changed to .data instead of .rodata, the system accepted without any error.

As far as I know, if it were executable code, I could use RIP-relative address mode, like lea rsi, [rel .Leucine]. I want to know how to get similar effect regarding string table.


Solution

  • The string table must be writable at some point as the addresses you put in it are only known at runtime. Ideally, the table would be writable initially, then the runtime link editor patches in the correct addresses, and then it's set to read-only.

    While ELF does not provide this feature on its own, there's a GNU extension available on Linux for it: any section named .data.rel.ro or one of its subsections will be mapped read/write at program start, then the runtime linker patches in relocations, and then it's remapped as read-only. This allows you to get the desired effect, e.g. do:

    ; this data should be read-only at runtime, but must be writable while the
    ; dynamic linker fixed up relocations
    section .data.rel.ro progbits alloc noexec write align=8
    table:
        dq .Leucine         ; UUA
        dq .Phenylalanine   ; UUC
        dq .Phenylalanine   ; UUU
        dq .Leucine         ; UUG
        dq 0                ; UGA
        dq .Cysteine        ; UGC
        dq .Cysteine        ; UGU
        dq .Tryptophan      ; UGG
    
    ; this section is properly readonly
    section .rodata
    .Leucine:
        db "Leucine", 0
    .Phenylalanine:
        db "Phenylalanine", 0
    .Cysteine:
        db "Cysteine", 0
    .Tryptophan:
        db "Tryptophan", 0