动态链接 GAS 程序集文件时出现问题 (x64)
Issues when linking GAS assembly files dynamically (x64)
我正在尝试 link 2 个文件。我正在尝试使用 3 个符号(汇编程序)link。它与静态 linking 完美配合,但当尝试动态执行时 - 我收到错误。
/usr/bin/ld: warning: type and size of dynamic symbol `parse_intro' are not defined
/usr/bin/ld: warning: type and size of dynamic symbol `time_to_print' are not defined
/usr/bin/ld: warning: type and size of dynamic symbol `optimizing' are not defined
首先,我将一个文件制作到一个共享库中,我想从中导出 3 个符号。此文件的代码:
.data
premsg:
.ascii "0x" #to make the number have distinct x16 look when printed, this will be used as a prefix before a number
msg: #placeholder msg label, reserved memory will be used to store parsed number
.space 16 #16 bytes for 32 ascii numbers
nextline:
.ascii "\n" #switching to the next line after 16 ascii numbers
var1:
.quad 0x00000000000ef12b #number which will be parsed
.global parse_intro
.global optimizing
.global time_to_print
.text
parse_intro:
#binary mask for cutting smallest 4 bits (single number) from the 8 byte number
mov [=11=]b0000000000000000000000000000000000000000000000000000000000001111, %r9
mov [=11=]x1, %rbx #multiplier that will be used to shift from first 4 bits to proper position in the ascii number
mov [=11=]x0, %r12 #register that will hold first half of the reversed number
mov [=11=]x0, %r14 #register that will hold second half of the reversed number
jmp parse_start
parse_start: #main body of the parsing number to ASCII procedure
mov %r8, %rax #making a copy of a number before shifting it
shr , %r8 #shifting original number to 4 numbers right to go to the next number on the next step
and %r9, %rax #taking last 4 bits from a number
cmp [=11=]xa, %rax #checking if a number is smaller than 0xa
jl zero_to_9 #jumping to do +30 procedure if it's not x16 numeric symbol
add [=11=]x57, %rax #adding 57 because to change a number to ASCII number from a to f you need to add x16 57
mul %rbx
mul %rbx #we multiplying it twice because we go from 8 bytes to 16 bytes
add %rax, %r12 #adding summed with 30 and increased twice by rbx coefficient piece of the initial number to the previous pieces of a number to put it together in ASCII
shl , %rbx #shifting rbx 4 bits to the left to increase it by mul 10 without rax shenanigans
jmp check_for_overflow #going to overflow check after adding 8 bytes to %r12
zero_to_9: #function to change from bit number to ascii number
add [=11=]x30, %rax #adding 30 because to change a number to ASCII number from 0 to 9 you need to add x16 30
mul %rbx
mul %rbx #we multiplying it twice because we go from 8 bytes to 16 bytes
add %rax, %r12 #adding summed with 30 and increased twice by rbx coefficient piece of the initial number to the previous pieces of a number to put it together in ASCII
shl , %rbx #shifting rbx 4 bits to the left to increase it by mul 10 without rax shenanigans
jmp check_for_overflow #going to overflow check after adding 8 bytes to %r12
check_for_overflow: #function to store half of the value inside a second register, because it goes from 8 bytes to 16 bytes when parsed to ASCII
mov [=11=]x1000000000000000, %rax #value for overflow check
cmp %rax, %r12 #checking if r12 is overflowed
jle looping #doing a loop to parse_start with retq if loop ends in case if %r12 is not going to get overflow
cmp [=11=]x1, %rcx #checking if count is 1 and then
je finishing #Finishing to avoid moving %r12 to %r14 another time
mov %r12, %r14 #moving %r12 value to store in %r14
xor %r12, %r12 #resetting %r12
mov [=11=]x1, %rbx #resetting rbx to start from the first position
jmp looping #doing a loop to parse_start with retq if loop ends
looping:
loop parse_start
retq #exiting back to start if loops end, otherwise back to parse_start and decrementing %rcx
optimizing:
#binary mask for cutting lower 4 bytes out in optimization routine
mov [=11=]b1111111111111111111111111111111100000000000000000000000000000000, %rbx
mov , %rcx #the counter
movq %r8, %rax #moving the initial number value to accumulator register %rax
andq %rbx, %rax #leaving only first 32 not as 0 to check if biggest part of the number is full of 0 or not
cmp [=11=], %rax #checking if number is full of 0
jne finishing #first (from the left) 32 bits aren't full of 0 so we can't ignore them
subq , %rcx #second half of a number is full of 0, so we can only count from 8 instead of 16
retq #returning to _start
finishing:
retq #little function to jump return
time_to_print:
mov [=11=]b1111111100000000000000000000000000000000000000000000000000000000, %r9
cmp [=11=], %r14 #checking if we skipped 4 bytes because the original had zeroes in the biggest part of the number
je skipped_bytes #jumping to function that will swap r14 and r12, so that lower part is kept in r14 and r12 is full of zeroes (0x30 in ASCII)
mov , %rcx #making a counter which would stop the jump loop which would also work as a shift left value
call byte_fun #calling the reversing procedure
mov %r14, %r12 #moving second half of the number to r12
mov %rax, %r14 #storing the first half of the number from the accumulator having the value after completing byte_fun to %r14
mov , %rcx #we are putting 56 and not 64 because the last step will be made after the loop to avoid additional actions
call byte_fun #reversing the second half of a number
mov %rax, %r12 #moving stored second half of the number to the different register
lea msg(%rip), %rax #storing the address of the msg inside %rax
mov %r14, (%rax) #Putting on the first half of the reserved memory by msg, linked to %rax through previous instruction, the first half of the number needed to display formatted to ASCII saying mov value (rax) makes you move the value to the address stored in the register
add , %rax #adding 8 to address the second half of the reserved memory in msg
mov %r12, (%rax) #putting the second half of the reversed number to an address of the last 8 bytes resrved by msg
mov , %rax #putting 1 to %rax for printing syscall
mov , %rdi #puttin 1 to %rdi for printing syscall
lea msg(%rip), %rsi #taking a position independent link to the msg label with numbers related to ascii data and putting it to a printing register %rsi
mov , %rdx #setting length of 16 bits (2 per 1 number) and 1 bit for newline \n char
syscall #syscalling the print with the proper number
retq #returning to the _start body
byte_fun: #start of the reverse function to save the entry point
pop %rbp #putting entry point to the %rbp register to successfully return after a few jumping back and forth
jmp byte_reverse #jumping to the main body of the reverse function
byte_reverse: #function which separates the highest 2 bits from the reversed number and then shifts the reversed number by 8 bits (2 numbers) to the left and then switches 8 bits to their proper position and pushes them to a stack.
mov %r12, %rax #putting a copy of the currently shifted reversed number to an accumulator register %rax
shl , %r12 #shifting reversed number to the left for the next loop cycle
and %r9, %rax #applying binary mask which will only leave 8 bytes (or 2 numbers)
shr %cl, %rax #shifting current 8 bits of a reversed number right, prior to %rcx count, to reverse their position
push %rax #pushing a shifted piece of a number to a stack
sub , %rcx #substracting 8 from count to represent a shifting of the next 8 bits
jne byte_reverse #if sub from %rcx not resulted in zero - we are looping
push %r12 #pushing last 2 numbers of the reversed number, shifted to the left, as the final piece of the reversed value, which will be accessed first from stack to start the reverse
mov , %rcx #switching count register to 8 for a future loop inside byte_back
xor %rax, %rax #cleaning up the accumulator to 0
jmp byte_back #jumping to a code which put 8 elements inside stack all together and puts them back to stack as a single entity
byte_back: #function which sums elements in the stack to get the reversed version of the number
pop %rbx #poppint highest stack element to a register
add %rbx, %rax #adding the highest element to an accumulator
loop byte_back #decrementing the %rcx counter and starting at byte back again
push %rbp #we exited the loop and now putting the address of the print procedure to the top of the stack to get back to a procedure after calling the byte_fun
retq #returning to print procedure
skipped_bytes: #function to avoid issues with printing when only 4 bytes of the original number were evaluated in parsing
#I put this print of "0x" to avoid _start code because I want to use this program as a library
mov , %rax #putting 1 to %rax for print syscall
mov , %rdi #putting 1 to %rdi for print syscall
lea premsg(%rip), %rsi #putting a link to "0x" ascii value to %rsi to print it
mov , %rdx #setting 2 bytes to display 2 characters
syscall #syscall for printing "0x"
mov %r12, %r14 #moving the first half of the reversed number to a register that will be pushed to a stack first, so it would be last when we get it back
mov [=11=]x3030303030303030, %r12 #changing a second half or reversed number to ascii zeroes
jmp time_to_print #we are ready to start the printing procedure
我通过命令将这个程序制作成.so文件:
gcc printing.s -shared -o libprint.so
然后,我在主文件中使用此 .so 中的这 3 个过程(parse_intro、time_to_print 和优化),其中包含此代码
.data
linked_space: #space reserved for linked list nodes
.space 0x3000
list_head: #default list_head value
.quad 0x0
.quad 0x0
opening_bracket: #part of printing function construct
.ascii "["
straight_line: #part of printing function construct
.ascii "|"
closing_part: #part of printing function construct
.ascii "] -> "
last_part: #part of printing function construct, representing empty "first" node
.ascii "[empty|node]\n"
cut_error_text:
.ascii "Error: can't cut a core node\n"
.global _start
.text
add_head: #function which adds new element as head and makes a link to a previous head element
mov list_head(%rip), %rax #moving contents of a label (link to a head node) to a register
add , %rax #Moving the link to a point where a new node will start (1 node is 16 bytes)
mov %rbx, (%rax) #Putting a value that we want to hold in a new node inside the value address of a new head node
add , %rax #Moving address inside register by 8 to put a link to a previous head
mov list_head(%rip), %rdx #Moving old head address to an %rdx to put it then inside the new head
mov %rdx, (%rax) #Putting link of the old head inside a node of a new head
add , list_head(%rip) #Changing label which points to the head element to the new node we created
retq
cut_head: #Function which cuts the head by shifting the label 16 bytes back, with exception check to avoid touching core node
mov list_head(%rip), %rax #Putting link of the current head element which we will cutaway
add , %rax #Getting a link to a previous element to check if it's 0x0, which means it's a core node
cmp [=13=], (%rax) #Comparing it to 0
je cut_error #Jumping to error version of the cut if it's equal
sub , %rax #Moving to the beginning of the previous element, 16 bytes + 8 after previous add 8
mov %rax, list_head(%rip) #Moving new link to a head label
retq
cut_error: #Function which prints error text and avoid cutting the core element
mov , %rax #Printing error text
mov , %rdi
lea cut_error_text(%rip), %rsi
mov , %rdx
syscall
retq
pre_print: #Intro for print to put first head element, to avoid issues with loop shenanigans
push list_head(%rip)
jmp print_node #Moving to main print function
print_node: #Function which takes functions from printing code and prints linked list visually
pop %rbx #Putting stored link to node value into %rbx
push %rbx #Pushing back to a stack a link to avoid mutations of a link after working with %rbx
add , %rbx #Moving link to the address part of the node with the address to a previous list
mov (%rbx), %rax #Putting actual address to the previous node to check if it's 0x0, which means, it's a first empty node
cmp [=13=], %rax #Comparing link inside %rax to 0
je return_printing #If it's zero - moving to the final part of the print, where the first empty node is printed with \n char
mov , %rax #Printing opening bracket
mov , %rdi
lea opening_bracket(%rip), %rsi
mov , %rdx
syscall
pop %rbx #Getting link to the value field of the current node
mov (%rbx), %r8 #Moving it to %r8, which is a register that will contain a numeric value for future printing
push %rbx #Saving link stored inside %rbx because %rbx will be used in printing functions
call optimizing #Printing routine consists of 3 functions which need to be called from printing part of the program
call parse_intro
call time_to_print
mov , %rax #Printing straight line to separate value from link
mov , %rdi
lea straight_line(%rip), %rsi
mov , %rdx
syscall
pop %rbx #Putting link back from the stack again in rbx
push %rbx #Storing the link before mutating it again
add , %rbx #Changing link to the address which has a link to the previous element
mov (%rbx), %r8 #Moving link to %r8 to print it
call optimizing #Launching a print routine
call parse_intro
call time_to_print
mov , %rax #Printing closing part of the node "construct"
mov , %rdi
lea closing_part(%rip), %rsi
mov , %rdx
syscall
pop %rbx #Popping link of the printed node to shift it 16 bytes back to move to the previous node
sub , %rbx #Moving to the previous node
push %rbx #Putting a link to the previous node inside stack to use it later
jmp print_node #Looping back to print_node
return_printing: #Finalizing function which will print the first empty node and \n char
mov , %rax
mov , %rdi
lea last_part(%rip), %rsi
mov , %rdx
syscall
pop %rbx #Since we didn't pop the stored value in loop body, we need to get rid of it from stack to jump back to _start
retq
list_initialization:
lea linked_space(%rip), %rax #initializing the first node, it already has 0x0 as value and it's needed to put on the space for the linked list
mov %rax, list_head(%rip) #Putting link to the list_head, which currently contains the first node value/link 0x0, on the linked_space
retq
_start:
call list_initialization
#call pre_print #Function which prints the linked list
#mov , %rbx #%rbx will hold value which will be put inside new linked list node
#call add_head #Function which adds new node as head element
#call pre_print
#mov , %rbx
#call add_head
#call pre_print
#mov , %rbx
#call add_head
#call pre_print
#call cut_head #Function which cuts head element and moves link to a previous element
#call pre_print
#call cut_head
#call pre_print
#call cut_head
#call pre_print
#call cut_head
#call pre_print
mov , %rax #Exiting from a program
xor %rdi, %rdi
syscall
并编译成二进制文件,应该可以运行,通过命令:
gcc -L /home/*path_to_folder_with_so_file* -g -nostdlib -o output linkedlist.s -lprint
我也尝试编译同一个文件,但注释掉了程序试图从动态共享库访问文件的 6 行。这是此文件的“readelf”内容。
并读取 .so 文件:
我可能遗漏了什么,为什么原始文件找不到从 .so 导入的符号?
由于我无法将评论作为解决方案,因此,我将自己提供答案,该答案由fuz提供。
这里要做的重要事情是注意错误消息(我知道,多么明显)。 GAS 程序集中的每个动态链接符号,即使没有 C 库,也需要在 .so 文件中分配类型和大小,因为 GCC 无法自行获取有关导出符号的信息,程序员需要明确地给它.例如,对于函数 foo
foo:
...
ret
你需要通过输入
来给函数一个类型
.type foo, @function
在代码的某处,我在
之后的下一行做了
.global foo
行,我将相同的函数设置为全局函数,允许将其导出。
并且,要为动态符号指定大小,您需要将
.size foo, .-foo
在最后一条指令之后(例如,在“jmp”或“ret”之后)。 “点”是当前地址,foo 是“foo”函数中第一条指令的地址。因此,通过从当前地址中减去“foo”地址,您将获得“foo”的大小。
我正在尝试 link 2 个文件。我正在尝试使用 3 个符号(汇编程序)link。它与静态 linking 完美配合,但当尝试动态执行时 - 我收到错误。
/usr/bin/ld: warning: type and size of dynamic symbol `parse_intro' are not defined
/usr/bin/ld: warning: type and size of dynamic symbol `time_to_print' are not defined
/usr/bin/ld: warning: type and size of dynamic symbol `optimizing' are not defined
首先,我将一个文件制作到一个共享库中,我想从中导出 3 个符号。此文件的代码:
.data
premsg:
.ascii "0x" #to make the number have distinct x16 look when printed, this will be used as a prefix before a number
msg: #placeholder msg label, reserved memory will be used to store parsed number
.space 16 #16 bytes for 32 ascii numbers
nextline:
.ascii "\n" #switching to the next line after 16 ascii numbers
var1:
.quad 0x00000000000ef12b #number which will be parsed
.global parse_intro
.global optimizing
.global time_to_print
.text
parse_intro:
#binary mask for cutting smallest 4 bits (single number) from the 8 byte number
mov [=11=]b0000000000000000000000000000000000000000000000000000000000001111, %r9
mov [=11=]x1, %rbx #multiplier that will be used to shift from first 4 bits to proper position in the ascii number
mov [=11=]x0, %r12 #register that will hold first half of the reversed number
mov [=11=]x0, %r14 #register that will hold second half of the reversed number
jmp parse_start
parse_start: #main body of the parsing number to ASCII procedure
mov %r8, %rax #making a copy of a number before shifting it
shr , %r8 #shifting original number to 4 numbers right to go to the next number on the next step
and %r9, %rax #taking last 4 bits from a number
cmp [=11=]xa, %rax #checking if a number is smaller than 0xa
jl zero_to_9 #jumping to do +30 procedure if it's not x16 numeric symbol
add [=11=]x57, %rax #adding 57 because to change a number to ASCII number from a to f you need to add x16 57
mul %rbx
mul %rbx #we multiplying it twice because we go from 8 bytes to 16 bytes
add %rax, %r12 #adding summed with 30 and increased twice by rbx coefficient piece of the initial number to the previous pieces of a number to put it together in ASCII
shl , %rbx #shifting rbx 4 bits to the left to increase it by mul 10 without rax shenanigans
jmp check_for_overflow #going to overflow check after adding 8 bytes to %r12
zero_to_9: #function to change from bit number to ascii number
add [=11=]x30, %rax #adding 30 because to change a number to ASCII number from 0 to 9 you need to add x16 30
mul %rbx
mul %rbx #we multiplying it twice because we go from 8 bytes to 16 bytes
add %rax, %r12 #adding summed with 30 and increased twice by rbx coefficient piece of the initial number to the previous pieces of a number to put it together in ASCII
shl , %rbx #shifting rbx 4 bits to the left to increase it by mul 10 without rax shenanigans
jmp check_for_overflow #going to overflow check after adding 8 bytes to %r12
check_for_overflow: #function to store half of the value inside a second register, because it goes from 8 bytes to 16 bytes when parsed to ASCII
mov [=11=]x1000000000000000, %rax #value for overflow check
cmp %rax, %r12 #checking if r12 is overflowed
jle looping #doing a loop to parse_start with retq if loop ends in case if %r12 is not going to get overflow
cmp [=11=]x1, %rcx #checking if count is 1 and then
je finishing #Finishing to avoid moving %r12 to %r14 another time
mov %r12, %r14 #moving %r12 value to store in %r14
xor %r12, %r12 #resetting %r12
mov [=11=]x1, %rbx #resetting rbx to start from the first position
jmp looping #doing a loop to parse_start with retq if loop ends
looping:
loop parse_start
retq #exiting back to start if loops end, otherwise back to parse_start and decrementing %rcx
optimizing:
#binary mask for cutting lower 4 bytes out in optimization routine
mov [=11=]b1111111111111111111111111111111100000000000000000000000000000000, %rbx
mov , %rcx #the counter
movq %r8, %rax #moving the initial number value to accumulator register %rax
andq %rbx, %rax #leaving only first 32 not as 0 to check if biggest part of the number is full of 0 or not
cmp [=11=], %rax #checking if number is full of 0
jne finishing #first (from the left) 32 bits aren't full of 0 so we can't ignore them
subq , %rcx #second half of a number is full of 0, so we can only count from 8 instead of 16
retq #returning to _start
finishing:
retq #little function to jump return
time_to_print:
mov [=11=]b1111111100000000000000000000000000000000000000000000000000000000, %r9
cmp [=11=], %r14 #checking if we skipped 4 bytes because the original had zeroes in the biggest part of the number
je skipped_bytes #jumping to function that will swap r14 and r12, so that lower part is kept in r14 and r12 is full of zeroes (0x30 in ASCII)
mov , %rcx #making a counter which would stop the jump loop which would also work as a shift left value
call byte_fun #calling the reversing procedure
mov %r14, %r12 #moving second half of the number to r12
mov %rax, %r14 #storing the first half of the number from the accumulator having the value after completing byte_fun to %r14
mov , %rcx #we are putting 56 and not 64 because the last step will be made after the loop to avoid additional actions
call byte_fun #reversing the second half of a number
mov %rax, %r12 #moving stored second half of the number to the different register
lea msg(%rip), %rax #storing the address of the msg inside %rax
mov %r14, (%rax) #Putting on the first half of the reserved memory by msg, linked to %rax through previous instruction, the first half of the number needed to display formatted to ASCII saying mov value (rax) makes you move the value to the address stored in the register
add , %rax #adding 8 to address the second half of the reserved memory in msg
mov %r12, (%rax) #putting the second half of the reversed number to an address of the last 8 bytes resrved by msg
mov , %rax #putting 1 to %rax for printing syscall
mov , %rdi #puttin 1 to %rdi for printing syscall
lea msg(%rip), %rsi #taking a position independent link to the msg label with numbers related to ascii data and putting it to a printing register %rsi
mov , %rdx #setting length of 16 bits (2 per 1 number) and 1 bit for newline \n char
syscall #syscalling the print with the proper number
retq #returning to the _start body
byte_fun: #start of the reverse function to save the entry point
pop %rbp #putting entry point to the %rbp register to successfully return after a few jumping back and forth
jmp byte_reverse #jumping to the main body of the reverse function
byte_reverse: #function which separates the highest 2 bits from the reversed number and then shifts the reversed number by 8 bits (2 numbers) to the left and then switches 8 bits to their proper position and pushes them to a stack.
mov %r12, %rax #putting a copy of the currently shifted reversed number to an accumulator register %rax
shl , %r12 #shifting reversed number to the left for the next loop cycle
and %r9, %rax #applying binary mask which will only leave 8 bytes (or 2 numbers)
shr %cl, %rax #shifting current 8 bits of a reversed number right, prior to %rcx count, to reverse their position
push %rax #pushing a shifted piece of a number to a stack
sub , %rcx #substracting 8 from count to represent a shifting of the next 8 bits
jne byte_reverse #if sub from %rcx not resulted in zero - we are looping
push %r12 #pushing last 2 numbers of the reversed number, shifted to the left, as the final piece of the reversed value, which will be accessed first from stack to start the reverse
mov , %rcx #switching count register to 8 for a future loop inside byte_back
xor %rax, %rax #cleaning up the accumulator to 0
jmp byte_back #jumping to a code which put 8 elements inside stack all together and puts them back to stack as a single entity
byte_back: #function which sums elements in the stack to get the reversed version of the number
pop %rbx #poppint highest stack element to a register
add %rbx, %rax #adding the highest element to an accumulator
loop byte_back #decrementing the %rcx counter and starting at byte back again
push %rbp #we exited the loop and now putting the address of the print procedure to the top of the stack to get back to a procedure after calling the byte_fun
retq #returning to print procedure
skipped_bytes: #function to avoid issues with printing when only 4 bytes of the original number were evaluated in parsing
#I put this print of "0x" to avoid _start code because I want to use this program as a library
mov , %rax #putting 1 to %rax for print syscall
mov , %rdi #putting 1 to %rdi for print syscall
lea premsg(%rip), %rsi #putting a link to "0x" ascii value to %rsi to print it
mov , %rdx #setting 2 bytes to display 2 characters
syscall #syscall for printing "0x"
mov %r12, %r14 #moving the first half of the reversed number to a register that will be pushed to a stack first, so it would be last when we get it back
mov [=11=]x3030303030303030, %r12 #changing a second half or reversed number to ascii zeroes
jmp time_to_print #we are ready to start the printing procedure
我通过命令将这个程序制作成.so文件:
gcc printing.s -shared -o libprint.so
然后,我在主文件中使用此 .so 中的这 3 个过程(parse_intro、time_to_print 和优化),其中包含此代码
.data
linked_space: #space reserved for linked list nodes
.space 0x3000
list_head: #default list_head value
.quad 0x0
.quad 0x0
opening_bracket: #part of printing function construct
.ascii "["
straight_line: #part of printing function construct
.ascii "|"
closing_part: #part of printing function construct
.ascii "] -> "
last_part: #part of printing function construct, representing empty "first" node
.ascii "[empty|node]\n"
cut_error_text:
.ascii "Error: can't cut a core node\n"
.global _start
.text
add_head: #function which adds new element as head and makes a link to a previous head element
mov list_head(%rip), %rax #moving contents of a label (link to a head node) to a register
add , %rax #Moving the link to a point where a new node will start (1 node is 16 bytes)
mov %rbx, (%rax) #Putting a value that we want to hold in a new node inside the value address of a new head node
add , %rax #Moving address inside register by 8 to put a link to a previous head
mov list_head(%rip), %rdx #Moving old head address to an %rdx to put it then inside the new head
mov %rdx, (%rax) #Putting link of the old head inside a node of a new head
add , list_head(%rip) #Changing label which points to the head element to the new node we created
retq
cut_head: #Function which cuts the head by shifting the label 16 bytes back, with exception check to avoid touching core node
mov list_head(%rip), %rax #Putting link of the current head element which we will cutaway
add , %rax #Getting a link to a previous element to check if it's 0x0, which means it's a core node
cmp [=13=], (%rax) #Comparing it to 0
je cut_error #Jumping to error version of the cut if it's equal
sub , %rax #Moving to the beginning of the previous element, 16 bytes + 8 after previous add 8
mov %rax, list_head(%rip) #Moving new link to a head label
retq
cut_error: #Function which prints error text and avoid cutting the core element
mov , %rax #Printing error text
mov , %rdi
lea cut_error_text(%rip), %rsi
mov , %rdx
syscall
retq
pre_print: #Intro for print to put first head element, to avoid issues with loop shenanigans
push list_head(%rip)
jmp print_node #Moving to main print function
print_node: #Function which takes functions from printing code and prints linked list visually
pop %rbx #Putting stored link to node value into %rbx
push %rbx #Pushing back to a stack a link to avoid mutations of a link after working with %rbx
add , %rbx #Moving link to the address part of the node with the address to a previous list
mov (%rbx), %rax #Putting actual address to the previous node to check if it's 0x0, which means, it's a first empty node
cmp [=13=], %rax #Comparing link inside %rax to 0
je return_printing #If it's zero - moving to the final part of the print, where the first empty node is printed with \n char
mov , %rax #Printing opening bracket
mov , %rdi
lea opening_bracket(%rip), %rsi
mov , %rdx
syscall
pop %rbx #Getting link to the value field of the current node
mov (%rbx), %r8 #Moving it to %r8, which is a register that will contain a numeric value for future printing
push %rbx #Saving link stored inside %rbx because %rbx will be used in printing functions
call optimizing #Printing routine consists of 3 functions which need to be called from printing part of the program
call parse_intro
call time_to_print
mov , %rax #Printing straight line to separate value from link
mov , %rdi
lea straight_line(%rip), %rsi
mov , %rdx
syscall
pop %rbx #Putting link back from the stack again in rbx
push %rbx #Storing the link before mutating it again
add , %rbx #Changing link to the address which has a link to the previous element
mov (%rbx), %r8 #Moving link to %r8 to print it
call optimizing #Launching a print routine
call parse_intro
call time_to_print
mov , %rax #Printing closing part of the node "construct"
mov , %rdi
lea closing_part(%rip), %rsi
mov , %rdx
syscall
pop %rbx #Popping link of the printed node to shift it 16 bytes back to move to the previous node
sub , %rbx #Moving to the previous node
push %rbx #Putting a link to the previous node inside stack to use it later
jmp print_node #Looping back to print_node
return_printing: #Finalizing function which will print the first empty node and \n char
mov , %rax
mov , %rdi
lea last_part(%rip), %rsi
mov , %rdx
syscall
pop %rbx #Since we didn't pop the stored value in loop body, we need to get rid of it from stack to jump back to _start
retq
list_initialization:
lea linked_space(%rip), %rax #initializing the first node, it already has 0x0 as value and it's needed to put on the space for the linked list
mov %rax, list_head(%rip) #Putting link to the list_head, which currently contains the first node value/link 0x0, on the linked_space
retq
_start:
call list_initialization
#call pre_print #Function which prints the linked list
#mov , %rbx #%rbx will hold value which will be put inside new linked list node
#call add_head #Function which adds new node as head element
#call pre_print
#mov , %rbx
#call add_head
#call pre_print
#mov , %rbx
#call add_head
#call pre_print
#call cut_head #Function which cuts head element and moves link to a previous element
#call pre_print
#call cut_head
#call pre_print
#call cut_head
#call pre_print
#call cut_head
#call pre_print
mov , %rax #Exiting from a program
xor %rdi, %rdi
syscall
并编译成二进制文件,应该可以运行,通过命令:
gcc -L /home/*path_to_folder_with_so_file* -g -nostdlib -o output linkedlist.s -lprint
我也尝试编译同一个文件,但注释掉了程序试图从动态共享库访问文件的 6 行。这是此文件的“readelf”内容。
并读取 .so 文件:
我可能遗漏了什么,为什么原始文件找不到从 .so 导入的符号?
由于我无法将评论作为解决方案,因此,我将自己提供答案,该答案由fuz提供。
这里要做的重要事情是注意错误消息(我知道,多么明显)。 GAS 程序集中的每个动态链接符号,即使没有 C 库,也需要在 .so 文件中分配类型和大小,因为 GCC 无法自行获取有关导出符号的信息,程序员需要明确地给它.例如,对于函数 foo
foo:
...
ret
你需要通过输入
来给函数一个类型.type foo, @function
在代码的某处,我在
之后的下一行做了.global foo
行,我将相同的函数设置为全局函数,允许将其导出。
并且,要为动态符号指定大小,您需要将
.size foo, .-foo
在最后一条指令之后(例如,在“jmp”或“ret”之后)。 “点”是当前地址,foo 是“foo”函数中第一条指令的地址。因此,通过从当前地址中减去“foo”地址,您将获得“foo”的大小。