Как делают я нахожу, запускают и заканчивают строки объединенных ячеек в Excel с VBScript?

3 проблемы:

  • GNU / Linux с использованием объектных файлов ELF не не украшает / не меняет имена C с начальным подчеркиванием. Используйте call printf, а не _printf (В отличие от MacOS X, который украшает символы символом _; имейте это в виду, если вы смотрите учебные пособия для других ОС. Windows также использует другое соглашение о вызовах, но только 32-разрядные Windows изменяют имена с помощью _ или других декораций, которые кодируют выбор соглашения о вызовах.)

  • Вы не сказали ld связать libc , и вы сами не определили printf, поэтому вы не дали компоновщику никаких входных файлов, содержащих определение для этого символа. printf является библиотечной функцией, определенной в libc.so, и в отличие от внешнего интерфейса GCC, ld не включает ее автоматически.

  • _start не является функцией, вы не можете ret из нее. RSP указывает на argc, а не на обратный адрес. Вместо этого определите main, если хотите, чтобы это была нормальная функция.

Связывайтесь с gcc -no-pie -nostartfiles hello.o -o hello, если вам нужен динамический исполняемый файл, который предоставляет собственный _start вместо main, но все еще использует libc.


Это безопасно для динамических исполняемых файлов в GNU / Linux, потому что glibc может запускать свои функции init через ловушки динамического компоновщика . Это не безопасно в Cygwin, где его libc инициализируется только вызовами из его стартового файла CRT (которые делают это перед вызовом main).

Используйте call exit для выхода, вместо того, чтобы делать системный вызов _exit напрямую, если вы используете printf ; это позволяет libc сбрасывать любые буферизованные выходные данные. (Если вы перенаправите вывод в файл, stdout будет иметь полную буферизацию по сравнению с буферизованной строкой на терминале.)

-static не будет безопасным; в статическом исполняемом файле динамический компоновщик не запускается до вашего _start, поэтому у libc нет возможности инициализировать себя, если вы не вызовете функции вручную. Это возможно, но обычно не рекомендуется.

Существуют другие реализации libc, которым не нужны никакие функции инициализации, вызываемые до printf / malloc / других функций. В glibc такие вещи, как буферы stdio, выделяются во время выполнения. (Это раньше имело место для MUSL libc , но это, очевидно, уже не так, согласно комментарию Флориана к этому ответу.)


Обычно, если вы хотите чтобы использовать функции libc, было бы неплохо определить функцию main вместо собственной точки входа _start. ​​ Тогда вы можете просто связаться с gcc как обычно, без специальных опций.

См. Какие части этого ассемблерного кода HelloWorld необходимы для написания программы на ассемблере? для этого и для версии, которая использует системные вызовы Linux напрямую, без libc. [одна тысяча сто восемьдесят два]


Если вы хотите, чтобы ваш код работал в исполняемом файле PIE, как gcc делает по умолчанию (без --no-pie) в недавних дистрибутивах, вам понадобится call printf wrt ..plt.

В любом случае, вы должны использовать lea rsi, [rel message], потому что REA-относительный LEA более эффективен, чем mov r64, imm64 с 64-битным абсолютным адресом. (В позиционно-зависимом коде лучшим вариантом для помещения статического адреса в 64-битный регистр является 5-байтовый mov esi, message, поскольку известно, что статические адреса в исполняемых файлах, отличных от PIE, находятся на низком 2 ГБ виртуального адресного пространства, и, таким образом, работают как 32-битные исполняемые файлы с расширенными знаками или нулями. Но REA-относительный LEA не намного хуже и работает везде.)

;;; Defining your own _start but using libc
;;; works on Linux for non-PIE executables

default rel                ; Use RIP-relative for [symbol] addressing modes
extern printf
extern exit                ; unlike _exit, exit flushes stdio buffers

section .text
    global _start
_start:
    ;; RSP is already aligned by 16 on entry at _start, unlike in functions

    lea    rdi, [format]        ; argument #1   or better  mov edi, format
    lea    rsi, [message]       ; argument #2
    xor    eax, eax             ; no FP args to the variadic function
    call   printf               ; for a PIE executable:  call printf wrt ..plt

    xor    edi, edi             ; arg #1 = 0
    call   exit                 ; exit(0)
    ; exit definitely does not return

section .rodata        ;; read-only data can go in .rodata instead of read-write .data

    message:    db "Hello, world!", 0
    format:   db "%s", 0xa, 0

Собираются нормально, связаны с gcc -no-pie -nostartfiles hello.o. Это исключает файлы запуска CRT, которые обычно определяют _start, который делает некоторые вещи перед вызовом main. Функции инициализации Libc вызываются из ловушек динамического компоновщика, поэтому printf можно использовать.

Это было бы не так с gcc -static -nostartfiles hello.o. Я включил примеры того, что происходит, если вы используете неправильные опции:

peter@volta:/tmp$ nasm -felf64 nopie-start.asm 
peter@volta:/tmp$ gcc -no-pie -nostartfiles nopie-start.o 
peter@volta:/tmp$ ./a.out 
Hello, world!
peter@volta:/tmp$ file a.out 
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0cd1cd111ba0c6926d5d69f9191bdf136e098e62, not stripped

# link error without -no-pie because it doesn't automatically make PLT stubs
peter@volta:/tmp$ gcc -nostartfiles nopie-start.o 
/usr/bin/ld: nopie-start.o: relocation R_X86_64_PC32 against symbol `printf@@GLIBC_2.2.5' can not be used when making a PIE object; recompile with -fPIC
/usr/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status


# runtime error with -static
peter@volta:/tmp$ gcc -static -no-pie -nostartfiles nopie-start.o -o static_start-hello
peter@volta:/tmp$ ./static_start-hello 
Segmentation fault (core dumped)

Альтернативная версия, определяя main вместо _start

(И упрощая с помощью puts ] вместо printf.)

default rel                ; Use RIP-relative for [symbol] addressing modes
extern puts

section .text
    global main
main:
    sub    rsp, 8    ;; RSP was 16-byte aligned *before* a call pushed a return address
                     ;; RSP is now 16-byte aligned, ready for another call

    mov    edi, message         ; argument #1, optimized to use non-PIE-only move imm32
    call   puts

    add    rsp, 8               ; restore the stack
    xor    eax, eax             ; return 0
    ret

section .rodata
    message:    db "Hello, world!", 0     ; puts appends a newline

puts в значительной степени точно реализует printf("%s\n", string); Компиляторы C сделают эту оптимизацию за вас, но в asm вы должны сделать это сами.

Связь с gcc -no-pie hello.o или даже статическая связь с использованием gcc -no-pie -static hello.o. Код запуска CRT будет вызывать функции инициализации glibc.

peter@volta:/tmp$ nasm -felf64 nopie-main.asm 
peter@volta:/tmp$ gcc -no-pie nopie-main.o 
peter@volta:/tmp$ ./a.out 
Hello, world!

# link error if you leave out -no-pie  because of the imm32 absolute address
peter@volta:/tmp$ gcc nopie-main.o 
/usr/bin/ld: nopie-main.o: relocation R_X86_64_32 against `.rodata' can not be used when making a PIE object; recompile with -fPIC
/usr/bin/ld: final link failed: nonrepresentable section on output
collect2: error: ld returned 1 exit status

main - это функция, поэтому перед повторным вызовом функции необходимо повторно выровнять стек. Фиктивный толчок также является допустимым способом выравнивания стека при вводе функции, но add / sub rsp, 8 более понятны.

Альтернативой является jmp puts, чтобы вызвать его, так что возвращаемое значение main будет тем, что возвращает puts. В этом случае вы не должны изменять сначала rsp: вы просто переходите к puts с вашим обратным адресом, все еще находящимся в стеке, точно так же, как если бы ваш вызывающий абонент вызвал puts.


PIE-совместимый код, определяющий main

(Вы можете сделать PIE, который определяет свой собственный _start. Это оставлено в качестве упражнения для читателя.)

[114 ]

peter@volta:/tmp$ nasm -felf64 pie.asm
peter@volta:/tmp$ gcc pie.o
peter@volta:/tmp$ ./a.out 
Hello, world!
peter@volta:/tmp$ file a.out
a.out: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=b27e6032f955d628a542f6391b50805c68541fb9, not stripped

5
задан e.James 24 March 2009 в 18:39
поделиться

3 ответа

Чтобы протестировать, если объединенные ячейки покрывают более затем одну строку:

cellRange = ExcelApplication.Cells(rowIndex, columnIndex)    
mergedColumns = cellRange.MergeArea.Rows.Count
if mergedColumns > 1 then ' merged
0
ответ дан 13 December 2019 в 05:43
поделиться

Спасибо за Ваши ответы. eJames я думаю, что Вы ответили на все, которое я хотел знать, как обнаружить объединенные ячейки и также FolderName / присвоение ItemName. Еще раз спасибо.

Я могу записать CSV без проблемы после более близкого контроля, я заметил, что также ячейки FOLDERn объединяют столбцы (A-C) так, вероятно, я могу также свериться с кодом господина Lucky:

mergedColumns = cellRange.MergeArea.Columns.Count

И если mergedColumns> 1 затем это - имя папки.

2
ответ дан 13 December 2019 в 05:43
поделиться

Можно использовать:

if (Cell.MergeCells) Then ...

определить, является ли ячейка частью объединенного диапазона, и

Cell.MergeArea.Cells(1,1).value

получить доступ к значению, содержавшемуся в том объединенном диапазоне.

Вот некоторый пример кода для запущения Вас. Я опустил детали фактической записи файла CSV:

Public Sub MakeCSV()
    Dim rowIndex As Integer
    Dim itemColumn As Range
    Dim dataColumn As Range
    Dim itemCell As Range
    Dim FolderName As String
    Dim ItemName As String
    Dim CSVLine As String

    Set itemColumn = Range("A2:A100")
    Set dataColumn = Range("B2:B100")

    For rowIndex = 1 To dataColumn.Rows.Count
        If dataColumn.Cells(rowIndex, 1).value = "" Then
            Rem // determine the current folder name
            FolderName = itemColumn.Cells(rowIndex, 1).value
        Else
            Rem // determine the item name (accounting for merged cells)
            Set itemCell = itemColumn.Cells(rowIndex, 1)
            If itemCell.MergeCells Then
                ItemName = itemCell.MergeArea.Cells(1, 1).value
            Else
                ItemName = itemCell.value
            End If

            Rem // write a line to the CSV file
            CSVLine = dataColumn.Cells(rowIndex, 1).value
            CSVLine = CSVLine & "," & ItemName
            CSVLine = CSVLine & "," & FolderName
        End If
    Next rowIndex
End Sub
10
ответ дан 13 December 2019 в 05:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: